Django and Docker, one “how to” make the things happen

For intermediate developers / DevOps who wants upgrade your environments using Docker, Alpine, Supervisor, Nginx and Gunicorn.

Docker Image. Alpine + Nginx + Supervisor + Nginx + Gunicorn + Django

Introduction

This illustrates one of many ways to create a Django App and how to dockerize it. As the figure shows, this is an Alpine image with Python 3.8, Supervisor as Process Control System to manage Nginx, Gunicorn and Django application.

Prerequisites

  1. Docker
  2. A Django Project (in this case, I am going to use this simple code of gmaron/django-hello-world). Also, if you have some issues with your Django project, please let me know in the comments below.

Let’s get started

Supervisor is a client/server system that allows its users to monitor and control a number of processes on UNIX-like operating systems.

It shares some of the same goals of programs like launchd, daemontools, and runit. Unlike some of these programs, it is not meant to be run as a substitute for init as “process id 1”. Instead it is meant to be used to control processes related to a project or a customer, and is meant to start like any other program at boot time.

Supervisor gave us the opportunity to get resilience and a major control of our process. For example, Nginx and Gunicorn are two different process with its memory space, PID and other things. Supervisor allows us where to put logs, start retries, number of process, auto start and some other cool functions.

In this case, we have to control Nginx and Gunicorn. In case of Nginx the command to execute is no more no less

In the case of Gunicorn, we create a bash file with some instructions. By the way, we put the command to execute

supervisord.conf

Nginx is a web server which can also be used as a reverse proxy, load balancer, mail proxy and HTTP cache.

Simple. We are going to configure the Ngnix as proxy_pass to the Gunicorn socket. The port we configured is the same as the default HTTP port.

nginx.conf

Gunicorn ‘Green Unicorn’ is a Python WSGI HTTP Server for UNIX. It’s a pre-fork worker model. The Gunicorn server is broadly compatible with various web frameworks, simply implemented, light on server resources, and fairly speedy.

Here there is two concepts of what is going on behind the hood. As a normal execution, we activate our virtuaenv, run the server and access directly to Django app through HTTP. In this case, we are going to open a socket to generate the communication with Nginx and make the responses over there. So Nginx is the “face” to HTTP requests.

Gunicorn can start workers like threads to get more than one app. So Nginx acts like a Load Balancer for this workers. In case one worker is down for something, the application can reply to another request.

gunicorn_start.sh

This is the development side. To complete the docker commands and for the rest of the article I am going to use django-hello-world.

Docker can build images automatically by reading the instructions from a Dockerfile. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. Using docker build users can create an automated build that executes several command-line instructions in succession.

In the Dockerfile, we have the basic instructions for the clean build of docker image. As you can see, there is a simple line-by-line as a typical command line. Update the dependencies of the OS, choose the correct python version, install Nginx and Gunicorn and pip dependencies. The difference is we are going to use Supervisor as process control manager instead of systemd in the Linux ecosystem, for example.

As we can see in the first line, we started from FROM python:3.8.0-alpine which help us to make sure that we are running on Alpine with the binaries of Python 3.8.0.

Dockerfile

Amplification: Gunicorn does not belong inside requirements.txt since it’s only necessary inside the docker image. If you develop over Django project, you can use python manage.py runserver (with the virtualenv activated).

Hands on!

The steps for testing this “how to” is simple.

Make sure that you are in the root folder of the project or your project and execute docker build command. You would see something like below.

Then, you can check if everything is right executingdocker image ls to make sure that this image is there.

For the last step, you can run this image into a container. In my case, I have some environment vars into my hello_world/development.env file so I have to put it in the --env-file argument.

Now, we can test if everything is working putting in the browser localhost:8000 and we will see the Django welcome page.

References

Conclusions

This is not the only way to do that. This is the best way that I found, using it in production in so many microservices. This approach guarantees the health of all ecosystem points. Beginning from the django app passing to Nginx and the Supervisor doing some resilience mechanisms.

One issue founded was the limit of the number of workers due to your machine or server limitations. It will be catastrophic put 40 workers in a t2.mico.

To sum up, I hope this solution helps you in your work and if you see some pain points or improvements points, please let me know in the comments below.

Have a nice coding!

g

Computer Engineer 👨🏻‍💻 Nerd 🤓 CTO @ Maxscholar👨🏻‍💼 Polimath 📚 Writer ✍🏼