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
Top comments (12)
Great. But I get this error when the code tries to build
YAML_FILE_ERROR Message: Unknown runtime named 'docker'. This build image has the following runtimes: dotnet, golang, java, nodejs, php, python, ruby
So it seems like in version 5 they have removed "Docker: 19" and from now onwards default docker runtime will be available only. To make this code work you can:
Release details: github.com/aws/aws-codebuild-docke...
Oh thanks! Would try this and see how it works.
Sweet. Please do share how it goes!
It worked, but having another issue, the build break when it tries to run this
$(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)
The error message is:
An error occurred (AccessDeniedException) when calling the GetAuthorizationToken operation: User: arn:aws:sts:::assumed-role/codebuild-main--build-service-role/AWSCodeBuild--5c2c-4dff-a514- is not authorized to perform: ecr:GetAuthorizationToken on resource: *
[Container] 2021/02/07 14:25:18 Command did not exit successfully $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email) exit status 255
You have probably figured it out already. But for newer folks. You have to replace:
$(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)
with:
"- aws ecr get-login-password | docker login --username AWS --password-stdin $YOUR-AWS-Repostitory-URI"
Example buildspec.yml file:
What have you selected as "Runtime" and "Image"?
I have just setup another pipeline. I made sure I followed your steps exactly as it is. And I still have the same error.
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=780571656781.dkr.ecr.us-east-2.amazonaws.com/spurts-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 .
- 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":"spurts-container","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
files: imagedefinitions.json
This is my Buildspec file
I removed the entire
install
section. Worked like a charm.I need to setup a staging environment for me and my team to test code before we ship to production. Is there a way to add this as a step in this current process or do I need to setup a separate pipeline with the staging branch as a source?
A separate environment with a separate pipeline.