Django on K8s (Part V: Running with MySQL)
Mohamed M El-Kalioby
Posted on January 12, 2022
Welcome back, in the last part, we discussed how to persist the storage between container restarts (assuming we have only one node), but we can't use SQLite on any environment, and we need a robust DBMS like MySQL to run our web-app. so In this part we will see how to use multiple containers per pod strategy to run Django with MySQL.
Warning: There is a better technique for production, where we have a MySQL service (which has replicas across several pods) and the master node is exposed to the other pods and Django apps can find it using ConfigMaps, but this is out-of-scope for this tutorial.
MySQL Container
The MySQL image on docker hub needs the databases folders '/var/lib/mysql' to be an external volume which makes sense as we need to persist the databases if the container crashes. also the MySQL image needs an env variable MYSQL_ROOT_PASSWORD
to set the root password.
To configure the image, lets do the following steps
- Create a storage folder on minikube
- Start a MySQL container with the
MYSQL_ROOT_PASSWORD
environmental variable on the minikube docker - Create a database called 'django-example' which will the data for the application
- Create an image of the created container,
- Move the settings file to MySQL instead of SQLite.
- Update the requirements file
- Create new docker image of the new code.
- Adjust the deployment.yaml to
- Add MySQL container based on our image to the containers,
- Add a command to the web container to migrate the database as the container starts
1. Create a storage folder on minikube
As minikube is our hosting we need to create a storage folder.
minikube ssh -- mkdir -p /storage/mysql/django-example
2. Start a MySQL
Move your environment to use Docker inside minikube
$ eval $(minikube docker-env)
Start the container (with an external storage)
docker run -d -e MYSQL_ROOT_PASSWORD=pass1234 --name example-mysql -v /storage/mysql/django-example:/var/lib/mysql mysql
The -v
mount a folder from the host to the container.
After downloading all the image layers, you will get the container ID like the image below, next start a terminal and connect to mysql using the root and the password we set, as shown in the image below.
3. Create a database called 'django-example' which will the data for the application
Connect to MySQL & Create Database
$ docker exec -it example-mysql /bin/bash
mysql -u root -ppass1234
mysql> create database django_example;
Exit twice by (pressing CTRL+D) to get back to your terminal.
4. Create an image of the created container
Create a new database image using docker commit, and check the images list as shown in the image below
$ docker commit example-mysql django-example-db:v1.0
$ docker images
Verification Step
Start a new container from our image and let's check that the database is there.
docker run -d --name test-db -v /storage/mysql/django-example/:/var/lib/mysql django-example-db:v1.0
docker exec -it test-db /bin/bash
mysql -u root -ppass1234
on the mysql terminal, write
show databases
you shall find the database there as shown in the image below
Important: Clean what you done so we can continue, if you won't do so MySQL in the other containers won't start.
$ docker stop test-db
test-db
$ docker rm test-db
test-db
5. Move the settings file to MySQL instead of SQLite.
Note: All the steps descibed below are available in the MySQL
branch in the repo
Note:In Kubernetes, when multiple containers are in the same pod, they share the same IP and they communicate using the loopback interface (localhost) so we gonna to configure Django to connect to MySQL as follows:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django_example',
'USER': 'root',
'PASSWORD': 'pass1234',
'HOST': '127.0.0.1'
}
}
6. Update the 'requirements.txt' file to include 'mysqlclient' library
mysqlclient==2.1.0
7. Update Dockerfile to run the migrate command first.
FROM python:3.8-slim-bullseye
RUN apt update && apt install -y gcc libmariadb-dev-compat #<---New
RUN pip install gunicorn
WORKDIR /app
COPY django_app/requirements.txt .
RUN pip install -r /app/requirements.txt
COPY django_app/ /app/
CMD sleep 5 #<---New
CMD python manage.py migrate #<---New
EXPOSE 80/tcp
ENTRYPOINT gunicorn -w 4 k8s.wsgi -b 0.0.0.0:80
Run the docker build command
docker build -t django-example-mysql:v1.0 .
- Adjust the deployment.yaml run the sql container with web container, as shown in
django-example_mysql.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: django-example-mysql
labels:
app: django-example-mysql
spec:
replicas: 1
selector:
matchLabels:
app: django-example-mysql
template:
metadata:
labels:
app: django-example-mysql
spec:
volumes: # <-- Add MySQL Storage
- name: mysql-volume
hostPath:
path: /storage/mysql/django-example/
type: Directory
containers:
- name: web-app
image: django-example-mysql:v1.0 #<--Use New Image
command: ['/bin/bash'] #<-- Add Command to run at start-up
args: ['-c','python manage.py migrate; gunicorn -w 4 k8s.wsgi -b 0.0.0.0:80']
ports:
- containerPort: 80
- name: mysql #<-- Add Second Container
image: django-example-db:v1.0
volumeMounts:
- name: mysql-volume
mountPath: /var/lib/mysql/
Finally, Apply the configuration to Kubernetes
django-on-k8s$ kubectl apply -f django-example_mysql.yaml
Expose the service
$ kubectl expose deployment django-example-mysql
Start the minikube tunnel (in a new terminal window)
$ minikube tunnel
Get the IP
$ kubectl get svc
Now open a new browser window, and go to 'http://IP/' and try to login by 'admin','admin', you get 'Invalid Username or Password' as shown below.
I think, it is clear why the username and password are invalid, because the database doesn't have any users and it is just migrated by web container, so next we connect to the pod and run createsuperuser
command to create the first user that is done as follows
Get the pod name through
$ kubectl get po -l app=django-example-mysql
Note: This filters the pods by the app name
Then connect to the pod by
$ kubectl exec -it django-example-mysql-58459c6f4-q8zg8 -c web-app -- /bin/bash
The image below shows the steps
Now try to login again to the application.
You shall be able to login successfully, as shown below.
Verification
So, we logged-in successfully, but we need to make sure that the database will hold against containers failure so we can are going to delete the deployment and create it again and try to refresh and we shall be still login and we can logout and login again
To simulate pod failure, we will delete the pod automatically, which will force Kubernetes to start a new pod to match the deployment's requirements as shown in the image below
$ kubectl delete po/django-example-mysql-58459c6f4-q8zg8
Go to the browser window which is showing the application and refresh, you will find everything working and you logout and login again.
Wrap-up
In this part, we discussed how to start a MySQL container with our web-app container and make them talk together and now we have most of things to go production. Next part will show some advanced topics like rediness & liveness checks, rolling updates and rolling back and secrets, so stay tuned.
Posted on January 12, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.