Hello, I'm Alan

Docker + Elastic Beanstalk

Thursday, February 12 2015

Some of you may have seen my talks around Melbourne about some things I’ve used Docker for. No, it’s not for everything. It’s really not for things that store state locally (eg. databases).

Late 2014, I started a new position, and became responsible for (amongst other things), looking after ops. The existing solution was giving us some issues, and I’d been wanting to experiment with using Docker for running apps in production, not just doing running CI tasks.

For a while CoreOS had been on my list to experiment with, but although it seemed great for service worker type applications, I couldn’t see a good way of deploying web applications that need to be deployed with zero downtime. After this I just went with straight Ubuntu instances and installing docker on them.

From here I hooked up Buildkite to run a script everything someone pushes to GitHub. When this repository is pushed to, a script runs that builds the container setting up any library dependencies needed, and then pushes this container to the docker registry, and would then SSH to each server in staging and run another scrip that pulls the container down and restarts to run it. It would run serially to make sure each node is up and serving requests again before the next one got pulled down to update & restart. Overall is worked reasonably well, but wasn’t very cloud like, as any time I added or removed an instance, I’d have to tell Buildkite what hosts to now deploy to.

Recently I discovered that Elastic Beanstalk, can also deploy Docker containers, taking care of setting up ELB, RDS and managing zero downtime deployments.

It’s been a bit of futzing around to make all the parts work together. Firstly, setup a new IAM user, with this policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1420763850000",
            "Effect": "Allow",
            "Action": [
                "autoscaling:SuspendProcesses",
                "autoscaling:ResumeProcesses",
                "autoscaling:DescribeScalingActivities",
                "autoscaling:DescribeAutoScalingGroups",
                "cloudformation:GetTemplate",
                "cloudformation:DescribeStackResource",
                "ec2:DescribeImages",
                "ec2:DescribeKeyPairs",
                "ec2:DescribeSecurityGroups",
                "elasticbeanstalk:DescribeConfigurationOptions",
                "elasticbeanstalk:DescribeConfigurationSettings",
                "elasticbeanstalk:DescribeEnvironmentResources",
                "elasticbeanstalk:DescribeEvents",
                "elasticbeanstalk:DescribeEnvironments",
                "elasticbeanstalk:RequestEnvironmentInfo",
                "elasticbeanstalk:RetrieveEnvironmentInfo",
                "elasticbeanstalk:UpdateEnvironment",
                "elasticbeanstalk:ValidateConfigurationSettings",
                "elasticbeanstalk:CreateApplicationVersion",
                "elasticloadbalancing:DescribeInstanceHealth",
                "rds:DescribeOrderableDBInstanceOptions"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Sid": "Stmt1420691017000",
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:ListBucket",
                "s3:DeleteObject",
                "s3:GetObjectAcl",
                "s3:GetBucketPolicy",
                "s3:PutObjectAcl"
            ],
            "Resource": [
                "arn:aws:s3:::application-deploy/*",
                "arn:aws:s3:::application-deploy",
                "arn:aws:s3:::elasticbeanstalk*"
            ]
        }
    ]
}

Most permissions are straightforward. For S3, you’ll need a bucket you are using for deploys that the deploy user can write to.

You’ll also need access to a bucket that AWS manages that start with ‘elasticbeanstalk’, otherwise you get some odd errors.

When a build is good, you’ll need to create a new application version on Elastic Beanstalk, which you can then deploy to an Elastic Beanstalk Environment

Create an application version (once your docker container is pushed)

CONTAINERURL is where the docker container has been pushed to APPNAME is the application name in Elastic Beanstalk

SHA1=`git rev-parse --short HEAD | tr -d "\n"`

DEPLOY_FILE=$SHA1.zip

cat ops/Dockerrun.aws.json.template | sed "s#<TAG>#${SHA1}#" | sed "s#<CONTAINER_URL>#${CONTAINER_URL}#" > Dockerrun.aws.json
zip -r $DEPLOY_FILE Dockerrun.aws.json

aws s3 cp $DEPLOY_FILE s3://application-deploy/$APP_NAME/$DEPLOY_FILE

aws elasticbeanstalk create-application-version --application-name $APP_NAME --version-label $SHA1 --source-bundle S3Bucket=application-deploy,S3Key=$APP_NAME/$DEPLOY_FILE

When you want to deploy an application version to an environment

aws elasticbeanstalk update-environment --environment-name $APP_ENV --version-label ${SHA1}

It will return quickly, you’ll have to watch Beanstalk to see that the deploy has gone OK