DEV Community

Cover image for How to dockerize your Python Django application for AWS Fargate?
Andreas Wittig
Andreas Wittig

Posted on • Originally published at cloudonaut.io

How to dockerize your Python Django application for AWS Fargate?

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 and was first published on cloudonaut.io.

The following post describes how you can dockerize your Python Django application and run it on 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.

# 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/.

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 imge. 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.

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.
#!/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.

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.

Top comments (0)