The AWS Docker Templates for Django from TinyStacks enable launching a Django application as a Docker container using AWS CodePipeline. The template includes its own small Django sample application, enabling developers to start a new Django project immediately. Developers can also take the AWS CodePipeline-specific files in this template and use them to ship an existing Django application as a Docker image on AWS.
This sample code is made available under a BSD 3-Clause license. See the LICENSE file.
This solution makes use of a Docker image that comes with Python pre-installed; Django dependencies are installed dynamically when the Docker container is created. If you wish to run just the Docker container locally, you will only need Docker.
If you wish to build and test the Django application directly on your own machine (i.e., not from a Docker container), you should have Python installed locally.
This document also assumes that you have access to an AWS account. If you do not have one, create one before getting started.
Further prerequisites for running these templates on AWS are provided below.
Django is a Python framework for developing Web applications. With Django, developers can quickly create object-relational database mappings, REST APIs, user front ends, and even administrative interfaces. Django implements a Model-View-Controller (MVC) pattern that separates the application's data (model) from its user interface (view) and business logic (controller), making it easy to write cleanly structured code.
This template augments the existing power of Django by packaging your Django app into a Docker image, which can then be deployed onto AWS CodePipeline in a few simple steps. You can fork this repo into your own GitHub account and any changes you make to your Django application will automatically be updated via CodePipeline within minutes.
This sample contains the following files:
- A sample Django API and data model, defined in the directory
itemslist
. - The API and admin entry points for the Django app, defined in the directory
itemsAPP
. - A
wsgi.py
file (in theitemsAPP
directory) for running your Django application on your Docker image using WSGI. - A
Dockerfile
that builds the Django application as a Docker image. - A
requirements.txt
file that enables the Dockerfile to install Django and other dependencies via thepip
command. - A
manage.py
file for executing Django administrative tasks from the command line. - A
build.yml
file for AWS CodeBuild that builds the image and pushes it to Amazon Elastic Container Registry (ECR). - A
release.yml
file for AWS CodeBuild that deploys the image stored in ECR to a Amazon Elastic Container Service (ECS) cluster.
Users can use the build.yml
and release.yml
YAML files to create an AWS CodePipeline pipeline that compiles the latest application into a Docker image, which is then stored in an Amazon Elastic Container Registry (ECR) registry that is accessible to the user. The Django application itself is deployed onto AWS as a Docker container using Amazon Elastic Container Service (Amazon ECS) onto one of the user's available ECS clusters.
The sample application is a simple CRUD (Create/Read/Update/Delete) application that stores data in a local SQLite database (the default data storage method for Django). When this application runs, it presents a set of REST API endpoints that other applications can call to store data.
The application is defined in the itemslist
directory. The file models.py
defines the databsse schema for an item, which consists of two fields: title
and content
.
from django.utils import timezone
class ItemList(models.Model):
title = models.CharField(max_length=250) # a varchar
content = models.TextField(blank=True) # a text field
class Meta:
verbose_name = ("Item")
verbose_name_plural = ("Items")
def __str__(self):
return self.title # name to be shown when called
The application uses the Django REST Framework to define REST API functions in the views.py
file. Two sets of API functions are defined. First, we define ViewSets for the ItemList
class that we defined in our model. We also define ViewSets for the built-in Users and Groups objects in Django, which are used to implement a user authorization model.
class ItemViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows items to be viewed or edited.
"""
queryset = ItemList.objects.all()
serializer_class = ItemSerializer
permission_classes = [permissions.IsAuthenticated]
Second, we use the @api_view
decorator to declare some more advanced API functions such as the item_list
view to view a list of items or to create a new item. The /itemslist
REST method supports all possible CRUD operations for our application by implementing them as their respective HTTP verbs:
Endpoint Type | Description |
---|---|
PUT | Create |
GET | Read |
POST | Update |
DELETE | Delete |
To expose these methods publicly in Django as API endpoints, we must define routes (URL paths) that map to the functions. These are defined in itemsAPP\urls.py
:
urlpatterns = [
path('', include(router.urls)),
path('ping', include(router.urls)),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
path('', include(router.urls)),
url(r'^admin/', admin.site.urls),
url(r'^$', index, name="ItemList"),
path('itemslist/', views.item_list),
path('itemslist/<int:pk>', views.item_detail),
To test out the sample application directly before you package it into a Dockerfile, clone this project locally, then create a virtual environment. Using a virtual environment allows you to install the dependencies required for Django relative to your Django application. This isolates the Django dependencies from other libraries installed on your system, reducing version conflicts across Python applications.
python -m venv venv
Next, activate your virtual environment and run the Django application. To run the Django application, we use the manage.py file, which was automatically generated by the Django framework when we created our Django application and provides a set of commands for managing our environment easily. (For more on creating Django applications, see the official documentation.)
On Linux:
. venv/bin/activate
pip install -r requirements.txt
python manage.py migrate
python manage.py runserver
On Windows (Powershell):
venv\Scripts\activate
pip install -r requirements.txt
python manage.py migrate
python manage.py runserver
To test that the server is running, test its /ping
endpoint from your Web browser by navigating to http://127.0.0.1:8000/ping
. If the server is running, this call will return a pretty-printed page displaying the authenticated API endpoints for your Django application:
To use the unauthenticated endpoint, navigate to http://127.0.0.1:8000/itemslist
:
Note: If you encounter an error when navigating to this page, return to the command line, press CTRL-C, and run the following command:
python manage.py migrate --run-syncdb
Then restart your Django server:
python manage.py runserver
You can use Django's built-in administration UI to view, add, and delete items.
The Dockerfile copies the sample application into a Docker image and runs a Django server.
A Dockerfile uses a Docker base image stored in a public Docker repository and then adds functionality to the image required by your application. This project's Dockerfile is derived from Bitnami's Python 3.6 image, which is freely available on the Amazon ECR Public Gallery. The Dockerfile performs the following steps:
- Copies the Django application into a working directory on the Docker image.
- Installs the contents of the
requirements.txt
file using pip. Requirements.txt contains a list of the Python modules that are required to run your Django application. The file included with this project installs Django; the Django REST Framework; and whitenoise, a package that enables serving static Web site content efficiently. - Starts Django through the
runserver
command defined in the auto-generatedmanage.py
file.
If you have Docker installed, you can build and try out the sample application locally. Open a command prompt to the directory containing the Dockerfile and run the following command:
docker build -t tinystacks/django-crud-app:latest .
Once built, run the Docker command locally, mapping port 8080 on your host machine to port 8000 on the container:
docker run -p 8080:8000 -d tinystacks/django-crud-app:latest
To test that the server is running, test its /ping
endpoint by navigating to http://127.0.0.1:8080/ping
from a Web browser. You should see the same page you saw earlier when running Django locally.
The build.yml
file is an AWS CodeBuild file that builds your Dockerfile and publishes the output to an Amazon ECR registry.
To publish to Amazon ECR, the build script first needs to obtain login credentials to the repository. It does this using a combination of the AWS CLI command aws ecr get-login-password
and the docker login
command. After authentication, the script then builds your Docker image, names it, and tags it with the name latest
to mark it as the most recent build. Finally, it performs a docker push
, publishing the new Docker image to your Amazon ECR Docker repository.
version: 0.2
phases:
build:
commands:
- aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_ENDPOINT
- docker build -t builtimage .
- docker tag builtimage:latest $ECR_IMAGE_URL:latest
- docker push $ECR_IMAGE_URL:latest
To run this in AWS CodeBuild, your build pipeline needs to define the following environment variables:
- ECR_ENDPOINT: The name of the Amazon ECR repository to publish to. This variable takes the format: <accountnumber>.dkr.ecr.<aws-region>.amazonaws.com
- ECR_IMAGE_URL: The name of the Amazon ECR repository plus the name of the container you are publishing. This should take the format: <accountnumber>.dkr.ecr.<aws-region>.amazonaws.com/aws-docker-django
The variable AWS_REGION
is a default global variable that will default to the same AWS region in which your build pipeline is defined. If you need to publish to an Amazon ECR repository in another region, modify this script to use a custom environment variable specifying the correct region. For more information on environment variables, see Environment variables in build environments on the AWS Web site.
The release.yml
file is another AWS CodeBuild file that takes the build output from the build.yml
files (a Docker container image in an Amazon ECR repository) and runs it within an Amazon ECS cluster to which the pipeline has access.
After logging in to the ECR repository using the docker login
command, the script pulls down the image that was compiled and changes its tag from the name of the previous build to the name of the new build. Once the container's label has been updated, the script updates a defined service in Amazon ECS that pulls its image from our published Docker container.
version: 0.2
phases:
build:
commands:
- aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_ENDPOINT
- docker pull $ECR_IMAGE_URL:$PREVIOUS_STAGE_NAME
- docker tag $ECR_IMAGE_URL:$PREVIOUS_STAGE_NAME $ECR_IMAGE_URL:$STAGE_NAME
- docker push $ECR_IMAGE_URL:$STAGE_NAME
- aws ecs update-service --service $SERVICE_NAME --cluster $CLUSTER_ARN --force-new-deployment
In addition to the variables discussed for build.yml
, release.yml
requires several environment variables defined in order to run:
- PREVIOUS_STAGE_NAME: The previous build number or build stage name. This should be the previous Docker image tag name generated by the previous build.
- STAGE_NAME: The current build number or build stage name you wish to use (e.g.,
latest
). - SERVICE_NAME: The name of the Amazon ECS service to run. You will need to define this service yourself once you have the URI to your published container.
- CLUSTER_ARN: The name of the cluster within your Amazon ECS service to which the release script will deploy the container. This should be the name of a custer that is running one or more instances of the service referenced by
SERVICE_NAME
.
If you already have an existing Django application, you can use the core files included in this sample to run them on a Docker container in AWS. Make sure to use your own existing requirements.txt file if you have one.
If your project is already Dockerized (i.e., it has its own Dockerfile), then simply copy over the build.yml
and release.yml
files into the root of your existing project.
If your project is not Dockerized, you will also need to copy over the Dockerfile included in this sample. If your application uses a different port than port 8000, you will also need to update the EXPOSE
line in the Dockerfile to use a different port:
EXPOSE 8000
The method of running a Django server discussed above is recommended for development purposes only. For shipping a production Django application, the Django team recommends running your server using Gunicorn. For more information, see Django's guide to production deployments.
By default, your Django application is set to run with debugging turned on. This is set in the itemsAPP\settings.py
file on this line:
DEBUG = True
If you change DEBUG to False
, you will be required to set a value for the ALLOWED_HOSTS environment setting in the same file. If you do not, your Django application will throw an error and will not run.
The values in the ALLOWED_HOSTS array are matched against the request sender's HTTP Host header. If this value does not match an entry in the ALLOWED_HOSTS list, the request is denied. Currently, this value is blank. You can set it to *
to allow traffic from any location:
ALLOWED_HOSTS = ['*']
If you are using Amazon API Gateway as the entry point to your Django REST API, you should instead set this to the host name used by your gateway.
For more information on ALLOWED_HOSTS, see the Django settings documentation.