AWS CodePipeline for Amazon ECS, Part 1: A Rolling Updates Deployment Type
By: Ivan Polovyi
Tags: AWS, Continuous Delivery, Docker, Continuous Integration, Pipeline

AWS CodePipeline for Amazon ECS, Part 1: A Rolling Updates Deployment Type

At the beginning of this tutorial, we will create the ECS cluster and deploy an app on it. Then we will create the pipeline to build and deploy the app to this cluster. The ECS cluster will be simple, I won’t focus match on security and high availability. I have a separate tutorial where I explain how to create an ECS cluster from scratch step by step. So if you would like to learn more, please check here.

The diagram below explains how the AWS CodePipeline integrates with the AWS ECS with rolling updates deployment type.

Tutorial diagram

1. Create a load balancer

Go to EC2 -> Load Balancing -> Load Balancers -> Create Load Balancer. Select Application Load Balancer and press Create. Give it a name, and select default VPC and at least 2 subnets, and press Next.

Configure load balancer page

Then select Create a new security group and give it a name press Next.

Configure security groups page

Select New target group, give it a name, and press Next.

Configure route page

On the Register Targets step just press Next, as we will register targets on cluster creation later in this tutorial. After on the Review page verify all information and press Create. After a couple of minutes, the load balancer will be created.

2. Create an ECS cluster

Go to ECS -> Create cluster select EC2 Linux + Networking and press Next step.

The select cluster template page

Give a name for the cluster, select Provisioning model and EC2 Instance type. For this example, I will use 1 On-Demand t2.micro EC2 instance. Select SSH key pair if you want to have access to instances in the cluster. SSH is optional and if you want to know how to create and use the key please check another tutorial here.

Configure cluster page

In the networking section select default VPC and 1 private subnet. Make sure to select the same subnet as one of the subnets of the Load Balancer created in step 1 of this tutorial. Then select Create new security group, leave the rest of the configuration with default values and press Create.

Networking section of creating cluster page

3. Create task definition

Now we need to create a task definition. Go to ECS -> Clusters -> Task Definition -> Create new Task Definition, select EC2 type and press Next step.

Select launch type compatibility page

Give it a name and select defaults for Task Role and Task execution role.

Configure task and container definitions page

Define a Task memory and Task CPU. These two configurations depending on the requirements of the application running inside the container. Then press Add container.

Task size and container definitions sections

The new window will open. Give the name for the container, for the image past URI of your image. The image used for this tutorial is located in AWS ECR. How to upload an image to ECR you can check in my tutorial here. This image has a Node.js app that listens on port 8080 and displays its host/container name. The source code you can check here.

Set the memory limit. In Port, mappings fill only the Container port and omit the Host port (or set it to 0) and your container automatically receives a port in the ephemeral port range for your container instance. The rest of the configurations live as default and press Add.

Edit container page

4. Create service

On the cluster level on the Service tab press Create.

Cluster page

Select Launch type as EC2, select previously created task definition, fill-up service name, select service type as Replica, a number of tasks put 2.

Configure service page

Then select Deployment type as Rolling update, rest of the configuration leave as is, and press Next.

Deployments and Task placement section of Create service page

Select Load balancer type as the Application load balancer.

VPC and security group of configure network page

Then select default service role, load balancer, and target group, previously created in the step 1of this tutorial, and press Next.

Container to load balance section of configure network page

On the Set Auto Scaling step leave default configuration and press Next. Then on the Review page check, all configurations and press Create service

To check the tasks of this service go to Clusters -> Cluster-name ->Tasks tab.

List of tasks

5. Update security group of EC2 instances

By default the security group of the instances of the cluster isn’t accessible from a load balancer, so we have to update the security group of EC2 instances. Go to EC2 -> Network & Security -> Security Groups, find the Security group of a cluster. The name should look like this EC2ContainerService-<cluster-name>-EcsSecurityGroup-<RANDOM>, open it and press Edit inbound rules.

The security group of a cluster

Add the rule to allow all traffic from the security group of the load balancer and save it.

Edit inbound rules page

To check the app go to EC2 -> Load Balancing -> Load Balancers Open a load balancer of a cluster on a Description Tab copy a DNS name.

Load balancer description page

Then paste it into a browser and you will see the output.

The output of an app hosted on a cluster

6. Create a connection to the source code repository

The source code for this example you can find here. To connect your repository follow this simple tutorial.

Connection list page

7. Create an S3 bucket

The CodePipeline uses the S3 bucket to store a zip file containing files outputted by an action called artifacts. The CodePipeline wizard can create an S3 bucket for you, but I will show you how to create it outside of a wizard. Go to Amazon S3 and press Create bucket. Give it a name, rest of the configuration leave as is, and press Next.

Create an S3 bucket page

8. Create an IAM role

The CodePipeline needs 2 service roles — one for the CodePipeline and another for the CodeBuild. The CodePipeline wizard creates roles on our behalf and it is very convenient. The problem is that it will create roles with wider policies than your CodePipeline needs. It is always a good practice to create the role with only the necessary policies (permissions). For some reason, there is no way to create the role for the CodePipline outside of the CodePipeline wizard. We will create a role for the CodeBuild in IAM and let the CodePipeline wizard create one for the CodePipeline. I won't update this role afterward. But you should always check the role created by the wizard and make its policies more strict.

The first step is to create policies for a role. IAM -> Policies -> Create policy. And then past the JSON code below. In this code example, you should replace each of the Resource sections with your own values.

{

"Version": "2012-10-17",

"Statement": [

{

"Sid": "CloudWatch1",

"Effect": "Allow",

"Action": [

"logs:CreateLogGroup",

"logs:CreateLogStream",

"logs:PutLogEvents"

],

"Resource": [

"arn:aws:logs:us-east-1:708995052028:log-group:ecs-rolling-update-build",

"arn:aws:logs:us-east-1:708995052028:log-group:ecs-rolling-update-build:*",

"arn:aws:logs:us-east-1:708995052028:log-group:/aws/codebuild/ecs-rolling-update-build",

"arn:aws:logs:us-east-1:708995052028:log-group:/aws/codebuild/ecs-rolling-update-build:*"

]

},

{

"Sid": "ECR2",

"Effect": "Allow",

"Action": "ecr:GetAuthorizationToken",

"Resource": "*"

},

{

"Sid": "ECR3",

"Effect": "Allow",

"Action": [

"ecr:GetDownloadUrlForLayer",

"ecr:UploadLayerPart",

"ecr:PutImage",

"ecr:BatchGetImage",

"ecr:CompleteLayerUpload",

"ecr:InitiateLayerUpload",

"ecr:BatchCheckLayerAvailability"

],

"Resource": "arn:aws:ecr:us-east-1:708995052028:repository/aws-pipeline-with-ecs-rolling-update-deployment-image"

},

{

"Sid": "S34",

"Effect": "Allow",

"Action": [

"s3:GetBucketAcl",

"s3:GetBucketLocation",

"s3:PutObject",

"s3:GetObject",

"s3:GetObjectVersion"

],

"Resource": [

"arn:aws:s3:::pipelines-for-ecs*"

]

},

{

"Sid": "CodeBuild5",

"Effect": "Allow",

"Action": [

"codebuild:CreateReportGroup",

"codebuild:CreateReport",

"codebuild:UpdateReport",

"codebuild:BatchPutCodeCoverages",

"codebuild:BatchPutTestCases"

],

"Resource": [

"arn:aws:codebuild:us-east-1:708995052028:report-group/ecs-rolling-update-build-*"

]

}

]

}

Press Next: Tags. This is optional in case you want to add some tags. Then press Next: Review.

Create policy page

Give it a name and a description and press Create Policy.

Now we will create the service role for CodeBuild and attach created above policy.

Go to IAM -> Role -> Create role. Select type of trusted entity as AWS service, choose service name as CodeBuild and press Next: Permission.

Create role page

Filter policy you want to attach in my case it is a policy created below, and press Next: Tags.

Attach policy to a role page

Press Next: Tags. This is optional in case you want to add some tags. Then press Next: Review. On the next screen give a name and description for the role.

Create a role review page

9. Create CodePipeline

Finally, we will create the CodePipeline. Go to Developer tools -> CodePipeline -> Pipelines and press Create pipeline. Give a name for the pipeline, choose a new service role, and give it a name, select the S3 bucket created in step 7 of this tutorial, and press Next.

Choose pipeline settings page

Select source provider as GitHub, the connection created in step 6 of this tutorial, repository, and branch. Check flag Start the pipeline on a source code change and press Next.

Add source stage page

Select CodeBuild as a provider, region, and press on Create project.

Add build stage page

This will open another window in your browser. Give a name for the project and select environment configurations.

Create a build project page

Check the Privileged option because the CodeBuild in this pipeline will build a Docker image. Without this option, the build will fail. Select the Existing service role created in step 6 of this tutorial. If you use an option New service role the CodePipeline wizard will create a new one for you, but the build will fail because the role created by the wizard does not have policies to access the AWS ECR. So after pipeline creation, you will have to update this service role in the AWS IAM to include missing policies and then run the pipeline again.

In the Build specification check the option Use a buildspec file if the buildspec.yaml file is in the root directory of your source code.

The source code of my application contains the buildspec.yaml file. You just have to replace 4 environment variables with your values.

buildspec.yaml file

The last step is to create logs. It is optional. Give it a group name and press continue to CodePipeline.

Create logs for CodeBuild page

On the Add deploy stage add Deploy provider as Amazone ECS, fill the name of a cluster, the name of a service and press Next

Add deploy stage page

On the next page review stages and press Create CodePipeline. Then the created pipeline starts its execution.

CodePipelline execution page

Check the quick demo video showing the CodePipeline in action.

AWS CodePipeline quick tutorial video

Summary:

In this tutorial, I showed you the power of the AWS CodePipeline. The CodePipeline allows automatically without the manual interaction build, test, and deploy your code every time there is a code change. As you can see the AWS CodePipeline has very good integration with AWS ECS. A rolling deployment is generally faster so, in my opinion, it is good for development. For production, the better choice is a Blue/green type of deployment mainly because it is much easier to roll back if a deployment fails. In Part 2 I will show how to create AWS CodePipeline with a Blue/green type of deployment.

Thank you for reading! If you have any questions or suggestions, please feel free to write me on my LinkedIn account.

More content at plainenglish.io