Deploying Django with Kubernetes

Introduction Link to heading

Kubernetes is a great platform to run your applications on. There are plenty of blogs out there that go into why Kubernetes is great, so go read some if you haven’t already. One of the many great fits is a Django application. Here, we’ll run through the very basics.

We’ll deploy this demonstration to Google Cloud Platform as this provides a good foundation to spin up a Kubernetes cluster and required ancillaries. All of the kubectl commands are ambiguous to where you’ve installed Kubernetes.

Prerequisites Link to heading

First, pull the demo repository. This contains some manifest templates, a couple of Dockerfiles and a simple Django guestbook, written by Bill Prin, who was influential in me this post.

git clone https://github.com/joedborg/django-kubernetes-demo.git

You’ll need to have kubectl and gcloud installed. You’ll also need a GKE account if you want to follow this guide step-by-step; you can do this here.

Linux Link to heading

snap install kubectl --classic

See here for instructions on installing gcloud.

Mac Link to heading

Assuming you have Homebrew installed.

brew install kubectl
brew install google-cloud-sdk

Setting up GKE Link to heading

Update Link to heading

Make sure gcloud is up-to-date.

gcloud components update

Create a Project Link to heading

If you’ve not already setup one, we need to create a project. We must call it something unique. For this blog, I’ll call it guestbook-jb. If you’ve never used to gcloud before, you need to initialise it. You can follow the steps to create a new project.

gcloud init

Otherwise, do the following.

gcloud projects create guestbook-jb

Then we need to set it as the project ID.

gcloud config set project guestbook-jb

Start a Cluster Link to heading

Now that we have gcloud setup, we can start a Kubernetes cluster.

gcloud container clusters create guestbook --scopes "userinfo-email","cloud-platform","datastore","default" --num-nodes 2

The --scopes flag specifies which other Google Cloud APIs your cluster can utilise. We’ll only use 2 nodes for this demonstration to keep costs sane.

We then want to copy the cluster’s config into our kubectl.

gcloud container clusters get-credentials guestbook

We can then test by looking for our 2 nodes.

kubectl get nodes

Django Stack on Kubernetes Link to heading

We need generate completed yaml manifest files. Make sure you’re in the repository directory.

make templates

Secrets Link to heading

Kubernetes has a built in secrets manager which allows you to store secrets and then inject them into pods via different means.

Having generated the templates, the file secrets.yaml will have been created with a randomly generated password. This password will then be injected into the Django and Postgres pods.

To apply these secrets.

kubectl apply -f ./secrets.yaml

Redis Link to heading

We’re going to use Redis to cache the amount of page views.

kubectl apply -f ./redis.yaml

Postgres Link to heading

Postgres is a bit more complex because we need a persistent volume to store our data. Fortunately, Kubernetes also has a built in component to handle this too.

First of all, we need to crate a volume in GCP.

gcloud compute disks create pg-data  --size 200GB

This is then tied to the database by Kubernetes with the persistentvolume and persistentvolumeclaim directive in the postgres.yaml manifest.

To handle Kubernetes secrets, we’ve got a very simple, custom Docker image in the postgres directory. This needs to be built and pushed to our registry.

cd postgres/

docker build . --tag gcr.io/$(gcloud config list project --format="value(core.project)")/postgres-pw:v1

gcloud docker -- push gcr.io/$(gcloud config list project --format="value(core.project)")/postgres-pw:v1

Finally, we can deploy Postgres.

cd ../
kubectl apply -f ./postgres.yaml

Note there may been some errors thrown regarding persistent volume claims, this should clear up in a minute or less; it’s a race condition within Kubernetes.

Django Link to heading

We’re finally ready to deploy Django. The manifest also defines a load balancer which will act as Nginx or Apache would in a legacy deployment.

First, we need to build and push the initial Docker image. The Dockerfile for our guestbook is generic enough to be used with most basic Django deployments. You will, however, need to change the settings.py to get the correct username and password from the mounted secrets directory. Open settings.py to see how this handled.

cd guestbook/

docker build . --tag gcr.io/$(gcloud config list project --format="value(core.project)")/guestbook:v1

gcloud docker -- push gcr.io/$(gcloud config list project --format="value(core.project)")/guestbook:v1

We can then deploy.

cd ../
kubectl apply -f guestbook.yaml

And run migrations.

kubectl exec $(kubectl get pods | grep guestbook | awk '{ print $1 }') python ./manage.py migrate

We can then get the public IP address from the load balancer.

kubectl get services

You should then be able to connect to that IP address and see your Django site!

Operating the Deployment Link to heading

This is all great, but hasn’t really bought us much over a legacy deployment. Let’s explore a couple of very simple things Kubernetes buys us straight away.

Rolling out an update Link to heading

We can roll out updates to our Django application. These roll outs are zero downtime and will stop if there is an issue that means that Django won’t start.

First off make a change, then build and push the Docker image. Pay attention to change the version tag.

cd guestbook/

docker build . --tag gcr.io/$(gcloud config list project --format="value(core.project)")/guestbook:v2

gcloud docker -- push gcr.io/$(gcloud config list project --format="value(core.project)")/guestbook:v2

Now we need to tell Kubernetes to use the new image. This will create a new pod and only swap if the container inside starts (i.e. Django starts).

kubectl set image deployment/guestbook guestbook=gcr.io/$(gcloud config list project --format="value(core.project)")/guestbook:v2

We can see if the roll out is working.

kubectl rollout status deployment/guestbook

You can read more about roll outs here.

Scaling Link to heading

Another thing we might want to do is scale our Django application.

We can either do that manually.

kubectl scale deployment guestbook --replicas=3

Or we can set criteria for automatic scaling.

kubectl autoscale deployment guestbook --min=1 --max=5 --cpu-percent=80

You can read more about scaling here.

Cleaning Up Link to heading

If you’ve just run through this as a demonstration, I advise you clean up the cluster and storage to keep costs down.

gcloud container clusters delete guestbook
gcloud compute disks delete pg-data