Supreet Singh - May 21, 2023
In the second part of this series, we were looking at all the resources we need to get the application running on AWS. In this post, we'll look at setting up a CD pipeline with Github Actions and ECR.
Rails is really starting to embrace containerization these days and as a matter of fact, we'll be getting an official Dockerfile starting with Rails 7.1. For this post, we'll be using Github Actions to build and push our image to ECR and trigger automated deploys to ECS.
Github Actions is a CI/CD tool that comes with Github. It's free for public repositories and has a generous free tier for private repositories. I thought about using AWS CodePipeline but I wanted to keep things simple and avoid having to deal with another service in AWS. I like Github a lot more than I like AWS.
The intent here is simple:
main
branchLet's start by creating a new file in .github/workflows
called deploy.yml
and add the following:
on:
push:
branches: [main]
name: Deploy to Amazon ECS
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: arn:aws:iam::123xyzabc456:role/github-role
aws-region: us-east-2
- 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 }}
ECR_REPOSITORY: your-ecr-repo-name
IMAGE_TAG: ${{ github.sha }}
PORT: 3000
WEB_CONCURRENCY: 2
run: |
# Build a docker container and push it to ECR so that it can be deployed to ECS.
docker build . --file Dockerfile --tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG --tag $ECR_REGISTRY/$ECR_REPOSITORY:latest --build-arg PORT=$PORT --build-arg WEB_CONCURRENCY=$WEB_CONCURRENCY
docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
- name: Download task definition
id: download-task-definition-json
run: |
aws ecs describe-task-definition --task-definition your-task-definition-name --query taskDefinition > task-definition.json
- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: task-definition.json
service: your-service-name
cluster: your-cluster-name
Let's break this down a bit.
on:
push:
branches: [main]
This is the trigger for the workflow. We want to run this workflow when we push to the main
branch.
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
This is required for the actions/checkout
step. It's a bit confusing but it's required for the checkout step to work.
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
This is the job that will run when the workflow is triggered. We're calling it deploy
and it will run on ubuntu-latest
.
steps:
- name: Checkout
uses: actions/checkout@v3
This step checks out your repository content into the workflow's runner so that the workflow can access your code.
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: arn:aws:iam::123xyzabc456:role/github-role
aws-region: us-east-2
This step uses an AWS action to configure the AWS credentials that the workflow will use. You need to replace arn:aws:iam::123xyzabc456:role/github-role with the ARN of your IAM role, and us-east-2 with your AWS region.
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
This step logs in to your Amazon Elastic Container Registry (ECR) using another AWS action.
- name: Build, tag, and push image to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: your-ecr-repo-name
IMAGE_TAG: ${{ github.sha }}
PORT: 3000
WEB_CONCURRENCY: 2
run: |
# Build a docker container and push it to ECR so that it can be deployed to ECS.
docker build . --file Dockerfile --tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG --tag $ECR_REGISTRY/$ECR_REPOSITORY:latest --build-arg PORT=$PORT --build-arg WEB_CONCURRENCY=$WEB_CONCURRENCY
docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
This step builds a Docker image from your application code, tags it, and pushes the image to ECR. The image is tagged with both the commit SHA and latest.
- name: Download task definition
id: download-task-definition-json
run: |
aws ecs describe-task-definition --task-definition your-task-definition-name --query taskDefinition > task-definition.json
This step downloads the task definition JSON for your ECS service. You will need to replace your-task-definition-name with your actual ECS task definition name.
- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: task-definition.json
service: your-service-name
cluster: your-cluster-name
Finally, this step deploys the new Docker image to your ECS service. Replace your-service-name and your-cluster-name with the name of your ECS service and the name of your ECS cluster, respectively.
That's it! By using GitHub Actions, you can automate the build and deployment process of your application every time you push changes to the main branch.
Hopefully, this helps fill in some blanks for you. If you have any questions, reach out to me on Twitter @supreet321. I'm happy to help.