Set up CI/CD for containerized React App using Docker, AWS CodeBuild, AWS ECS, AWS CodePipeline & Github
Mubbashir Mustafa
Posted on May 7, 2020
This is the last part of the series "Dev-ops for Front-End developers". I assume you already have:
-Containerized your React Application using Docker
-Deployed on AWS ECS using Fargate
-Attached ELB and domain with the Container
-Attached SSL to ELB & Enabled HTTPS
-Setup Github repo for your project and pushed your code to it
1. Setting up CodeBuild Project
From the AWS console, navigate to CodeBuild. From the CodeBuild's homepage, select "Create project".
A CodeBuild project has 6-7 parts (at the time of writing):
Project Configuration
Enter in the name (required), description (optional) and tags (optional). Click on "Enable build badge" if you want to show build pass/fail badge on your Github repo page.
Source
Select Github, select "Repository in my Github Account", click on "Connect using OAuth (you can also use access token method if you prefer)", then click on "Connect to GitHub". It will ask you to sign in and authorize, if you intend to authorize repositories from your organization then you will also have to grant access to the organization. Afterward, it will ask you to enter your Github password.
After entering the Github password, it will take you to the CodeBuild page and from there select "Confirm".
Once authorized, you will be taken back to the CodeBuild. Search and select your repo ("my-app" in my case), then enter the branch name in the Source version field (the branch from which you would like to build e.g. master in my case).
Primary source webhook events
Leave it unchecked as we will be triggering build using code pipeline.
Environment
Select:
-"Managed Image" as an Environment image
-"Ubuntu" as an operating system
-"Standard" as a Runtime
-The latest version in Image dropdown ("aws/codebuild/standard:4.0" is the latest at the time of writing)
-"Always use the latest image for this runtime version" as Image version
-"Linux" as Environment type
-Enable the "Privileged" flag
-"New Service Role" as Service role (it will fill in the next field automatically, you can edit if you prefer some custom name)
-Leave Additional configuration as it is (unless you need to increase compute capacity etc.)
Buildspec
Enter "Buildspec.prod.yml" in the Buildspec name field (we will create this file later on).
Artifacts & Logs
Leave these sections and click on "Create build project"
Create and push Buildspec file
Create a new file in your project (react's app) root dir and name it "Buildspec.prod.yml" and paste the following snippet into it.
version: 0.2
phases:
install:
runtime-versions:
docker: 19
pre_build:
commands:
- $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)
- REPOSITORY_URI=681373743177.dkr.ecr.us-east-2.amazonaws.com/my-app
- COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
- IMAGE_TAG=${COMMIT_HASH:=latest}
build:
commands:
- docker build -t $REPOSITORY_URI:latest -f Dockerfile.prod .
- docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
post_build:
commands:
- docker push $REPOSITORY_URI:latest
- docker push $REPOSITORY_URI:$IMAGE_TAG
- printf '[{"name":"my-app-default-container","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
files: imagedefinitions.json
Replace the container name ("my-app-default-container") with the one that you used while creating Task Definition in earlier articles. Replace YOUR_ECR_IMAGE_URI_HERE
with URI of your image, which you can get from AWS ECR.
Save the file, commit, and push to your Github repo.
Note*: Make sure you are providing your Dockerfile name at "-f Dockerfile.prod" in the snippet above.
Give Access to CodeBuild
ECR Permissions
Now you need to give AWS CodeBuild access to your AWS ECR repo. To do that, go back to ECR and click on your repo. Inside your repo, click on "Permissions" from the left sidebar.
Click "Edit Policy JSON" and add the following JSON to the popup and click Save.
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "new statement",
"Effect": "Allow",
"Principal": {
"Service": "codebuild.amazonaws.com"
},
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
]
}
]
}
CodeBuild Role Policies
From AWS console go to IAM and select "Policies" from the left sidebar. Inside the Policies' page click on "Create policy".
Select JSON, enter the following snippet and click "Review Policy".
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ecr:CompleteLayerUpload",
"ecr:GetAuthorizationToken",
"ecr:UploadLayerPart",
"ecr:InitiateLayerUpload",
"ecr:BatchCheckLayerAvailability",
"ecr:PutImage"
],
"Resource": "*"
}
]
}
Name your policy "CodebuildToECR", give a description if you want, and click "Create policy".
Once the policy has been created, it's time to add the policy to the CodeBuild's service role (created earlier). For that select "Roles" from the left sidebar.
Search and select the CodeBuild role that was created earlier.
Search for the policy we created earlier (i.e CodebuildToECR), select it and click "Attach Policy".
Now we are ready to build our project using CodeBuild. But we still need to automate the CodeBuild and Deploy to ECS steps, so follow along.
2. Setting up CodePipeline
From the AWS console home, go to CodePipeline. Click "Create pipeline".
Enter the pipeline name and click "Next".
Just like before select "Github" as the source provider, use OAuth to grant access, select and search the repo (my-repo in my case) and enter the branch name (master in my case). Click "Next".
Select "AWS CodeBuild" as the Build provider. Search and select the CodeBuild project we created earlier and click "Next".
Select "Amazon ECS" as the Deploy provider. Select cluster and service we created earlier (in previous articles) and click "Next". Review the configuration and click "Create pipeline".
That's it. After creating the pipeline it will start to build & deploy automatically (the first time). Now, whenever you push to the master branch (or branch you provided earlier) pipeline will be triggered automatically.
You can set up notification rules using AWS Chatbot with Slack (that's what we use) or use SNS with any other service you prefer. That I will cover in some other series.
Technically the part we have done is CD only. CI comes into play when we want to merge branches/PRs etc, and that requires you to write test cases that are executed before merging.
To implement CI (Continous Integration), we will use Github Workflows. Create a folder in your app's root dir. and name it ".github". Inside this folder create a sub-folder and name it "workflows". Inside that folder create a new file called "react.yml". You can also use the following commands to achieve that.
mkdir .github
cd .github
mkdir workflows
touch react.yml
Open up "react.yml" with a text editor and paste the following snippet:
name: React CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: yarn
- run: yarn test
Save the file. Commit the changes and push to your Github repo. That's it. Now, whenever you make changes to your code and create new PRs it will run test cases automatically. You can check your workflow by going to the "Actions" tab on Github within your repo.
Further reading: https://www.atlassian.com/continuous-delivery/principles/continuous-integration-vs-delivery-vs-deployment
Posted on May 7, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.