In this tutorial, we’ll deploy a containerized Django application with Kubernetes (K8s).
Step 1 — Containerize our Application with Docker
Let’s create a new Django application:
$django-admin startproject django-k8s
Navigate to the django-k8s directory:
After that, we need to create a new Dockerfile which Docker will use to build our container image:
FROM ubuntu:20.04 RUN apt-get update && apt-get install -y tzdata && apt install -y python3.8 python3-pip RUN apt install python3-dev libpq-dev nginx -y RUN pip install django gunicorn psycopg2 ADD . /app WORKDIR /app EXPOSE 8000 CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "django-k8s.wsgi"]
This Dockerfile uses the
official Ubuntu 20.04 docker image as a base and installs
Python3.8. Finally, it exposes that port
8000 will be used to accept incoming container connections, and runs
gunicorn with 3 workers and listening on port 8000.
Now, let’s build our image using docker build:
$docker build -t django-k8s.
We named the image
django-k8s using the
-t flag and pass in the current directory as a build context, the set of files to reference when containerize the image.
After Docker builds and tags the image, list available images using docker images:
You should see the django-k8s image listed:
REPOSITORY IMAGE ID CREATED SIZE
In the next step, we will run the configured container locally.
Step 2 — Creating the Database Schema
With the container built and configured, use
docker run to override the CMD set in the Dockerfile and create the database schema using the
manage.py makemigrations and
manage.py migrate commands
$docker run -i -t django-k8s sh
This will provide you with a shell prompt inside of the running container
#python3 manage.py makemigrations && python3 manage.py migrate
If you’re running this you should see:
Output Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying auth.0012_alter_user_first_name_max_length... OK Applying sessions.0001_initial... OK
This shows that the database schema has successfully been created.
#python3 manage.py createsuperuser
Enter a username, email address, and password for your superuser, and after creating the superuser, hit CTRL+D to quit the container and kill it.
Now let’s run our docker container .
$docker run -p 80:8000 django-k8s
you should see :
Output [2022-04-18 06:40:37 +0000]  [INFO] Starting gunicorn 20.1.0 [2022-04-18 06:40:37 +0000]  [INFO] Listening at: http://0.0.0.0:8000 (1) [2022-04-18 06:40:37 +0000]  [INFO] Using worker: sync [2022-04-18 06:40:37 +0000]  [INFO] Booting worker with pid: 9 [2022-04-18 06:40:37 +0000]  [INFO] Booting worker with pid: 10 [2022-04-18 06:40:37 +0000]  [INFO] Booting worker with pid: 11
Here, we run the default command defined in the Dockerfile,
gunicorn --bind :8000 --workers 3 django-k8s.wsgi:application, and expose container port
8000 so that port
80 on your local machine gets mapped to port
8000 of the
You should now be able to navigate to the
django-k8s app using your web browser by typing
http://localhost in the URL bar.
http://localhost to see the djangoapp:
When you are finished exploring, hit CTRL+C in the terminal window running the Docker container to kill the container.
Step 3 — Pushing the Django App Image to Docker Hub
To deploy your application to Kubernetes, your app image must be uploaded to a docker hub registry. Kubernetes will retrieve the application image from its repository and then deploy it to your cluster.
In this tutorial, we’ll push a Django image to the public Docker Hub repository.
Begin by logging in to Docker Hub on your local machine:
Enter your Docker Hub username and password to login. After you successfully logged in you should see:
Output Login Succeeded
The Django image has the django-k8s:latest tag. To push it to your Docker Hub repository, re-tag the image with your Docker Hub username and repo name:
$docker tag django-k8s:latest your_dockerhub_username/your_dockerhub_repo_name:latest
Push the image to the repo:
You’ll see some output that updates as image layers are pushed to Docker Hub.
Now that your image is available to Kubernetes on Docker Hub, you can begin deploy it in your cluster.
Step 4 — Deploy the Django App using a Deployment
In this step you’ll create a Deployment for your Django app. A Kubernetes Deployment is a controller that can be used to manage stateless applications in your cluster. A controller is a control loop that regulates workloads by scaling them up or down. Controllers also restart and clear out failed containers.
Deployments control one or more Pods, the smallest deployable unit in a Kubernetes cluster. Pods enclose one or more containers.
Begin by opening a file called
django-deployment.yaml in your favorite editor:
Paste in the following Deployment manifest:
apiVersion: apps/v1 kind: Deployment metadata: name: django-app labels: app: django spec: replicas: 3 selector: matchLabels: app: django template: metadata: labels: app: django spec: containers: - image: your_dockerhub_username/app_repo_name:latest name: django ports: - containerPort: 8000 name: gunicorn
Fill in the appropriate container image name, referencing the Django project image you pushed to Docker Hub in Step 2.
Here we define a Kubernetes Deployment called django-app and label it with the key-value pair
app: django. We specify that we’d like to run
three replicas of the Pod defined below the
Finally, we expose
containerPort 8000 and name it
When you’re done editing the file, save and close it.
Create the Deployment in your cluster using
kubectl apply -f:
$kubectl apply -f django-deployment.yaml
you should see:
Check that the Deployment rolled out correctly using kubectl get:
$kubectl get deploy django-app
Output: NAME READY UP-TO-DATE AVAILABLE AGE django-app 3/3 3 3 3m21s
If you encounter an error or something isn’t quite working, you can use kubectl describe to inspect the failed Deployment:
$kubectl describe deplo
You can inspect the three Pods using kubectl get pod:
$kubectl get pod
Output: NAME READY STATUS RESTARTS AGE django-app-7c55868755-4wglz 1/1 Running 0 2m5s django-app-7c55868755-7tpjd 1/1 Running 0 2m5s django-app-7c55868755-9s4s8 1/1 Running 0 2m5s
Three replicas of your Django app are now up and running in the cluster. To access the app, you need to create a Kubernetes Service, which we’ll do next.
Step 5 — Allowing External Access using a Service
In this step, you’ll create a Service for your Django app. A Kubernetes Service is an abstraction that allows you to expose a set of running Pods as a network service. Using a Service you can create a stable endpoint for your app that does not change as Pods die and are recreated.
Creating a file called
django-svc.yaml using your favorite editor:
Paste in the following Service manifest:
apiVersion: v1 kind: Service metadata: name: django labels: app: django spec: type: NodePort selector: app: django ports: - port: 8000 targetPort: 8000
Here we create a NodePort Service called
django and give it the
app: django label. We then select backend Pods with the app: django label and target their
When you’re done editing the file, save and close it.
Roll out the Service using kubectl apply:
$kubectl apply -f django-svc.yaml
Output: service/django created
Confirm that your Service was created using kubectl get svc:
$kubectl get svc django
Output: NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE django NodePort 10.107.211.249 <none> 8000:30306/TCP 15s
This output shows the Service’s cluster-internal IP and NodePort (30306). To connect to the service, we need the external IP addresses for our cluster nodes:
In your web browser, visit your Django app using
You should see the same Django app interface that you accessed locally in Step 1.
In this tutorial, you deployed a scalable Django app into a Kubernetes cluster. Running Pods can be quickly scaled up or down using the replicas field in the django-app Deployment manifest.
git source code here.