Creating a Continuous Deployment workflow using Github Actions to deploy your application to ECS

wednesdaysol

Wednesday Solutions

Posted on September 23, 2021

Creating a Continuous Deployment workflow using Github Actions to deploy your application to ECS

A well-written Continuous Deployment (CD) pipeline ensures that on every merge to a release branch, an artifact is created and deployed to the correct environment.

While working with containerized applications the CD pipeline needs to contain the following steps

  • Code checkout
  • Install dependencies
  • Build the image
  • Push to a container registry
  • Use the latest image for the next deployment
  • Trigger a new deployment

In this tutorial, we will write a CD pipeline that does all of the above tasks using Github Actions. We will deploy our application using AWS ECS.

AWS ECS is a fully managed container orchestration service from AWS. It helps you easily deploy, manage and scale containerized applications.

This tutorial assumes that you have a solid understanding of

It also makes the implicit assumption that your application is already deployed on ECS. At the end of this tutorial you will be able automate your deployments on merge to a release branch.

In this tutorial I will take you through how to

  • Trigger a workflow on merge to a release branch.
  • Build and push the image to the Elastic Container Registry
  • Update the task-definition using the newly created image
  • Deploy your application to the new environment

Starter Project

Please clone the following repository: https://github.com/wednesday-solutions/ecs-cd-starter

Setup database connection

Update the relevant database connection details in the .env.development

DB_URI=postgres://<role>:<password>@<host>:<port>/<db>
POSTGRES_HOST=<host>
POSTGRES_DB=<db>
POSTGRES_USER=<user>
POSTGRES_PASSWORD=<password>
Enter fullscreen mode Exit fullscreen mode

Add secrets

We need to add the following secrets

  • AWS_ACCESS_KEY_ID ACCESS_KEY_ID with access to deploy
  • AWS_SECRET_ACCESS_KEY Associated SECRET_ACCESS_KEY
  • AWS_REGION Region in which the cluster is deployed
  • AWS_ECR_REPOSITORY Name of the ECR repository that we will push the image to.

Step 1

Add the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY secrets*.*

Alt Text

Alt Text

Step 2

Add the AWS deployment region

Alt Text

Step 3

Get the repository name from the AWS console and add it as a secret.

Alt Text

I have omitted the -dev since that represents the stage. We will be using the same workflow to deploy to multiple environments and will hence infer the stage at runtime.

Alt Text

Create the task-definition.json for all the environments

Step 1

Go to the following URL: https://ap-south-1.console.aws.amazon.com/ecs/home?region=ap-south-1#/taskDefinitions

I'm using ap-south-1 as the AWS region. Please change the URL according to the region you are in

Alt Text

Step 2
Select the task definition for your environment and project. In my case its the ecs-cd-starter-dev

Alt Text

Step 3

Select the latest revision and go to the JSON tab

Alt Text

Step 4

Copy the JSON.
Run the following snippet in the terminal

mkdir task-definition
touch task-definition/dev.json
Enter fullscreen mode Exit fullscreen mode

Paste the JSON from the AWS console into the newly created file.

I use dev.json since dev is my default branch name. I want the code that is pushed to this branch deployed to the dev environment.

You will need to repeat this step for the qa and production environments.

Trigger the workflow to run on merge to a release branch

Step 1 - Create workflow

Create a new workflow for continuous deployment in the .github/workflows folder

touch .github/workflows/cd.yml
Enter fullscreen mode Exit fullscreen mode

Step 2 - Setup triggers

Identify your release branches. The first release branch will be your default branch which should also be the branch that the team typically raises a pull request to when they want to add a new feature.

In my case this is the dev branch. Typically you would have 2 more environments.

  • qa
  • production

So let's trigger this workflow whenever there is a push on one of these branches.

name: ECS Starter CD Develop

on:
  push:
    branches:
      - dev
      - qa
      - master
jobs:
  docker-build-push-deploy:
    name: Docker build, push and deploy
    runs-on: ubuntu-latest
    steps:
Enter fullscreen mode Exit fullscreen mode

Paste the above code into the newly created cd.yml file

Step 3 - Checkout code

This step pulls the latest code.

...
    steps:
      - name: Checkout
        uses: actions/checkout@v2
Enter fullscreen mode Exit fullscreen mode

Step 4 - Get branch name

Get the current branch name. This step will fetch the current git branch name and store it. It can now be accessed like so: ${{steps.vars.outputs.stage}}

...
    steps:
      ...
      - name: Get branch name
        id: vars
        run: echo ::set-output name=stage::${GITHUB_REF#refs/*/}
Enter fullscreen mode Exit fullscreen mode

Step 5 - Configure AWS Credentials

Configure AWS Credentials and region. Use the values from Github secrets to configure the AWS Credentials.
To get a better understanding of all of the configuration options please go through the documentation here: https://github.com/aws-actions/configure-aws-credentials#usage

...
    steps:
      ...
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}
Enter fullscreen mode Exit fullscreen mode

Step 6 - Login to ECR

Use the aws-actions/amazon-ecr action to log in to AWS ECR.

To get a better understanding of all of the configuration options please go through the documentation here: https://github.com/aws-actions/amazon-ecr-login#usage

...
    steps:
      ...
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1
Enter fullscreen mode Exit fullscreen mode

Step 7 - Build tag and push image to ECR

We now need to build the docker image,. tag and push it to AWS ECR. Use the commit hash to tag the image.

...
    steps:
      ...
      - name: Build, tag, and push image to Amazon ECR
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: ${{ secrets.AWS_ECR_REPOSITORY }}-${{steps.vars.outputs.stage}}
          AWS_REGION: ${{ secrets.AWS_REGION }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG3
Enter fullscreen mode Exit fullscreen mode

Step 8 - Render the task definition

We create a new task-definition revision by updating the value of image. We will point to the image that we just pushed to ECR.

...
    steps:
      ...
      - name: Render Amazon ECS task definition
        id: ecs-cd-starter-container
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: task-definition/${{steps.vars.outputs.stage}}.json #1
          container-name: ecs-cd-starter-${{steps.vars.outputs.stage}} #2
          image: ${{ steps.login-ecr.outputs.registry }}/${{ secrets.AWS_ECR_REPOSITORY }}-${{steps.vars.outputs.stage}}:${{ github.sha }}
Enter fullscreen mode Exit fullscreen mode
  1. the task-definition folder will contain task-definitions for all of the environments. I create json files with the name of the environment and access it using ${{steps.vars.outputs.stage}}.json in the workflow
  2. I name my containers with the stage as the suffix. I reference it using <container-name>${{steps.vars.outputs.stage}}.json in the workflow.
  3. We reference the image that we just pushed to the ECR registry.

Step 9 - Deploy to ECS

We use the latest revision of the task-definition that we just created to deploy the application to ECS. I use the same -branchName suffix when naming my service and cluster

...
...
    steps:
      ...
      - name: Deploy to Amazon ECS service
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: ${{ steps.pawlyclinic-api-container.outputs.task-definition }}
          service: ecs-cd-starter-${{ steps.vars.outputs.stage }}
          cluster: ecs-cd-starter-${{ steps.vars.outputs.stage }}
Enter fullscreen mode Exit fullscreen mode

Step 10 - Logout of ECR

Once the deployment is done logout of ECR

...
    steps:
      ...
      - name: Logout of Amazon ECR
        if: always()
        run: docker logout ${{ steps.login-ecr.outputs.registry }}
Enter fullscreen mode Exit fullscreen mode

Where to go from here

Now that you have setup a CD pipeline to deploy your application to ECS I would recommend reading our article on "how to execute batch jobs in a multi-container environment"

I hope you enjoyed this tutorial on how to create a CD pipeline to deploy your ECS application. If you have any questions or comments, please join the forum discussion below.

💖 💪 🙅 🚩
wednesdaysol
Wednesday Solutions

Posted on September 23, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related