Deploying Dockerized application to AWS using Jenkins

There’s probably a handful of ways to run your application at AWS as a multi Docker container app. The easiest way doing this is probably with the help of Amazon ElasticBeanstalk. It allows you to quickly deploy and manage applications in the AWS Cloud and let Amazon care about the infrastructure magic.

In short all you need to do is to create an application in Elastic Beanstalk, configure its environment and provide an archive with Dockerrun.aws.json file. This is where you define which docker images should be used and how they need to be configured when deploying your app.

Continuous integration? Yes, this can be automated, Jenkins is there to help with lots of AWS plugins (so you don’t need to know any Groovy). We’ll discuss them a bit later.

Challenge

Let’s imagine we have an application with frontend and backend modules source code in separate repositories. Every time someone commits into one of these repos a Jenkins build is triggered. As a result the Docker image is built and pushed to AWS registry.

After that we would like to run job to deploy our app to AWS and specify which build should be deployed. We need a Dockerrun.aws.json and Jenkins plugins to help here.

Accepted!

So this is mine Dockerrun.aws.json file (inspired by this gist)

Let me please explain this.

“AWSEBDockerrunVersion”: version 2 indicates that this is a multicontainer configuration;

“volumes”: here we define which directories of a host instance we would like to mount into our container afterwards. We’re not using any volumes so far.

“containerDefinitions”: below goes the definition of 2 containers that our app includes: 
- backend-app container and
- frontend-app container.

Please mind the portMapping for each of containers that specifies which host port is mapped onto container port and environment configuration allowing us to setup database credentials, Spring application profile or whatever other properties.


The most interesting part of this file in terms of continuous integration to us are images and their tags.

AWS_ACCOUNT_ID.dkr.ecr.eu-west-1.amazonaws.com/backend-app and AWS_ACCOUNT_ID.dkr.ecr.eu-west-1.amazonaws.com/frontnend-app are repository URIs of backend-app and frontend-app images respectively. Certainly my AWS id is not AWS_ACCOUNT_ID, this I’ve changed for privacy reasons.

But <BACKEND_TAG> and <FRONTEND_TAG> tags are actually what we should have in our Dockerrun.aws.json. They’re here as placeholders for real tags that will be put by Jenkins during deployment. We’ll get into these details soon.

Now let’s go through build and deployment process step by step.

Build process

Every time someone commits into Bitbucket a webhook triggers Jenkins build. As a result Jenkins builds Docker image, tags it and pushes it to AWS container registry (ECR) with the help of

Docker build and push plugin that apparently builds and pushes Docker image
Amazon ECR plugin that generates Docker authentication token that is needed for pushing the image. (Actually this plugin has a bug and push quite often fails and I need to restart the job. I will update this post when I find the solution)

We tag feature branches build with build number tag and develop branch build with test tag. (I’ll explain that a bit later). For example if it was 355th build of frontend-app some feature branch then Docker image AWS_ACCOUNT_ID.dkr.ecr.eu-west-1.amazonaws.com/frontnend-app:355 is pushed to ECR.

Deployment process

For instance we’ve started deploying frontend-app. Since this is a parameterized build we need to select the tag of frontend-app image we would like to deploy. The list is implemented with the help of

Dynamic Parameter Plug-in.

Let’s choose 355 tag, this will set build parameter FRONTEND_TAG to be 355.

The other build parameter BACKEND_TAG we’ll get from backend-app deploy job latest build, using

Jenkins Copy build artifacts plugin.

Ok, now when we know the parameters we can start the deployment. 
The first step (that runs during Execute shell step) is the shell script to replace tag placeholders with actual tags:

# Replace the <FRONTEND_TAG> with the real version number
sed -i='' "s/<FRONTEND_TAG>/$DOCKER_TAG/" elastic-beanstalk/docker/dev/web/Dockerrun.aws.json
# Replace the <BACKEND_TAG> with the real version number
sed -i='' "s/<BACKEND_TAG>/$BACKEND_TAG/" elastic-beanstalk/docker/dev/web/Dockerrun.aws.json

Next step is zipping and uploading the archive to Amazon S3 and then deploying it to ElasticBeanstalk. This is done with the help of

AWSEB Deployment plugin

That’s it, our application is deployed!

What is your AWS dockerized application deployment pipeline? Please share experience in the comments.