Andreas Wittig
Posted on November 19, 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 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.
- Static assets are generated in a Python stage
- Based on the official python image
- Using
pip3
to install the Python dependencies - Copying the app
- Generating the static assets with
python3 manage.py collectstatic
(output goes to theassets
folder)
- The static assets are copied (using
COPY --from=build
) into the NGINX stage which produces the final Docker image- Based on the official nginx image
- 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 of2-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.
- Use our Free Templates for AWS CloudFormation.
- Use our cfn-modules.
- Use the blueprint from our book Rapid Docker on AWS.
Posted on November 19, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 5, 2019