Dockerizing a Django project (part-1)

Abhimanyu Gupta
5 min readJun 5, 2018

--

This post is a tutorial on Docker, in which I will guide you how one can move from a monolithic architecture of a Django Project to a Microservice architecture.

Prerequisite:

  1. Should be familiar with Django (at least the basic architecture)
  2. Familiar with basic Docker concepts/commands.
  3. Familiar with differences between Monolithic vs Microservices Architecture

Hot terms used in the tutorial:

Gunicorn:

Gunicorn (Green Unicorn) is a Python WSGI HTTP Server for UNIX. The WSGI server makes our Linux Web server(e.g Nginx, Apache) to communicate with Django Application Server(127.0.0.1:8000).

Dockerfile:

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.

Note: The complete Dockerfile I have used in this post can be found here.

Entry point:

The ENTRYPOINT specifies a command that will always be executed when the container starts.

Overview of Monolithic vs Microservices:

s
       Monolithic vs Microservice Architecture(Image Source: here)

Generally, a complex Django project is divided into many different components like:

  1. Frontend Code
  2. Backend
  3. Database
  4. Cache Server
  5. Celery

but this tutorial of Dockerization contains splitting the whole project only into 2 parts (microservice):

(a) Web container (contains code only relevant to the web)

(b) Database container (e.g Postgres)

Setting up the Environment:

  • Install docker and docker-engine, refer to this tutorial.
  • Install docker-compose, refer this link for installation.

NOTE:-

During the installation, Docker automatically creates a User Group named docker in Linux system. So if you don’t want to run docker with sudo command every time then you have to add the Current user to this User Group.

Here are the post-installation-steps. If you haven’t done that you might encounter this error if you don’t run docker with sudo command.

docker: Cannot connect to the Docker daemon. Is the docker daemon running on this host?.
See 'docker run --help'.

Well, up to now we have discussed the setup required for this tutorial. Let’s start with actual migrations.

DATABASE PART:

1. We are supposed to make an image of Postgres Database, run:

docker run — name postgres -p 5432:5432 -e POSTGRES_PASSWORD=mysecretpassword -e POSTGRES_USER=randomuser -e POSTGRES_DB=dbname -d postgres

2. This command will directly pull a Postgres instance from hub.docker.com and run the instance with the following properties.

(a) Environment Variables:

POSTGRES_PASSWORD=mysecretpassword
POSTGRES_DB=dbname
POSTGRES_USER=randomuser

(b) Port Mapping:

5432:5432 Request on port 5432 on the host machine will be routed to exposed port 5432 of the docker container.

WEB PART:

  1. Open settings.py file and replace Database settings with this:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': env('DB_NAME'),
'USER': env('DB_USER'),
'PASSWORD': env('DB_PASS'),
'HOST': env('DB_HOST'),
'PORT': env('DB_PORT'),
}
}

2. Create .env in the project root and paste the following:

# New database configuration for docker
POSTGRES_PASSWORD=mysecretpassword
POSTGRES_DB=dbname
POSTGRES_USER=randomuser
DB_HOST='Your_Local_IP_ADDRESS'
DB_PORT=5432

Note: Check your Local IP Address using ifconfigand put it as a DB_HOST

3. Create a new file in the root of the Project Directory named docker-entrypoint.sh paste the following code inside that file.

#!/bin/shpython manage.py migrate — noinput
python manage.py collectstatic — noinput
gunicorn config.wsgi:application -w 3 -b 0.0.0.0:${PORT:-8000}
  • This file is responsible for running the actual Django Application server. It gets invoked/triggered as soon as we start the docker container.

4. Make another file named Dockerfileinside the root of the project folder.

  • Let’s go step-by-step:
# Base Image
FROM python:3.5-alpine
  • I am using alpine Linux as a base image. Alpine is a most lightweight Linux system, the size is even less than 5 MB. It used apk as a package-manager instead of apt-get This base image will be pulled from the Docker hub.
# Initialize
RUN mkdir -p /data/web
COPY . /data/web/
RUN mkdir -p mydjango/static/admin
  • Run is used for running a command inside for the images as what we do in the shell in a Linux System.
  • COPY in Docker world is used to copy from Source(our system) to the Destination (base image). Since Dockerfile is inside the root directory, the whole project is copied to the location /data/web/ of the base image.
# Setup
RUN apk update
RUN apk upgrade
RUN apk add --update python3 python3-dev
RUN apk add libffi-dev zlib-dev
ENV LIBRARY_PATH=/lib:/usr/lib
  • I had already told you what RUN command does inside the image and apk is a package manager. It the above code It will install the dependencies that we will need for running the project.
  • ENV is used to set the Environment Variable inside the base image.
# SetupWORKDIR /data/web
RUN pip3 install -r requirements.txt
RUN pip3 install gunicorn
  • Workdir in Dockerfile does the same as what cd command do for in a Linux system.
  • Since we have now changed our directory to /data/web which contains the requirements.txt file.
  • Above installthe command will install the dependencies. Next line will install an additional package namedgunicorn .
# Copy entrypoint script into the image
COPY ./docker-entrypoint.sh /
ENTRYPOINT [“/docker-entrypoint.sh”]
  • Entrypoint With this command we are locating docker-entry point script that is supposed to get invoked every time the instance of this image will run.

5. Now run docker build -t web . in the terminal.

  • While Building you will see logs something like:
Sending build context to Docker daemon 97.31MB
Step 1/14 : FROM python:3.5-alpine
— -> bd03e573dfc5
Step 2/14 : RUN mkdir -p /data/web
— -> Using cache
— -> cf9cbac7053f
Step 3/14 : COPY . /data/web/
— -> ea08bdf8b6f5
Step 4/14 : RUN mkdir -p mydjango/static/admin
— -> Running in 43332a8ff2cc
Removing intermediate container 43332a8ff2cc
— -> aff18c3edf0c
Step 5/14 : RUN apk update
— -> Running in 5dfe52e47c80
...
...
...
  • Once the build is complete do docker images lsin the terminal you will see something like:
REPOSITORY    TAG      IMAGE ID        CREATED             SIZE
web latest 54982cce50f3 3 minutes ago 601MB
  • Our Docker Image for Web part has been completed. You can now run the instance using :
docker run --name web -p 8000:8000 -d web

If everything goes fine then Docker will be up and running. Cheers :)

In the next part of this tutorial, I will explain how to use docker-compose to automate, pull, build, run and make communications between the instances more easily.

Please comment for any kind of confusion/suggestions etc. Also Follow me as I write mostly about Python, Blockchain and Web development using Django.

Thanks for reading & Happy hacking :)

--

--