How to setup a continuous deployment from Bitbucket Pipeline to Amazon ECS

Thomas Strohmeier
byte\schneiderei
Published in
4 min readMar 19, 2018

--

For one of my recent projects I had to setup a continuous deployment workflow for a web app, consisting of a dozen microservices. We wanted to use docker to build a image for each service. I already described in my previous article how you can build and push a docker image to Amazon ECR through a Bitbucket Pipeline. You can find this article here:

In this article I want to describe the following points:

  • How to create a new revision of an task definition in a Bitbucket Pipline
  • How to update a service of a ECS cluster to this new revision

My Solution

How does the general workflow looks like?

Workflow of a deployment

1. Commit a new version

I would recommend to use consistent versions for your images, task definitions and services. The best way is to specify a version number in your pipeline or you read it from a file in your git repository. In this example we export an environment variable VERSION_NUMBER and for the target deployment stage the environment variable DEPLOYMENT_ENV. This two combined result in the image name, service name, etc.

In our example we will deploy version 1.0.0 of our microservice, to the “test” stage of the cluster.

# set version and build id
— export VERSION_NUMBER=1.0.0
— export DEPLOYMENT_ENV=test

2. Build & push the Docker image to the Amazon ECR

Please have a look here how to do this:

3. Create a new revision of your task definition

First I created a JSON-File containing the container definition of our task definition. This could look like this:

[
{
"name": "mytestapp-webgui",
"image": "xxx.dkr.ecr.eu-central-1.amazonaws.com/mytestapp-webgui:<IMGAGE_VERSION>",
"cpu": 0,
"memory": 500,
"memoryReservation": 75,
"portMappings": [],
"essential": true,
"entryPoint": [],
"command": [],
"environment": [
{
"name": "ENV_DEBUG",
"value": "false"
}
],
"mountPoints": [],
"volumesFrom": [],
"workingDirectory": "/usr/share/nginx/html/",
"disableNetworking": false,
"privileged": true,
"readonlyRootFilesystem": false,
"dnsServers": [],
"dnsSearchDomains": [],
"dockerSecurityOptions": [],
"logConfiguration": {
"logDriver": "json-file"
}
}
]

I saved the file with the name mytestapp-webgui.container-definition.json

To create a new revision we will use the “aws ecs register-task-definition” command. We have to specify the task family and pass the content of the JSON file. The command could look like this:

aws ecs register-task-definition --family mytestapp-webgui —- container-definitions <json-file>

This command is included in the “deploy-ecs.sh” script below. I added some clue code in that script.

From the output of the “aws ecs register-task-definition” command we parse the revision number and store it in a variable. If this step was successfully we proceed with the next one.

4. Update the ECS cluster service

If we managed to create a new revision of our task definition, we can update our service. Therefore we use the “aws ecs update-service” command. We have to specify the cluster name, the service and the task definition incl. the revision. The command could look like this:

aws ecs update-service --cluster mytestapp-test-cluster --service mytestapp-test-webgui-service --task-definition mytestapp-webgui:1

Complete solution:

According to the documentation of Pipelines (Link), you can specify in each step one of the following deployment steps:

  • test
  • staging
  • productive

If you don’t want to use this Pipelines feature, you are free to use any other name too.

Like I mentioned in my older post you have to allow all the needed action to your AWS IAM deploy user to be able to deploy your service. A list from my user:

"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:GetRepositoryPolicy",
"ecr:DescribeRepositories",
"ecr:ListImages",
"ecr:DescribeImages",
"ecr:BatchGetImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:PutImage",
"ecs:DeregisterTaskDefinition",
"ecs:RegisterTaskDefinition",
"ecs:UpdateService"

For deployment I wrote the “deploy-ecs.sh” script. The script will create a new revision of our task definition for you and update your service. You only have to specify following parameters:

  • the name of your app (“mytestapp” in our case) [-b]
  • the service you want to deploy (“webgui” in our case) [-s]
  • the deployment environment (“test” in our case) [-e]
  • the image name (“test_1.0.0” in our case) [-i]

PS: If you have an idea how to improve this setup, I would be happy If you leave a comment ;)

--

--

Entrepreneur | Student @ TU Graz | Software Engineer | DevOps | love learning & teaching | business software | privacy | https://byteschneiderei.com