Deployment Automation using GitHub Actions with ECS

Deployment Automation (Django + Docker + ECS + Fargate)

Deployment Automation using GitHub Actions with ECS

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:

  1. Open the IAM console at https://console.aws.amazon.com/iam/.
  2. In the navigation pane, choose “Roles,” then “Create role.”
  3. Select “Elastic Container Service” as the type of trusted entity.
  4. For the use case, choose “Elastic Container Service Task” and then select “Next: Permissions.”
  5. Search for AmazonECSTaskExecutionRolePolicy, select it, and proceed to the next step.
  6. 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.