Enabling CI/CD for microservices using AWS App Runner, Github, AWS CodePipeline, and Terraform

Aritra Nag
Playground Tech
Published in
9 min readApr 18, 2023

--

Introduction

In one of our previous posts, we discussed enabling DevOps capabilities for hosting a single-page application using AWS Managed Services and Terraform. Moving from the front-end application, we will showcase the backend use case by hosting a microservice using AWS App Runner.

One of the most used AWS Managed Services for hosting any dockerized container is AWS Elastic Container Service(ECS). This service provides many customizable techniques like autoscaling, service mesh, and batch services. Also, on the hosting part of AWS ECS, there are options to be using compute services like AWS EC2 as well as offload them by using AWS Fargate.

This blog showcases the usage of AWS App Runner which is a much simpler version of AWS ECS + AWS ALB(Application Load Balancer)/API Gateway constructs of hosting and exposing via HTTP endpoint. We have built the whole infrastructure(IaC) using Terraform which can be reused and the end-to-end pipeline can be constructed in any AWS account.

Managed Service and Application Design

AWS App Runner

AWS App Runner is a fully managed service provided by Amazon Web Services that makes it easier to build and deploy containerized applications in the cloud. Introduced in 2021, developers can quickly deploy their applications without worrying about infrastructure or container orchestration. The service automatically builds and deploys container images from source code or pre-built images from popular registries, and scales the application based on traffic. It also provides built-in security features and integrates with other AWS services such as Amazon S3, Amazon RDS, and AWS CloudFormation for easy management and configuration. Overall, App Runner simplifies the process of deploying and scaling containerized applications in the cloud.

Endpoints

Applications that can be dockerized and uploaded into an image registry are always fantastic contenders to be used for hosting them serverless using AWS ECS Fargate or AWS App Runner. In this example, we’ll discuss the endpoints for a Spring Boot Microservices API. Here are the endpoints we’ll be present in the service:

Health

  • /health - This endpoint is a health check API for AWS App Runner to validate that the service is up and running internally.

Authentication

  • /auth/signup - POST request to create a new user account.
  • /auth/login - POST request to log in as a user and receive the authentication JWT token created using HS256, which stands for HMAC-SHA256. It is a cryptographic algorithm that uses a shared secret key to both signs and verifies the message's integrity.

Categories

Requests below require an Authorization Header with a Bearer JWT token.

  • /categories - GET request to list all categories.
  • /categories - POST request to add a new category with a given name.
  • /categories/:id - GET request to find a category with the given ID.
  • /categories/:id - PUT request to update a category with the given ID.
  • /categories/:id - DELETE request to delete a category with the given ID.
  • /categories/:id/products - GET request to list all products for the given category ID.

Products

Requests below require an Authorization Header with a Bearer JWT token.

  • /products - POST request to add a new product with a given name, price, and category ID.
  • /products/:id - GET request to find a product with the given ID.
  • /products/:id - PUT request to update a product with the given ID.
  • /products/:id - DELETE request to delete a product with the given ID.

These endpoints provide the necessary functionality to perform CRUD (Create, Read, Update, Delete) operations on categories and products, as well as authentication endpoints for user management. All the CRUD information is persisted in the AWS-managed service RDS with PostgreSQL DB.

Repository

For the demo purpose, we will be using GitHub repositories and which will be connected to AWS Github Connector.

To simplify the process, we have preconfigured the application code and infrastructure code in a single repository for this demo, which will be connected to AWS GitHub Connector.

However, for better organization and maintenance, we recommend creating a separate GitHub repository for the application code and connecting it to the AWS GitHub Connector. This can be done manually through both the AWS Console and Github Console. The steps to do this are outlined in the later part of the blog.

Database Configurations

For dynamically injecting the credentials while spinning up the terraform code for the infrastructure as well as the application code, We will be using the following format to configure the credentials which will be produced for AWS-managed database service RDS with PostgreSQL. These credentials will be then securely stored in the AWS Secrets Manager service and fetched while deploying the service in AWS App Runner.

This approach helps us to securely store the databases outside of the application code and repository as well as gives us the capability to dynamically change them without changing the source code.

Infrastructure

Once we create the GitHub repository for the code of the application. This repository becomes the single source of truth for all the application-level changes and adopts the concepts of DevOps around it. Apart from creating the repository and pushing the code-level changes, we need to also create a GitHub AWS Connection to complete an seamless integration between the platform.

We will explain the terraform code for creating this connection in the later part of the blog.

Manual Steps

Here are some of the manual steps which we need to complete for enabling the complete DevOps CI/CD setup

  1. Once we select the connection and “Complete Handshake”, it will redirect us to configure the previously mentioned AWS Github Connector for the account and repository.

2. We also need to complete the Handshake between AWS CodePipeline and GitHub as well. Any changes in the repository will also trigger an AWS CodePipeline to deploy them in AWS App Runner. We complete this configuration by going to the Developer Tools and Settings tab of the UI console.

Post completion of the above steps, we can pull the code from GitHub and host the application using AWS App Runner.

DevOps: CI/CD Setup

AWS CodePipeline

In the infrastructure source code, we will showcase the end-to-end setup for the application code. We also configure the AWS CodePipeline to push any changes made in the GitHub repository. This pipeline will build the changes and push them to the AWS ECR repository which will create a new image for uploading the changes in the AWS App Runner. Here is the Gist of the Terraform code to implement the above pipeline:

We need to have the AWS ECR Repository and GitHub Repository details in the above TF code to set up the pipeline. Once the above code is executed, the AWS Code pipeline is setup as follows:

Once the above implementation is completed, we will see a Pipeline created with multiple stages( Source, Build).

AWS CodeBuild

In the following code snippet, we will describe the stage: Build which is added to the pipeline. Here is an example of the AWS CodeBuild terraform code where we fetch the commands of the build from the buildspec.yml file.

We will configure the terraform code to use the below buildspec.yml file as part of the AWS CodeBuild project.

In the above code, AWS CodeBuild is fetching the latest source code and pushing them to the AWS ECR Repository which is configured while doing the first-time setup.

AWS App Runner

For setting up the AWS App Runner using IaaC(Terraform), we use the below code snippet which enables us to fetch the uploaded docker image AWS ECR repository and deploy them.

Please note the dynamic injection of the PostgreSQL AWS RDS credentials which are created as part of the infrastructure code and used while spinning up the application. In the source code, we avoid any manual sensitive secrets and only keep the placeholders as displayed in one of the above sections.

Another important feature of AWS AppRunner is the automated deployment based on the new image that will be uploaded to the AWS ECR repository.

Initial Deployment

Some of the other important configurations which are needed to set up the e2e pipeline for the first time are to build and create a docker image of the source code and upload them to the AWS ECR repository. We use the “null_resource” provisioner terraform code to complete this exercise.

Some of the other configurations which are needed to host this infrastructure and solution construct are present in this repository.

Once the above changes are incorporated and Terraform commands are applied. We can see the DevOps setup is completed with AWS CodePipeline fetching the latest changes and uploading it as a new docker image in AWS ECR which in turn pushes the latest changes to AWS AppRunner.

To showcase the pipeline getting triggered and new images getting uploaded, we will make a change in the GitHub Repository and validate the changes in AWS App Runner hosted URL. In the following snippet, we are creating a new endpoint and uploading it to the AWS App Runner.

Once the code is pushed to the GitHub repository, we will immediately see the AWS CodePipeline getting triggered to create a new image for uploading to the AWS ECR repository.

To validate it is the same commit, we can also check the GitHub repo and also verify the Commit ID. Finally, AWS App Runner also gets triggered as part of the above exercise and starts to push the new changes.

Once the status of AWS App Runner changes from “Operation in progress” to “Running”. It will start reflecting the new changes.

Optionally, we can also add notifications in the AWS CodePipeline, and AWS App Runner state changes and send them to relevant stakeholders using AWS EventBridge. Here is one of our previous posts which showcases how to enable the notifications on the CI/CD pipelines.

Conclusion

Finally, we concluded the demo and blog using AWS App Runner, Github, AWS CodePipeline, and Terraform, we have successfully enabled a CI/CD pipeline for hosting a microservice in the cloud.

AWS App Runner provided us with a simplified version of hosting and exposing HTTP endpoints for the microservice, while Github acted as the single source of truth for application-level changes.

Terraform was used to build the infrastructure as code (IaC), and AWS CodePipeline was used to push any changes made in the GitHub repository to the AWS ECR repository, which would create a new image and deploy the microservice in AWS App Runner.

Overall, this blog showcased an end-to-end DevOps setup for hosting and scaling a containerized application in AWS App Runner, simplifying the process of deploying and scaling containerized applications in the cloud.

We can further optimize by dynamically injecting some of the configurations within the AWS CodePipeline using continuous configuration services like AWS AppConfig, and AWS SSM Parameter Store. Here is another blog that discusses AWS AppConfig Capabilities and its advantages in the DevOps system

References

  1. https://github.com/apps/aws-connector-for-github
  2. https://www.apprunnerworkshop.com/
  3. https://github.com/aws-samples/aws-apprunner-terraform

--

--