Dockerizing Your Django App

Introduction

When I’m at work I frequently encounter situations that could be quite messy. I was working on developing a supporting tool recently, that contained a Python Django-based app and another jar file that had to be invoked and sends the output to the Django app for the web dashboard to display the .jar file output.

It was really cumbersome. By then I had no idea about docker, my best bet was to:

  • Configure host system to run the jar file smoothly
  • Run the Django application inside a virtual environment

Both on a virtual machine. Setting the Django web application was the easy part, the difficult part was the Java environment. JDK vs JRE paths, versions, dependencies, … it was hell.

After that, I had an idea: since I have two almost-independent environments that need to run, and I need both to be kind of close to one another, why not learn Docker?

Ok, let me get something straight at first: this article doesn’t intend to teach you Docker nor Python nor Django. However, it should act as a reference of how to quickly “Dockerize” your application that is Django-based.

Tutorial

Ok, so let’s get to the commands and see how Dockerizing your django app could be achieved.

Creating a test Django app

Let’s create our sample Django application:

If you don’t have Django installed:

$ pip install django

This could be pip or pip3, depending on your own set up and if you’re using the default Ubuntu-shipped pip or not. You can run:

$ which pip

to know which pip is being invoked.

Creating the app

We will create a sample project called theproject.

$ django-admin startproject theproject
# Let's go inside the directory and run it to test
$ cd theproject/
$ python manage.py runserver
Amazing! Sample app is running on port 8000.

Now that the app is running fine, let’s “dockerize” it. To dockerize the app, we will need to create a Dockerfile to guide Docker on how to build the image that we need for this application to run. This includes dependencies, what port to expose to the outside world so that we can access the app from outside Docker, and many more settings that we will not tackle here (volumes, for instance.)

Dockerfile

FROM python:3
ENV PYTHONUNBUFFERED 1
RUN mkdir /django_on_docker/
COPY requirements.txt /django_on_docker/

WORKDIR /django_on_docker

RUN pip install -r requirements.txt

COPY . /django_on_docker

EXPOSE 8000

CMD ["ls"]
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

What we wrote basically was importing the default Python image, create a directory in the container, copy requirements.txt if we have one, to mention our dependencies (in our case, it’s “Django==2.2.6”)

If we’re not sure, we can use pip freeze > requirements.txt command to generate a txt file of the current installed packages.

Creating the image

$ docker build -t testdjango .

You might have some sudo problems if you’re on Ubuntu. The previous command builds an image and calls it “testdjango” from the Dockerfile in the path “.” (current directory)

Output:

Sending build context to Docker daemon  20.48kB
Step 1/11 : FROM python:3
 ---> 02d2bb146b3b
Step 2/11 : ENV PYTHONUNBUFFERED 1
 ---> Using cache
 ---> f189027eb76f
Step 3/11 : RUN mkdir /django_on_docker
 ---> Using cache
 ---> 00cbac3a8583
Step 4/11 : COPY requirements.txt /django_on_docker/
 ---> f18487ddaee8
Step 5/11 : WORKDIR /django_on_docker
 ---> Running in f17fe8db4a7e
Removing intermediate container f17fe8db4a7e
 ---> 9d260da6f98f
Step 6/11 : RUN pip install -r requirements.txt
 ---> Running in f00e52ca07b8
Collecting Django==2.2.6 (from -r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/b2/79/df0ffea7bf1e02c073c2633702c90f4384645c40a1dd09a308e02ef0c817/Django-2.2.6-py3-none-any.whl (7.5MB)
Collecting sqlparse (from Django==2.2.6->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/ef/53/900f7d2a54557c6a37886585a91336520e5539e3ae2423ff1102daf4f3a7/sqlparse-0.3.0-py2.py3-none-any.whl
Collecting pytz (from Django==2.2.6->-r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/e7/f9/f0b53f88060247251bf481fa6ea62cd0d25bf1b11a87888e53ce5b7c8ad2/pytz-2019.3-py2.py3-none-any.whl (509kB)
Installing collected packages: sqlparse, pytz, Django
Successfully installed Django-2.2.6 pytz-2019.3 sqlparse-0.3.0
Removing intermediate container f00e52ca07b8
 ---> 313f0daa51db
Step 7/11 : COPY . /django_on_docker
 ---> 94c34b7e0786
Step 8/11 : WORKDIR /django_on_docker/
 ---> Running in eadfe9421c59
Removing intermediate container eadfe9421c59
 ---> b8767b36c53f
Step 9/11 : EXPOSE 8000
 ---> Running in 5917cb5c3789
Removing intermediate container 5917cb5c3789
 ---> 8137bb6fdfe6
Step 10/11 : CMD ["ls"]
 ---> Running in 83d1a91ce005
Removing intermediate container 83d1a91ce005
 ---> ec5558b57984
Step 11/11 : CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
 ---> Running in 4ce8bea1af13
Removing intermediate container 4ce8bea1af13
 ---> fb67dd3ed80f
Successfully built fb67dd3ed80f
Successfully tagged testdjango:latest

As we can see, Docker built the image and installed Django for us, and also run the commands that we mentioned and it should now be up and running!

The container

So, now we can create a container based on the image that we had built.

$ docker run -p 8000:8000 testdjango

Of course there are many options we’re omitting: volumes, naming the container, user namespace, running in interactive mode and more. Just for the sake of getting things up and running fast!

I might look into writing about that in a following series!

Quick testing

Now we can go to our localhost via the browser, on port 8000 and we find that it’s up and running! If things go smooth, you’re probably happy that Dockerizing your Django app didn’t take that long!