API with NestJS #95. CI/CD with Amazon ECS and GitHub Actions

AWS NestJS

This entry is part 95 of 121 in the API with NestJS

In the last two parts of this series, we’ve explained the details of manually deploying our NestJS application with Amazon Elastic Compute Cloud. It’s a great idea to go through the above process by hand to understand it fully. The next step is automating it by implementing Continuous Integration and Continuous Delivery. In this article, we explain each part of the above process and automate our deployments.

To follow this article, check out those parts first:

Integrating AWS with GitHub Actions

GitHub Actions is a CI/CD platform built into GitHub and allows us to automate our deployment. For example, it allows us to run a set of scripts every time someone creates a pull request or merges changes to a particular branch.

We need a way for the GitHub Actions platform to perform actions on AWS on our behalf. One way to do that is to allow GitHub to authenticate to our AWS user.

We probably defined some users in the Identity and Access Management (IAM) interface in AWS, and we could allow GitHub to log in to their accounts. Unfortunately, we need to make sure to give GitHub as few permissions as possible to increase security.

To do that, let’s go to the IAM dashboard and click on the “Add users” button.

Let’s use the “Attach policies directly” option and create a policy from scratch to maximize our control over the permissions. We want GitHub to be able to perform a few actions on our behalf:

  1. log in to ECR,
  2. build the Docker image and push it to the registry,
  3. update an existing ECS service.

To allow the above, when creating a policy, click on the “JSON” button and use the following JSON:

In the previous parts of this series we’ve worked with a public ECR repository, therefore we need to use the appropriate action with the suffix. We could take it a step further and replace with the exact actions GitHub needs to push the Docker image to ECR.

Now, we need to name our new policy and finalize it.

The last step is to use the above policy when creating our new IAM user.

Creating access keys for a user

Once we have our new user, we need to provide a way for GitHub actions to use it. We could achieve that by going to our new user in the IAM dashboard and creating access keys.

An access key consists of two parts: the access key and the secret access key. Make sure not to share them with anyone, though. Having both of them allows you to authenticate as a given user.

We need to open our GitHub repository and go to Settings -> Secrets and variables -> Actions. On this page, we need to add two repository secrets:

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY

Configuring GitHub Actions

A GitHub Actions workflow is an automated process that an event in our repository can trigger. A typical example of an event is pushing new changes to the branch. Every time that happens, we want a few things to take place:

  1. create a Docker image and push it to ECR,
  2. update an existing ECS service.

To configure GitHub actions, we need to create a YAML file in the directory.

.github/workflows/deploy.yml

Our workflow consists of two jobs: creating the Docker image and deploying it. It’s triggered every time we push new code to the branch.

Let’s take a closer look at the job.

Our job consists of four steps. Understanding what happens requires us to go through each step one by one.

Checking out the repository

For GitHub action to be able to access our code, we need to check out our repository. Fortunately, GitHub has an action we can use to check out the code for us.

Configuring the AWS credentials

The next step is to configure AWS credentials. This is why we had to put the and as secrets into our GitHub repository.

Please notice that I’m using above. Most AWS services are region-specific, but ECR public registry requires authentication in the region.

Logging into ECR

As soon as we have the credentials, we can log into ECR.

Building the Docker image and pushing it

The last step of this job is to build the docker image and push it to our ECR repository.

Above, we use the variable that comes from the previous step of this job. We also need to set up the correct registry alias that we can find on the page of our ECR registry in the AWS dashboard.

Deploying the new Docker image

The other job deploys the image created in the previous job.

Please notice that above we use , which is the location of my cluster. It depends on which AWS region we are using. We also add to make sure GitHub Actions wait for the previous job to be finished.

The most crucial part of the above code is using the to update the existing ECS service we’ve defined in one of the previous parts of this series. Please make sure to provide the correct details:

  • the name of your cluster with the argument,
  • name of your service using ,
  • the proper task definition with the argument.

We also use the flag to force a new deployment even when the service definition didn’t change. Thanks to the above, we deploy a new version of our service that uses the latest Docker image from our ECR repository.

This is the full content of our  file:

.github/workflows/deploy.yml

As soon as we push the above file to the  branch, we can go to the Actions page in our repository to inspect the logs of our workflow.

Doing that spawns a new task in our cluster that deploys our NestJS application. As soon as the task finishes, the latest version of our code is up.

Summary

In this article, we’ve learned how to use GitHub Actions with AWS ECS to set up a CI/CD pipeline. Whenever we push new changes to the branch, GitHub Actions creates a new Docker image and redeploys our ECS service. There is still more to learn when it comes to setting up CI/CD with GitHub Actions, so stay tuned!

Series Navigation<< API with NestJS #94. Deploying multiple instances on AWS with a load balancerAPI with NestJS #96. Running unit tests with CI/CD and GitHub Actions >>
Subscribe
Notify of
guest
1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Yacar
Yacar
1 year ago

Greetings, Thank you for this post. How can I keep only one fixed public IP. Since every time a new task is created, a new IP is assigned to it.

Thank you…