Deployment Automation (Django + Docker + ECS + Fargate)
Cloud service has become an indispensable part for us, like air.
I still vividly remember the exhilarating feeling when I first deployed my clumsy web application to an EC2 instance. However, as a novice developer back then, I encountered numerous challenges, particularly with:
- Nginx
- Security Group
- SSH Key
- Running the server in the background
These components didn’t provide clues suitable to my beginner level, leading to significant trial and error.
Manual Deployment Challenges
While initial deployment costs are justifiable, connecting to the instance via SSH key and running git pull
for every update is tedious. Although SSH key access is straightforward, novice developers often feel an uneasy reluctance towards directly accessing instances. The reluctance leads to infrequent updates and can diminish interest in the service being developed.
Introduction to Automation with GitHub Actions
To address these issues, we’ll automate the process of dockerizing our application and deploying it to AWS using GitHub Actions. this may sound daunting, but by leveraging AWS services, we’ll just need to provide the correct configuration values. Deployment becomes repetitive and mundane once the process is established, and as developers, avoiding repetitive tasks is our destiny.
Prerequisites
- Create a Django proejct
- Install Docker
Building the Docker Image
Before writing the Dockerfile, list the required Python libs in requirements.txt
. for this example, we’ll use django
and mysqlclient
.
django==3.1
mysqlclient==2.0.1
The `Dockerfile` executes a set of commands (DSL) to create the Docker image. Let’s create a Dockerfile in the project’s root directory.
FROM python:3.7
ENV PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt /app/
RUN pip install -r requirements.txt
COPY . /app/
EXPOSE 8080
CMD ["python3", "manage.py", "runserver", "0.0.0.0:8080"]
Now build the Docker image using the following command:
docker build . -t ecs-tutorial
If logs like the below have been printed, it means the image build was successful.
> docker build . -t ecs-tutorial
Step 1/8 : FROM python:3.7
---> 2699987679cd
Step 2/8 : ENV PYTHONUNBUFFERED=1
---> Using cache
---> b1ecf661981c
Step 3/8 : WORKDIR /app
---> Using cache
---> 4f89f55da03a
Step 4/8 : COPY requirements.txt /app/
---> Using cache
---> 363862418ed6
Step 5/8 : RUN pip install -r requirements.txt
---> Using cache
---> ee9a55e82551
Step 6/8 : COPY . /app/
---> Using cache
---> c0b955af8faf
Step 7/8 : EXPOSE 8080
---> Using cache
---> ebb82e5e1931
Step 8/8 : CMD [ "python3", "manage.py", "runserver", "0.0.0.0:8080" ]
---> Using cache
---> ba3d6c22cbb9
Successfully built ba3d6c22cbb9
Successfully tagged ecs-tutorial:latest
You can see the list of images you have created by entering the docker images
command.
REPOSITORY TAG IMAGE ID CREATED SIZE
ecs-tutorial latest 8b9365a9f930 2 seconds ago 919MB
...
Running the Docker Container
to run the image:
docker run -d -p 8080:8080 ecs-tutorial
Access 127.0.0.1:8080
to see the Django rocket welcoming you.
Creating a Repository in AWS ECR
ECR is a managed container image registry service by AWS. It securely stores Docker images, and integrates seemless with other AWS services.
Create a repository named repo-for-ecs-tutorial
in ECR. We will later upload our Docker image here Using GitHub Actions.
Creating a Task Definition
A task definition in ECS is similar to docker-compose
. It specifies the Docker image, port mappings, instance type, CPU, and memory allocations. Instead of using the AWS console, we’ll create a task-definition.json
file.
Create the task-definition.json
in your project root:
{
"executionRoleArn": "arn:aws:iam::your_account_id:role/ecsTaskExecutionRole",
"containerDefinitions": [
{
"name": "ecs-tutorial",
"image": "your_account_id.dkr.ecr.region.amazonaws.com/repo-for-ecs-tutorial:latest",
"memory": 512,
"cpu": 256,
"essential": true,
"portMappings": [
{
"containerPort": 8080,
"hostPort": 8080
}
]
}
],
"family": "ecs-tutorial"
}
Register the task definition using the AWS CLI:
aws ecs register-task-definition --cli-input-json file://task-definition.json
This command registers the task definition in your AWS account. You can verify the task definition in the AWS Management Console by navigating to the ECS service and selecting the “Task Definitions” tab.
Understanding Roles and Permissions
If you do not have an executionRoleArn
created, follow these steps to create one:
- Open the IAM console at https://console.aws.amazon.com/iam/.
- In the navigation pane, choose “Roles,” then “Create role.”
- Select “Elastic Container Service” as the type of trusted entity.
- For the use case, choose “Elastic Container Service Task” and then select “Next: Permissions.”
- Search for
AmazonECSTaskExecutionRolePolicy
, select it, and proceed to the next step. - Name the role
ecsTaskExecutionRole
and create the role.
Once created, the ARN for this role can be found in the IAM console under the role’s details. Use this ARN in your task-definition.json
file.
By following these steps and using the provided example, you should have a comprehensive understanding of creating and registering a task definition for AWS ECS.
Creating an ECS Cluster and Service
Define an ECS cluster, which is a logical grouping of tasks of services. AWS provides a default clsuter, but we’ll create a new one named cluster-for-ecs-tutorial
using the Network only
template for Fargate.
Creating an Application Load Balancer
In the EC2 console, create an ALB named alb-for-ecs-tutorial
. Configure the security settings to open port 80 to anywhere. Create a target group with IP as the target type. Associate this ALB with service created in ECS.
Automating Deployment With GitHub Actions
Configure GitHub Actions to deploy the Docker image to ECR and update the ECS service. Add the required secrets (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`) to GitHub. you can see the example of mine here.
Create a workflow file in your repository at .github/workflows/aws.yml
:
name: Deploy to Amazon ECS
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push image to Amazon ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: repo-for-ecs-tutorial
IMAGE_TAG: latest
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
- name: Deploy to Amazon ECS
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: task-definition.json
service: ecs-tutorial-service
cluster: cluster-for-ecs-tutorial
wait-for-service-stability: true
Conclusion
By automating the deployment process, we can now focus more on developing our service. In future, we can explore advanced topics like applying SSL cerificates using AWS Certificate Manager, configuring HTTPS for ALB, and using AWS System Manager for environment variables.
References
this document has been created to help consolidate knowleged and make the deployment process smoother and more understandable.