Deploying a Dockerised Web Application using AWS ECS and GitHub Actions

Deploying a Dockerised Web Application using AWS ECS and GitHub Actions

Objectives:

  1. Containerise a Web Application: Dockerise a simple web application.

  2. Set Up AWS ECS: Deploy the Dockerised application to AWS ECS (Elastic Container Service).

  3. Implement CI/CD Pipeline: Use GitHub Actions to automate the deployment process.

Step-by-Step Guide

Prerequisites:

  • AWS Account, IAM user with programmatic access keys

  • Basic knowledge of Docker, AWS, and GitHub Actions

  • Installed tools: Docker, AWS CLI, Git, GitHub CLI

Step 1: Containerise the Web Application

  1. Create a simple Node.js web application

    Create a new directory for your project:

     mkdir my-web-app
     cd my-web-app
    

    Initialise a new Node.js project:

     npm init -y
    

    Install Express:

     npm install express
    

    Create an index.js file:

     const express = require('express');
     const app = express();
     const PORT = process.env.PORT || 3000;
    
     app.get('/', (req, res) => {
         res.send('Hello World!');
     });
    
     app.listen(PORT, () => {
         console.log(`Server is running on port ${PORT}`);
     });
    
  2. Create a Dockerfile

     FROM node:14
    
     WORKDIR /app
    
     COPY package*.json ./
    
     RUN npm install
    
     COPY . .
    
     EXPOSE 3000
    
     CMD ["node", "index.js"]
    
  3. Build and test the Docker image locally

    Build the Docker image:

     docker build -t my-web-app .
    

    Run the Docker container:

     docker run -p 3000:3000 my-web-app
    

    Visit http://localhost:3000 to ensure the app is running.

Step 2: Push Docker Image to AWS ECR

  1. Create an ECR repository

    Go to the AWS Management Console, navigate to ECR, and create a new repository named my-web-app.

  2. Authenticate Docker to ECR

    Use the AWS CLI to authenticate Docker to your ECR registry:

     aws ecr get-login-password --region <your-region> | docker login --username AWS --password-stdin <your-aws-account-id>.dkr.ecr.<your-region>.amazonaws.com
    

    Tag and push the Docker image

    Tag the Docker image:

     docker tag my-web-app:latest <your-aws-account-id>.dkr.ecr.<your-region>.amazonaws.com/my-web-app:latest
    

    Push the Docker image to ECR:

     docker push <your-aws-account-id>.dkr.ecr.<your-region>.amazonaws.com/my-web-app:latest
    

Step 3: Set Up AWS ECS

  1. Create a new ECS cluster

    Go to the ECS console, create a new cluster with AWS Fargate (serverless) option leaving the rest as defaults.

  2. Create a new task definition

    Go to Task Definitions, create a new task definition:

    • Launch type: Fargate

    • Container: Add container

      • Select the Image pushed earlier to ECR:

  3. Create a service

    Go to Services, create a new service:

    • Launch type: Fargate

    • Cluster: Select your cluster

    • Task definition: Select your task definition

    • Number of tasks: 1

      • Wait until the service is created and a task is deployed, this could take few minutes. Once done test the app by selecting the task and finding the public IP address, then paste in a new browser tab followed by port number 3000

Finally, export the task definition file and paste it to the application working directory

Step 4: Implement CI/CD Pipeline with GitHub Actions

  1. Create a.github/workflows directory

    In your project directory, create the following structure:

     mkdir -p .github/workflows
    
  2. Create a GitHub Actions workflow file

    Create a new file .github/workflows/aws.yml with the following content:

     name: Deploy to Amazon ECS
    
     on:
       push:
         branches: [ "master" ]
    
     env:
     # set this to your preferred AWS region, e.g. us-west-1
       AWS_REGION: us-east-1
     # set this to your Amazon ECR repository name not the URI
       ECR_REPOSITORY: my-web-app    
     # set this to your Amazon ECS service name
       ECS_SERVICE: my-web-app           
     # set this to your Amazon ECS cluster name
       ECS_CLUSTER: my-web-app                 
     # set this to the path to your Amazon ECS task definition
     # file, e.g. .aws/task-definition.json
       ECS_TASK_DEFINITION: my-web-app.json 
     # set this to the name of the container in the                                           
     # containerDefinitions section of your task definition
       CONTAINER_NAME: my-web-app       
    
     permissions:
       contents: read
    
     jobs:
       deploy:
         name: Deploy
         runs-on: ubuntu-latest
         environment: production
    
         steps:
         - name: Checkout
           uses: actions/checkout@v4
    
         - 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: ${{ env.AWS_REGION }}
    
         - name: Login to Amazon ECR
           id: login-ecr
           uses: aws-actions/amazon-ecr-login@v1
    
         - name: Build, tag, and push image to Amazon ECR
           id: build-image
           env:
             ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
             IMAGE_TAG: ${{ github.sha }}
           run: |
             # Build a docker container and
             # push it to ECR so that it can
             # be deployed to ECS.
             docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
             docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
             echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
         - name: Fill in the new image ID in the Amazon ECS task definition
           id: task-def
           uses: aws-actions/amazon-ecs-render-task-definition@v1
           with:
             task-definition: ${{ env.ECS_TASK_DEFINITION }}
             container-name: ${{ env.CONTAINER_NAME }}
             image: ${{ steps.build-image.outputs.image }}
    
         - name: Deploy Amazon ECS task definition
           uses: aws-actions/amazon-ecs-deploy-task-definition@v1
           with:
             task-definition: ${{ steps.task-def.outputs.task-definition }}
             service: ${{ env.ECS_SERVICE }}
             cluster: ${{ env.ECS_CLUSTER }}
             wait-for-service-stability: true
    
  3. Set up GitHub

    Create a new repo in Github to host the code, and set it up to allow Github Actions to deploy to ECS by configuring the secret keys for the IAM user:

    Your-Repo>Settings>Environments>Select your env>Add environment secret

Step 5: Commit and Push Changes

Initialize a git repository and push to GitHub

  1.   git init
      git remote add origin <your-github-repo-url>
      git add .
      git commit -m "Initial commit"
      git push -u origin main
    

This will trigger the GitHub Actions workflow, which will build the Docker image, push it to ECR, and deploy it to ECS.

Congratulations! You have successfully set up a CI/CD pipeline to deploy a Dockerised web application to AWS ECS using GitHub Actions.

Test the workflow by committing a change to the index.js in the line:

res.send('Hello World!');

Then push the change to Github, this should trigger the workflow to deploy the new change to the docker image and create a task based on this new image.

Once the workflow is finished, browse to ECS on the management console and find the IP address of the newly launched task to test it on the browser.