Quick and dirty docker deployment in AWS
At Spantree we periodically do hackathons, sometimes for internal projects, at other times for non-profit organizations. Every year on Martin Luther King, Jr. Day we held our annual hackathon for social good, which is always exciting.
This past year, we decided that we did not want to spend too much time upfront implementing a continuous deployment solution because we wanted to focus on more fun and productive aspects of the hackathon. So, I came up with the following super dirty solution to deploy an application that runs as a docker container in an AWS host.
The application is a small demo application written in clojure. We used CircleCI as the CI server and the application directory structure was somewhat like this:
$ tree -L 2
.
├── README.md
├── circle.yml
├── deploy-app.sh
└── server
├── Dockerfile
├── README.md
├── build.sh
├── deploy.sh
├── dev
├── profiles_sample.clj
├── project.clj
├── resources
├── run.sh
├── src
├── supervisord.conf
└── test
The first step is create an AWS instance and install docker, the installation of docker and creation of an AWS instance are extremely well documented everywhere in the internet so I will not bore the audience with that.
The second step is to create a circleci.yml file with a configuration similar to this:
general:
build_dir: server
machine:
services:
- docker
java:
version:
oraclejdk8
test:
override:
# Yes quick and dirty
- echo "Skipping tests"
deployment:
staging:
branch: develop
commands:
- lein uberjar
- cp target/my-artifact.jar .
- docker build -t spantree/myapp .
- docker login -u $DOCKER_LOGIN -p $DOCKER_PASSWORD -e $DOCKER_EMAIL
- docker push spantree/myapp
- ../deploy-app.sh $AWSHOST
The variables $DOCKER_LOGIN
, $DOCKER_PASSWORD
, $DOCKER_EMAIL
and $AWSHOST
are defined in the Web UI
The third step is to add a script with a content similar to the followint
#!/usr/bin/env bash
set -o xtrace # trace what gets executed
set -o errexit # exit when a command fails.
set -o nounset # exit when your script tries to use undeclared variables
__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
pushd "$__dir"
server=$1
ssh -t ubuntu@"$server" '( docker kill $(docker ps -aq --filter "label=net.spantree.myapp=true" --filter status=running ) ; docker rmi -f spantree/myapp:latest ) ; docker run -d --label=net.spantree.myapp=true -p 3000:3000 spantree/myapp:latest'
popd
Being that this simple project is not business critical we can afford to have a small downtime, we are not using any type of versioning. Instead, we want to have a deployment that just overwrites the previous one for fast iteration.
The script connects through ssh to the server and the proceeds to:
docker kill $(docker ps -aq --filter "label=net.spantree.myapp=true" --filter status=running
, kill all the running containers that are running and have the label "net.spantree.myapp=true"docker rmi -f spantree/myapp:latest
remove the current image because we do not want to fill the filesystem with unused images.docker run -d --label=net.spantree.myapp=true -p 3000:3000 spantree/myapp:latest
start a new version of the container.
It is probably obvious that this type of deployment is terrible for anything serious, but for something super quick it just works!

Sebastian Otaegui
Sebastian Otaegui is a system admin currently living in Buenos Aires. He started his career working as one of the web hosting administrators in the eBusiness hosting team at IBM, soon he got tired of the red tape that big organizations suffer. After working for one year for a very large extended warranty company, he was asked to move to Chicago to work as the team leader for the web hosting administration team. During the six years working at The Warranty Group he met Cedric. Sebastian and Cedric became friends and begun introducing concepts that back then didn't have a name and today are known as DevOps practices.
In his free time, Sebastian enjoys trying new technologies, watching movies with his wife and playing Star Wars The Old Republic. Fun fact about Sebastian is he played rugby for 14 years.