How to dockerize your Python Django application for AWS Fargate?

Michael Wittig – 19 Nov 2019

The biggest game-changer for Docker on AWS was the announcement of AWS Fargate. Operating Docker containers could not be easier. With AWS Fargate, you launch Docker containers in the cloud without any need to manage virtual machines. Django is a popular Python web framework that encourages rapid development and clean, pragmatic design.

This blog post is an excerpt from our book Rapid Docker on AWS.

The following post describes how you can dockerize your Python Django application and run it on AWS Fargate.

Dockerize your Python Django application for AWS Fargate

Building the Docker images

Two Docker images are needed:

  • NGINX to serve static files and proxy to Django
  • Python Django application

First, you will learn how to build NGINX image. The Dockerfile makes use of multi-stage builds. You can use more than one FROM statement in your Dockerfile, as shown in the following example.

  1. Static assets are generated in a Python stage
    1. Based on the official python image
    2. Using pip3 to install the Python dependencies
    3. Copying the app
    4. Generating the static assets with python3 manage.py collectstatic (output goes to the assets folder)
  2. The static assets are copied (using COPY --from=build) into the NGINX stage which produces the final Docker image
    1. Based on the official nginx image
    2. Copying the static/ folder from the previous stage

Customization Most likely, your folder structure is different. Therefore, adapt the Copy Python files section in the following Dockerfile to your needs.

docker/nginx/Dockerfile
# Static Assets
FROM python:3.7.4 AS build

WORKDIR /usr/src/app

# Install Python dependencies
COPY requirements.txt /usr/src/app/
RUN pip3 install -r requirements.txt

# Copy Python files
COPY example /usr/src/app/example # MODIFY THIS LINE: YOUR FOLDERS ARE DIFFERENT
COPY rapid /usr/src/app/rapid # MODIFY OR REMOVE THIS LINE: YOUR FOLDERS ARE DIFFERENT
COPY manage.py /usr/src/app/

# Build static assets
RUN SECRET_KEY=secret python3 manage.py collectstatic


# NGINX
FROM nginx:1.14

# Configure NGINX
COPY docker/nginx/default.conf /etc/nginx/conf.d/default.conf

# Copy static files
COPY --from=build /usr/src/app/build/ /var/www/html/static
RUN chown -R nginx:nginx /var/www/html

The NGINX configuration file forwards requests to the Python container if the path does not start with /static/.

docker/nginx/default.conf
server {
listen 80;
server_name localhost;
root /var/www/html;

location ~ ^/static/ {
# serve from NGINX
}

location / {
# pass to Python gunicorn based on
# http://docs.gunicorn.org/en/stable/deploy.html
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
# we don't want nginx trying to do something clever with
# redirects, we set the Host: header above already.
proxy_redirect off;
proxy_pass http://127.0.0.1:8000;
}
}

Next, you will learn how to create the Django image. The Dockerfile is based on the official python image with the following additions:

  • wait-for-it is installed to wait for the MySQL database container if you test locally.
  • Python dependencies are installed with pip3 install.
  • A custom etrypoint is defined to run commands before the Django app starts (read on to learn more).
  • gunicorn runs the app.

Customization Most likely, your folder structure is different. Therefore, adapt the Copy Python files section in the following Dockerfile to your needs.

docker/python/Dockerfile
FROM python:3.7.4

WORKDIR /usr/src/app

# Install wait-for-it
COPY docker/wait-for-it.sh /usr/local/bin/
RUN chmod u+x /usr/local/bin/wait-for-it.sh

# Install Python dependencies
COPY requirements.txt /usr/src/app/
RUN pip3 install -r requirements.txt

# Copy Python files
COPY example /usr/src/app/example # MODIFY THIS LINE: YOUR FOLDERS ARE DIFFERENT
COPY rapid /usr/src/app/rapid # MODIFY THIS LINE: YOUR FOLDERS ARE DIFFERENT
COPY manage.py /usr/src/app/

# Configure custom entrypoint to run migrations
COPY docker/python/custom-entrypoint /usr/local/bin/
RUN chmod u+x /usr/local/bin/custom-entrypoint
ENTRYPOINT ["custom-entrypoint"]

# Expose port 8000 and start Python server
EXPOSE 8000
CMD ["gunicorn", "-b", "0.0.0.0", "-w", "2", "rapid.wsgi"]

Customization The -w parameter of gunicorn defines the number of workers and should be in the range of 2-4 x $(NUM_CORES).

The custom entrypoint is used to:

  • Wait for the MySQL container if the WAIT_FOR_IT environment variable is set (used for testing locally only).
  • Run the database migrations before the Django app is started.
docker/python/custom-entrypoint
#!/bin/bash
set -e

if [ -n "${WAIT_FOR_IT}" ]; then
wait-for-it.sh mysql:3306
fi

echo "running migrations"
python3 manage.py migrate

echo "starting $@"
exec "$@"

That’s it. You have everything you need to build both, the NGINX as well as the Django image. Next, you will learn how to test your containers and application locally.

Testing locally

Use Docker Compose to run your application locally. The following docker-compose.yml file configures Docker Compose and starts three containers: NGINX, Django as well as a MySQL database.

docker/docker-compose.yml
version: '3'
services:
nginx:
build:
context: '..'
dockerfile: 'docker/nginx/Dockerfile'
depends_on:
- python
network_mode: 'service:python' # use network interface of python container to simulate awsvpc network mode
python:
build:
context: '..'
dockerfile: 'docker/python/Dockerfile'
ports:
- '8080:80' # forwards port of nginx container
depends_on:
- mysql
environment:
DATABASE_HOST: mysql
DATABASE_NAME: app
DATABASE_USER: app
DATABASE_PASSWORD: secret
SECRET_KEY: secret
WAIT_FOR_IT: 'true'
mysql:
image: 'mysql:5.6'
command: '--default-authentication-plugin=mysql_native_password'
ports:
- '3306:3306'
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: app
MYSQL_USER: app
MYSQL_PASSWORD: secret

The following command starts the application:

docker-compose -f docker/docker-compose.yml up --build

Magically, Docker Compose will spin up three containers: NGINX, Django, and MySQL. Point your browser to http://localhost:8080 to check that your web application is up and running. The log files of all containers will show up in your terminal, which simplifies debugging a lot.

After you have verified that your application is working correctly, cancel the running docker-compose process by pressing CTRL + C, and tear down the containers:

docker-compose -f docker/docker-compose.yml down

Deploying on AWS

You are now ready to deploy your application on AWS.

(1) Build Docker images:

docker build -t python-django-nginx:latest \
-f docker/nginx/Dockerfile .
docker build -t python-django-python:latest \
-f docker/python/Dockerfile .

(2) Create ECR repositories:

aws ecr create-repository --repository-name python-django-nginx \
--query 'repository.repositoryUri' --output text
aws ecr create-repository --repository-name python-django-python \
--query 'repository.repositoryUri' --output text

(3) Login to Docker registry (ECR):

$(aws ecr get-login --no-include-email)

(4) Tag Docker images:

docker tag python-django-nginx:latest \
111111111111.dkr.ecr.eu-west-1.amazonaws.com/\
python-django-nginx:latest
docker tag python-django-python:latest \
111111111111.dkr.ecr.eu-west-1.amazonaws.com/\
python-django-python:latest

(5) Push Docker images:

docker push \
111111111111.dkr.ecr.eu-west-1.amazonaws.com/\
python-django-nginx:latest
docker push \
111111111111.dkr.ecr.eu-west-1.amazonaws.com/\
python-django-python:latest

There is only one step missing: you need to spin up the cloud infrastructure.

  1. Use our Free Templates for AWS CloudFormation.
  2. Use our cfn-modules.
  3. Use the blueprint from our book Rapid Docker on AWS.
Michael Wittig

Michael Wittig

I’m the author of Amazon Web Services in Action. I work as a software engineer, and independent consultant focused on AWS and DevOps.

You can contact me via Email, Twitter, and LinkedIn.

Briefcase icon
Hire me
Cover of Rapid Docker on AWS

New book: Rapid Docker on AWS

A rapid way to get your web application up and running on AWS. Made for web developers and DevOps engineers who want to dockerize their web applications and run their containers on Amazon Web Services. Prior knowledge of Docker and AWS is not required.

Buy icon
Buy now
Marbot Logo

Incident Management for Slack

Team up to solve incidents with our chatbot marbot. Never miss a critical alert. Escalate alerts from your AWS infrastructure among your team members. Strong integrations with all parts of your AWS infrastructure: CloudWatch, Elastic Beanstalk, RDS, EC2, ...

Slack icon
Try for free
📚 Rapid Docker on AWS
A rapid way to get your web application up and running on AWS. Learn how to package your application into Docker containers. Learn more.