In this article we will learn how to package Django app into Docker. And then running this Docker image on ECS.

ECS is an EC2 cluster service offered by AWS, is highly scalable and easy to manage docker images.

Prerequisites:

Installed Docker (https://docs.docker.com/engine/installation/) AWS account with AWS client installed on local machine(optional) Basic understanding of Gunicorn and Nginx

Setting up Docker Image

First check Docker is installed properly:

$ docker info

For running Docker container, we have generally three steps:

Create Dockerfile (think it as a requirement.txt or Gemfile or build.sbt) Build Docker Image using Dockerfile Run Docker image on a container

So let’s start creating our Dockerfile. First we will make a directory where we will have our Dockerfile and other config files.

$ mkdir gunicorn_docker

$ cd gunicorn_docker

$ mkdir code

code directory will have the django app.

Now create a file with name Dockerfile . And paste the code given below:

############################################################

# Dockerfile to run a Django-based web application

# Based on an AMI

############################################################ # Set the base image to use to Ubuntu

FROM ubuntu:14.04

# Set the file maintainer (your name - the file's author)

MAINTAINER Rohit Khatana # Set env variables used in this Dockerfile (add a unique prefix, such as DOCKYARD)

# Local directory with project source

ENV DOCKYARD_SRC=code/django_app

# Directory in container for all project files

ENV DOCKYARD_SRVHOME=/srv

# Directory in container for project source files

ENV DOCKYARD_SRVPROJ=$DOCKYARD_SRVHOME/$DOCKYARD_SRC

# Update the default application repository sources list

RUN apt-get update && apt-get -y upgrade

RUN apt-get install -y python python-pip

RUN apt-get install -y python-dev

RUN apt-get install -y libmysqlclient-dev

RUN apt-get install -y git

RUN apt-get install -y vim

RUN apt-get install -y mysql-server

RUN apt-get install -y nginx # Create application subdirectories

WORKDIR $DOCKYARD_SRVHOME

RUN mkdir media static logs

#read

VOLUME ["$DOCKYARD_SRVHOME/media/", "$DOCKYARD_SRVHOME/logs/"] # Copy application source code to SRCDIR

COPY $DOCKYARD_SRC $DOCKYARD_SRVPROJ # Install Python dependencies

RUN pip install -r $DOCKYARD_SRVPROJ/requirement.txt # Port to expose

EXPOSE 8000 # Copy entrypoint script into the image

WORKDIR $DOCKYARD_SRVPROJ

COPY ./docker-entrypoint.sh /

COPY ./django_nginx.conf /etc/nginx/sites-available/

RUN ln -s /etc/nginx/sites-available/django_nginx.conf /etc/nginx/sites-enabled

RUN echo "daemon off;" >> /etc/nginx/nginx.conf

ENTRYPOINT ["/docker-entrypoint.sh"]

Read the above file carefully, this file is self-explanatory.

But let’s have a step-by-step walk-through:

First, we define the base image, then we define the maintainer (author name). After this we define the environment variable for source code ( DOCKYARD_SRC ), which will then be used to create the source project. DOCKYARD_SRVHOME variable will be used for binding volume for media and log files.

Then we update the OS and install the basic dependencies for the project.

WORKDIR command is used for changing the current directory to respected directory.

Then we expose the port on which docker container will listen. And finally we copy docker-entrypoint.sh and django_nginx.conf (Nginx config for the current Django app). and then symlink the new conf with Nginx’s sites-enabled.

And most importantly we add daemon off ; in nginx.conf , as docker expects the last process to be long running.

ENTRYPOINT command is used to register all necessary scripts which is required to run first before running any other command.

So usually entrypoint scripts have a one time setup, like creating static files, migration etc.

Now as we have our Dockerfile setup, we will create our docker-entrypoint.sh which will have Django migration, collect static commands, run gunicorn in background and finally starts Nginx.

#!/bin/bash

python manage.py migrate # Apply database migrations

python manage.py collectstatic --clear --noinput # clearstatic files

python manage.py collectstatic --noinput # collect static files

# Prepare log files and start outputting logs to stdout

touch /srv/logs/gunicorn.log

touch /srv/logs/access.log

tail -n 0 -f /srv/logs/*.log & echo Starting nginx

# Start Gunicorn processes

echo Starting Gunicorn.

exec gunicorn cv_django_merchandising.wsgi:application \

--name cv_django_merchandising \

--bind unix:django_app.sock \

--workers 3 \

--log-level=info \

--log-file=/srv/logs/gunicorn.log \

--access-logfile=/srv/logs/access.log & exec service nginx start

Now we will look at our django_nginx.conf file



listen 80;

server_name ~^(.+)$;

location = /favicon.ico { access_log off; log_not_found off; }

location /static/ {

root /srv/code/django_app;

}

location / {

include proxy_params;

proxy_pass

}

} server {listen 80;server_name ~^(.+)$;location = /favicon.ico { access_log off; log_not_found off; }location /static/ {root /srv/code/django_app;location / {include proxy_params;proxy_pass http://unix:/srv/code/django_app/django_app.sock;

This is the normal Nginx configuration for Django in which we have defined the listen port for Nginx, then whitelisted every domain. Then we define our location for Django static file, which will be served by Nginx. And finally we bind the root with django_app.sock file for reverse proxy.

Now our directory should look like this:

$ ls gunicorn_docker code/django_app

Dockerfile

docker-entrypoint.sh

django_nginx.conf

Finally we will now build our django_nginx image:

$ docker build -t author/gunicorn-docker .

This will build the docker image with the name: author/gunicorn-docker . Make sure you are in the gunicorn_docker folder, as build command expects Dockerfile in the current directory.

Sit tight, it will take some time, as first it will download the ubuntu image and then will install all the necessary packages and finally copy the source code into docker image. Then it will install the requirement.txt …

When it is done (image building process):

For listing the images:

$ docker images

And now you can run the docker image:

$ docker run --detach=false --publish=8000:80 author/gunicorn-docker

At this time, docker will invoke the ENTRYPOINT scripts. So at this time database migration, static file creation, gunicorn process creation and finally starting nginx will be done.

Now in your browser you can start using this django app which is running in docker container.

You can also run the docker container in detach mode: (first kill the above running process)

$ docker run --detach=true --publish=8000:80 author/gunicorn-docker

You can see the running container by using this command:

$ docker ps

If you want to go inside the container:

$ docker exec -it container-id bash

Now you have your working docker image.

If you want to deploy this image on AWS ECS you have to follow these simple steps:

Install the aws command line client and login $ aws ecr get-login --region ap-southeast-1 Build docker image and tag it with the latest repository $ cd gunicorn_docker $ docker build -t author/gunicorn-docker . $ docker tag author/gunicorn-docker:latest some-id.dkr.ecr.ap-southeast-1.amazonaws.com/author/gunicorn-docker:latest Push to AWS repository $ docker push some-id.dkr.ecr.ap-southeast- 1.amazonaws.com/author/gunicorn-docker:latest Register TASK definition with ECS (you can easily create the task file on aws console) $ aws ecs register-task-definition — cli-input-json file://gunicorn_docker_task.json Now you can run this task using AWS console.

Congratulations on finishing the setup of Docker in ECS. In addition, you also know how to run Gunicorn app with Nginx!