Deploy Node.js to Amazon EC2 and ECR Automatically with GitHub Actions

Deploy Node.js to Amazon EC2 and ECR Automatically with GitHub Actions

ยท

9 min read

In this blog we will learn how to create a node application, run it using docker, push the image to AWS ECR and deploy to AWS EC2 instance using the latest image from ECR, automatically using GitHub Actions.

First we will learn few insights about the services being used.

What is Docker?

Docker is an open source platform for developing, building and running container applications. Docker enables us to separate our applications from our infrastructure so we can deliver software quickly.

What is AWS ECR?

AWS ECR is a fully managed docker container registry service from Amazon Web Service (AWS) that allows developers to store share and deploy container images.

What is GitHub Actions?

GitHub Actions is a Continuous integration and Continuous delivery (CI/CD) platform. In this platform we can automate our build, test and deployment pipeline, also we can create workflows which we can request to run on given conditions like push or pull in given branches.

Pre-requisites for creating your first node application.

  • nodejs

  • npm

Let us get started with the steps to install the above packages:

1. Install nodejs

This is the first step. Go ahead and download and install it. This will give you the tools needed to run a server on your local machine.

https://nodejs.org/en/download/package-manager

2. Open your terminal and type the below. This creates your working directory and go inside the directory.

mkdir nodeapp
cd nodeapp

3. Initialize your project using npm

npm init

This command will give you multiple prompts, just ENTER by giving the default value. Also this creates a package.json file inside your nodeapp folder. This contains all the npm packages you have downloaded for your project.

4. Install Express

npm install express --save

Express is a web framework for nodejs. The above command will find the packages and install it inside your project. The --save will save the packages to the dependencies list which is located inside package.json.

5. Open your editor and create a file named index.js

var express = require('express');
var app = express();
app.get('/', function (req, res) {
  res.send('Hello World!');
});
app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

This starts the server and listens on port 3000, Save this file.

6. Run the application

node index.js

After running the command, go to your browser and hit http://localhost:3000 to check the output.

We have successfully created our first Node app.

Let us dive into the next step which is running our application using docker.

Pre-requisites for Dockerizing your application.

  • Docker

  • AWS ECR

Getting started with installing docker and creating repository in AWS ECR.

1. Install Docker

https://docs.docker.com/engine/install/

Install docker based on your operating system.

2. Login to AWS

  • Login to AWS

  • Search for ECR

  • Click on get started

  • You can select private or public repository

  • Give a name to your repository

  • Click on create.

3. Create a Dockerfile in your working directory.

You can either use your terminal to write the dockerfile or open your code in VS code and write it from there. Below is the dockerfile for the node project which we had created in earlier steps.

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]

4. Now we need to build the Dockerfile above. This will create a Docker image, which we will then push to AWS ECR. We will perform this using Github actions.

5. Creating GitHub Actions file.

We will create a github actions file and automate the above process of building and pushing a docker image to AWS ECR.

6. Before creating the actions file, let us push our code which is in local to remote repository.

  • First create a new public repository in your github account. It will show the commands to get your code from local to remote.

  • Next go to your terminal where your code resides and run all the below commands.

  • Initialize the folder using git init command.

  • Add the code using git add . command. This will add all the files.

  • Commit using git commit -m "adding node files" command.

  • Add your remote repo using git remote add origin https://github.com/username/repo.git command.

  • Push your code using git push -u origin main command.

  • Refresh your repository to see the pushed code.

7. Go to your Github repository, click on the actions tab, select setup a workflow yourself.

8. Name the file, paste the YAML file below, and commit the changes to your GitHub repository.

name: actions-nodejs

on:
  push:
    branches:
      - main

jobs:
  Build&Push:
    runs-on: ubuntu-latest

    steps:
      - name: checkout the code
        uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-south-1

      - name: Log in to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build, tag, and push Docker image to Amazon ECR
        env:
          REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          REPOSITORY: demo-app
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $REGISTRY/$REPOSITORY:$IMAGE_TAG .
          docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG

Explanation:

  • Checkout - This step checks out your code under the $GITHUB_WORKSPACE so that your workflow can access the same.

  • Configure AWS credentials - We need to configure AWS credentials which allows github actions to authenticate with your AWS account and perform tasks.

  • Login to AWS ECR - After configuring credentials, we will login into AWS ECR account.

  • Build, tag and push Docker image to AWS ECR - Based on above AWS login step, we are fetching the ECR registry. We are passing the ECR repo name configured in the secret key and then dynamically building and pushing the docker image to AWS with the tag which is also generated dynamically.

9. Before committing the file, open your repo in a new tab and add the secrets for the above YAML file. Go to the settings of your repository. On the left-hand side at the bottom, you will see "Secrets and variables." Click on "Actions," then click on the "Add secret" button to add your secrets. In our case, we are adding AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.

10. Commit the above actions file and go and check your actions tab, a job would have started and it is building the docker image and pushing it to ECR.

11. Once the job is executed, check your ECR repo to see the latest image pushed.

12. Now whenever the code is pushed into the main branch, docker image will be build and would be automatically pushed to ECR using the configured github actions.

We have successfully executed CI/CD using github actions to build and push docker image to AWS ECR.

Now the final stage is remaining where we deploy the code into AWS EC2.

Pre-requisites for deploying nodejs into AWS EC2

  • ECR login

Login to your EC2 instance via terminal and authenticate to ECR using the below command.

aws ecr get-login-password --region <AWS_REGION> | docker login --username AWS --password-stdin <ECR Repo URI>

You will get a Login Succeeded message.

1. We need to update the actions file for further deploying the code to AWS EC2.

2. Open the actions yaml file in edit mode in your repository, and paste the below code.

      - name: Pull the docker image
        env:
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker pull 976566383149.dkr.ecr.ap-south-1.amazonaws.com/demo-app:${{ github.sha }}

      - name: ssh into instance
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USER }}
          key: ${{ secrets.kEY }}
          envs: |
            AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }}
            AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }}
            AWS_REGION=ap-south-1
          script: |
            docker stop mynodeapp || true
            docker rm mynodeapp || true
            docker run -d --name mynodeapp -p 3000:3000 976566383149.dkr.ecr.ap-south-1.amazonaws.com/demo-app:${{ github.sha }}

Explanation:

  • Pull the docker image - We are pulling the image from AWS ECR using the docker pull command, using the ECR registry / repo name : tag. This will pull the image from ECR.

  • SSH into instance - SSH using a github actions, where we provide host, username and private key of our EC2 instance along with the access and secret access keys. Later we are running a script which includes stopping the existing container, removing it and running the new container in port 3000.

  • Before committing the code, set the values of host, username and private key into the secrets.

Git secrets settings

Compiled workflows with all the stages

name: actions-nodejs

on:
  push:
    branches:
      - main

jobs:
  pull:
    runs-on: ubuntu-latest

    steps:
      - name: checkout the code
        uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-south-1

      - name: Log in to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build, tag, and push Docker image to Amazon ECR
        env:
          REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          REPOSITORY: demo-app
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $REGISTRY/$REPOSITORY:$IMAGE_TAG .
          docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG

      - name: Pull the docker image
        env:
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker pull 976566383149.dkr.ecr.ap-south-1.amazonaws.com/demo-app:${{ github.sha }}

      - name: ssh into instance
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USER }}
          key: ${{ secrets.kEY }}
          envs: |
            AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }}
            AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }}
            AWS_REGION=ap-south-1
          script: |
            docker stop mynodeapp || true
            docker rm mynodeapp || true
            docker run -d --name mynodeapp -p 3000:3000 976566383149.dkr.ecr.ap-south-1.amazonaws.com/demo-app:${{ github.sha }}

Complete execution of stages inside the github actions.

3. Once all the steps is executed copy your ec2-public-ip, open a new browser and hit the public-ip with port 3000.

4. You can make changes to your code and see the CI/CD process in action. Your code will be automated using GitHub Actions, and the changes will be visible in your browser.

Summary:

1. This blog is created from scratch as how to build a node project and running the application locally.

2. Once the application is running locally, create a Dockerfile. Write all the commands needed, then build and run this Dockerfile locally to see the application in action.

3. Install docker and create ECR repository so that we can push our docker image.

4. Push code to the main branch to trigger the workflow.

5. Checkout the code and configure AWS credentials.

6. Login to ECR, build, tag and push image to AWS ECR.

7. Last and final stage of pulling the image from ECR, stopping the previous container and removing it.

8. Running the container using the latest image pulled from ECR.

Note: The whole CI/CD was not straightforward as it looks. It took me multiple attempts to get this done. Attaching scrrenshots of those failed attempts here. But its all worth when finally the pipeline works.

Thus we have learned to " Deploy a simple nodejs application on AWS EC2 and ECR using github actions."

I hope this article has helped you in your work and learnings. If it did, please drop a like ๐Ÿ‘๐Ÿผ.

Thanks for reading!

Follow Bala for more such posts ๐Ÿ’ฏ๐Ÿš€

ย