Skip to main content

Introduction

It wouldn’t be an exaggeration to say that containers brought a massive change to the industry, where scaling applications moved from scaling servers, to scaling individual container replicas within servers. With the capacity to ‘limitlessly’ scale workloads & replicas on servers, finding the right balance between availability and cost became a challenge. Orchestration (management, scaling and deployment of containers to servers) became a key concern and to remedy this, AWS introduced Elastic Container Service(ECS).

Orchestrating containers with ECS became a deciding factor for many, to migrate to AWS. ECS, whether it be Fargate, or ECS on EC2, provides a scalable solution to container management. Fargate became a great way to take advantage of a serverless approach to containers, whilst for workloads that required hosts/nodes, ECS on EC2 filled the void.     

On the deployment front, AWS provided multiple methods for updating and scaling your containers. While AWS CLI allowed for new TaskDefinitions to be created, replacing running Containers instances, the ECS + CLI alone did not allow for state management, or the ability to rollback without creating your own scripts. This toolset was also limited to  Rolling Deployments, not permitting for any other advanced deployment methodologiesTo improve the developer experience AWS introduced CodeDeploy.

The addition of CodeDeploy gives developers and engineers alike a means of deployment outside the usual RollingUpdates. CodeDeploy + ECS introduces the inclusion of Linear and Canary deployments. By using multiple Target Groups with CodeDeploy, ECS deployments can be seamless to the client facing side.

Linear & Canary Deployments

As a quick summary, Linear deployments perform incremental releases via shifting between the old and new deployments, these are determined by either AWS defaults, such as ECSLinear10PercentEvery{1/3/5}Minutes or by creating your own percentage and time gap. These incremental transitions will be monitored by health checks set on your ECS deployments, and on any failure and will automatically roll back to the previous deployment.

Canary provides a similar approach to deployment however, it will transition all remaining traffic after a set period of time, say for example with ECSCanary10Percent5Minutes after 5 minutes, as long as all health checks pass, the remaining 90% traffic will shift to the new deployment.

 

The how and why: CDK + ECS + CodeDeploy

How: CDK

CDK builds on the foundations of CloudFormation. Pick your language of the 6(including Python/Typescript/Go. Read more here) and build. How does CDK enhance our ECS deployments? By the use of custom patterns/constructs.

As an advanced feature of CDK, repeatable patterns can be used as Custom Constructs/Patterns, these custom constructs combine several AWS resources to create a focused solution, in our case we combined AWS resources to create a simple to use, Blue-Green CodeDeploy construct. Take for example the below:

Here we have created a single CDK Construct of our own called CreateDeploymentGroupCustomResource that will create:

  • CodeDeploy Group
  • Pass through our required Targets Groups (named Blue/Green in our case)
  • Set what type of deployment were running( Linear10PercentEvery1Minute in example above)
  • A Custom Resource which ties together, CodeDeploy, CodeDeploy App and Groups to our ECS cluster (this is not available in standard CloudFormation or CDK at time of writing this)
  • Lambda function handling Custom Resource calls
  • Much more..

Effectively this Custom Construct, shrinks the usual several hundred lines of JSON/YAML into something much more digestible. The need to copy paste templates across multiple repos or environments is reduced.

How: CodeDeploy

To trigger CodeDeploy deployments we required an appspec.yml and taskdef.json file therefore our directory structure for this deployment is looks like:

Gitlab CI is our pipeline of choice here. Inside of the .gitlab-ci.yml file we have a build, test, push and deploy stages. Take note of the below:

With CodeDeploy when deploying to ECS, the AWS CLI is used to invoke a deployment. With the aws ecs deploy command we pass in the `appspec.yml` and the expected `taskdef.json’. The AppSpec file is used to defines  each deployment as a series of lifecycle event hooks, which are defined in the file and a task definition which describes the container and volume definitions of an Amazon Elastic Container Service task. You can specify which Docker images to use, the required resources, and other configurations related to launching the task definition through an Amazon ECS service or task.

Note example of command below:

As our focus here is ECS + CodeDeploy, as a point of note, the version tag for the container image is dynamically updated on the `taskdef.json` file after a successful push to ECR. To breakdown the option choice above:

`–task-defnition` Will generate a new TaskDef in AWS ECS based on the taskdef.json filed passed through
`–service` the ECS service expecting the deployment
`–codeDeploy-appspec` The appsec that will determine the type of deployment specifics such as Fargate version are passed, reference link
`–cluster` Cluster of choice
`–codedeploy-application/deployment-group` The app and deployment group created in the customer resource

 

Why?

The ‘how’ gave an overview of what we were doing but the ‘why’ really seals the argument here for ECS + CDK is the why. An easy to use, repeatable pattern was made that can be used across multiple projects, the pattern was shipped to a package repo (in this case NPM but this could as easily be built in Python and send to pip), and rather than copying a whole template to do a BlueGreen or Linear or Canary Deployment, we instead use an import and 13-14 lines of code.

Our use case was to create a means for ECS and Fargate microservice deployments:

  • Limit impact to developers/engineers workflows
  • Deploy with minimal downtime if any downtime
  • Provide a means of built-in roll back with a hands off approach
  • Provide a hands off approach to developers, to allow them to set deploy conditions without needing ops/DevOps involved.
  • Provide repeatable patterns that can be used across multiple projects.

CodeDeploy + ECS met the minimal downtime and rollback conditions. As CodeDeploy Linear and Canary deployments are based around traffic shifting, in case the new deployment fails, all the traffic will be shifted back to the old deployment.

CDK provided a hands off approach as everything was defined in infrastructure as code, and the nature of the design meant Custom Constructs fit the need of the repeatable patterns.

Overall this deployment approach was a Quality of Life improvement, a simplification of ways of working with deployment methods, and an example of how ECS + B/G can be done with minimal effort.