Ship it! with ecs-ship

Having a fast and reliable way to update your deployment environments is a game changer in terms of team velocity and confidence. ecs-ship is a simple yet powerful tool that can help your team ship faster. 10 minute read Introduction Most of our teams at NextRoll leverage the power of AWS ECS for container orchestration. […]

Having a fast and reliable way to update your deployment environments is a game changer in
terms of team velocity and confidence. ecs-ship is a simple yet powerful tool that can
help your team ship faster.

10 minute read


Introduction

Most of our teams at NextRoll leverage the power of AWS ECS for container
orchestration. Each team manages their AWS resources, providing them with
ultimate freedom when choosing technologies or what or when to deploy. In this article,
we want to share ecs-ship, a small tool the likes of ecs-deploy that
we wrote to solve some of our common workflows and will soon be open source (Update: it’s now opensource yay!!)

Some typical workflows

Let’s see by example which workflows you might want to use ecs-ship for by looking at
two typical tasks we can accomplish with it:

  • Deploying a new version of an application.
  • Changing the resource allocation of an application (aka right-sizing).

Deploying with ecs-ship

When deploying a new version of a containerized application to AWS ECS, there
are three resources you need to account for: container images, ECS task definitions, and
ECS services. Container images are a pretty common artifact these
days. They represent a “unit” containing your application and all the
dependencies it might need to run correctly. ECS Task definitions
represent a containerized workload; essentially, they let ECS know what’s required to run
your tasks. For example, I need three containers running my application image and 1 running
a database image
. You express these requirements via container definitions inside the task
definition. Finally, ECS services reference a version of a task
definition and are responsible for letting AWS know “we do need these containers running
now”.

In this framework, to deploy a new version of your application, you need to follow these
steps:

  • Create and push a new version of your application’s image to a registry.
  • Create a new version of your task definition that references the freshly uploaded image.
  • Finally, update your service to the newly created task definition.

Let’s go through them step by step.

Building and pushing your images to a registry would usually look like this:

export VERSION=`git rev-parse HEAD`
docker build -t your.registry.io/yourusername/yourapp:$VERSION .
docker push your.registry.io/yourusername/yourapp:$VERSION

Once you have your new image (or images) up on the registry and ready to use, ecs-ship
comes in to help you with the rest:

cat << EOF | ecs-ship your-cluster yourapp-service
containerDefinitions:
  yourapp-definition:
    image: your.registry.io/yourusername/yourapp:$VERSION
    environment:
      VERSION: $VERSION
      SHIPPED_BY: ecs-ship
EOF

As you can see, ecs-ship takes the cluster you want to update and the service you want
to update in that cluster. Furthermore, it takes a configuration file to
non-destructively modify some aspects of the chosen service, including the container
definitions. Here inside the container definition, we have decided to update the image to
refer to the new VERSION and upserted a couple of environment variables to later check on
the task definition. You can check the README on ecs-ship’s repo to get
familiar with which aspects of ecs services you can change.

Running this command will perform these steps for you:

  • Pull the task definition of the specified cluster and service.
  • Create a copy of this task definition performing the updates you requested on the input
    file (only if anything is updated).
  • Update the specified service to point to the newly created version of the task
    definition.
  • Wait for your service to reflect the changes you requested correctly.

If for some reason, your service doesn’t reach stability with your new configuration,
ecs-ship will automatically:

  • Revert to the last task definition (if it was stable).
  • De-register the new bugged task definition.

Right-sizing your services

We just saw how ecs-ship would help you in deploying new versions of ECS services. Now
let’s explore another simple yet powerful ecs-ship that can help you change the
number of resources a particular service requests from your ECS cluster.

One essential part of your ECS service configuration is the amount of resources a given
container or a given service requires. These resources are controlled by the cpu,
memory, and memoryReservation settings. cpu represents the amount of “shares” of a
virtual CPU your service needs. Simultaneously, memory and memoryReservation are hard and
soft caps to the amount of memory your service can use, respectively. These variables
cost you money in the following sense: if the sum of either the memory or the cpu
required by the services running in your cluster surpasses the number of actual
resources in the cluster, you need to spin up a new instance incurring in some
new costs.

However, not always do your services use as many resources as you reserve for them, and
since you’re charged for the amount of resources you do request, it is a good idea to
keep those as close as possible to the actual usage values. To work out the
amount of resources your ECS services need, you can enable and monitor CloudWatch
metrics
, and once you have worked out the number of resources you need,
you can update the configuration using ecs-ship:

cat << EOF | ecs-ship your-cluster yourapp-service
containerDefinitions:
  yourapp-definition:
    cpu: 123
    memoryReservation: 123
EOF

As a side note, if you specify memory, you need to make sure the value is
comfortably higher than memoryReservation.

How ecs-ship came to be

Those familiar with fabfuel’s ecs-deploy will notice that there’s no much
difference between it and ecs-ship. ecs-ship can be seen as a new take on the
already popular project, with just a few different opinions. ecs-ship focuses only on
updating a given service with a new version of its task definition, leaving out other
add-on features like slack reporting or newrelic metrics for deployments.

In terms of new features, ecs-ship can change resource allocations and
could be extended to change other aspects of a task definition. Furthermore, and we will
see an application of this on a further post, ecs-ship’s input file-based API makes it
easier to interoperate with other third-party or homebrewed tools.

Besides these minor differences, one of the primary motivations for building ecs-ship
was our bi-annual HackWeek events. These events give us some time to explore
technologies, work on a little side-project or solve issues in our workflow that are not
directly related to our backlog. In our case, with ecs-ship, we experimented with
golang (our team working mostly with Python and JavaScript) and solved the
need to update resource allocations for our services.

Conclusion

ecs-ship is a little tool you can use either as a static linked binary or as a
containerized tool to manage your ECS deployments from your local machine or your
continuous delivery pipelines.

Update (2021-04-08)

As of yesterday ecs-ship is now open source! You can grab the source code, binaries
and docker images following the instructions at https://github.com/AdRoll/ecs-ship.

Source: AdRoll