Deploying a simple Django (DRF) app to a Kubernetes cluster, part 2: running on Gunicorn

Introduction

In my previous post I described how to deploy a django restframework app to a Kubernetes cluster. However, I used the ‘runserver’ command which is not suitable for production. Some research taught that Gunicorn is a far better choice on production, and in this article I will describe the rather simple steps to deploy this app with Gunicorn.

The requirements file

Add ‘gunicorn’ to the requirements.txt file:

django
djangorestframework
psycopg2
gunicorn

Now run:

pip install -r requirements.txt

You should do this in your virtual python environment of course.

The shellscript

If you are following along with our previous project, you will have an entrypoint.sh shellscript. Change this as follows:

#!/bin/bash
python manage.py migrate
echo "from django.contrib.auth.models import User; User.objects.create_superuser('$DJANGO_SUPERUSER_USERNAME','$DJANGO_SUPER_USER_EMAIL','$DJANGO_SUPERUSER_PASSWORD')" | python manage.py shell 
gunicorn --bind :8000 --workers 3 --threads 2 --timeout 90  webevents.wsgi

As you can see only the last line has changed. I will explain the arguments:

  • bind: the port to bind to, in our case 8000
  • workers: the number of worker processes to handle the requests
  • threads: the number of threads per worker
  • timeout: workers silent for more than this many seconds are killed and restarted.

Serving static files

When working on this, I found out that gunicorn does not serve static files out of the box. For that we need to change our urls file in the webevents directory as follows:

from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.urls import path, include

urlpatterns=[
    path('admin/', admin.site.urls),
    path('', include('eventsapi.urls')),
]

urlpatterns+=staticfiles_urlpatterns()

We just add the staticfiles to our url patterns. Doing that ensures we can see the static files.

If you are in Linux or MacOS you should now be able to run it, if you are on Windows you have no such luck since Gunicorn does not run on Windows

Dockerize!

If you have followed along with our previous project, you also have built a Dockerfile. Open a terminal or commandline in your project and type:

docker build -t <your hubusername>/webeventspython:latest .

Make sure Docker/Docker desktop is running

After this has finished succesfully, you can push it to the hub:

docker push <your hubusername>/webeventspython:latest

Depending on your internetspeed, this can take some minutes

Deploying it to your local Kubernetes

For this you need to have a local kubernetes cluster running, I myself prefer minikube.

If you are using minikube, type in your terminal or commandline:

minikube start

In your project go the kubernetes folder and open the web-deployment.yaml file and change the image tag to this:

image: <your hubusername>/webeventspython:latest

Deploying the database

Open a terminal or commandline in your kubernetes directory and type the following (that is, if you are still using our project):

kubectl apply -f db-configmap.yaml
kubectl apply -f db-persistent-volume.yaml
kubectl apply -f db-persistent-volume-claim.yaml
kubectl apply -f db-deployment.yaml
kubectl apply -f db-service.yaml

This will deploy the postgres database which we will need for our API.

Deploying the API

In the same terminal type:

kubectl apply -f web-deployment.yaml
kubectl apply -f web-service.yaml

Time to test

If you are using minikube you can now type:

minikube service web-service

A web browser will now open. Add ‘/admin’ to URL in the address bar, enter your credentials, and you can see our API is still working

Conclusion

As you can see, changing from ‘runserver’ to ‘gunicorn’ is not very hard. From the documentation I understand that gunicorn can be used in production, however it is often highly recommended to put an nginx proxy in front of it, but this is for another post.

Leave a Reply

Your email address will not be published. Required fields are marked *