I’ve reach the point with my level-three-rest microservice where it needs to be delivered to infrastructure which isn’t my local machine.

Iteration 0

The very basic way to this would be just to build the project locally, copy the executable and run them on the target machine. This completely defeats the purpose of having a CI process.

Iteration 1

Lets think about this some more, we can have Travis CI build the binary, deliver it to an AWS S3 bucket which has versioning enabled. And with the appropriate AWS Auto Scaling Group and launch configuration, when the S3 bucket gets updated, you can have a lambda function scale the service out and then back in to deploy the latest binary in the S3 bucket.

This solution will work and easy to setup, however we would be consuming an entire EC2 instance to run what is currently a ~6MB binary file. What happens when you have 30 microservices deployed in this fashion? That is a lot of instances which you have to manage.

Iteration 2

In this iteration we are going to have Travis CI build a Docker container from scratch so it should be a similar size as the compile binary file. This will be then uploaded to a Docker registry, when the EC2 instance boots it can pull the latest images and then execute the Docker run command.

Dockerfile

The Dockerfile is very straight forward.

FROM scratch
ADD level-three-rest /level-three-rest
CMD ["/level-three-rest"]

Test Locally

Before we have Travis CI build our image for us, we should test it out locally. I do all my development in my spare time on Windows, therefore the GOOS flag has to be set to linux to function in Docker.

$env:GOOS = "linux"

We are now ready to bulid the project and the Docker image.

go build
docker build -t level-three-rest -f .\Dockerfile .

If we run docker images we should get the following output:

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
level-three-rest    latest              abeffecf909d        6 days ago          6.099 MB

You should now be able to run the Docker container and call the API on port 8080.

docker run -d -p 8080:8080 level-three-rest

.travis.yml

The updates to the are straight forward since all we need to do is build the Docker image and upload it to our Docker hub. You can find all the information on the Travis-CI Documents.

The first thing to do is add the encrypted variables which are required to logon to Docker hub

travis encrypt DOCKER_USER=username --add
travis encrypt DOCKER_PASS=password --add

Set the appropriate environment variables so the binary is statically compiled.

env:
  global:
  - COMMIT=${TRAVIS_COMMIT::8}
  - REPO=bhavikk/level-three-rest
  - CGO_ENABLED=0
  - GOOS=linux
  - GOARCH=amd64  

Add the docker build step after you run go build in your build chain.

script:
 - export TAG=`if [[ $TRAVIS_PULL_REQUEST == "false" ]] && [[ $TRAVIS_BRANCH == "master" ]]; then echo "latest"; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi`
 - export REPO=bhavikk/level-three-rest
 - docker build -t $REPO:$TAG -f Dockerfile .

After a successful build, we login and then upload the docker image to Docker h ub. You can set the appropriate tags before uploading the image. One other interesting thing you can perform at this stage is running the docker image and running appropriate tests against it to ensure everything passes.

 after_success:
 - docker login -u $DOCKER_USER -p $DOCKER_PASS
 - if [[ $TRAVIS_PULL_REQUEST == "false" ]] && [[ $TRAVIS_BRANCH == "master" ]]; then
   docker tag $REPO:$TAG $REPO:$TRAVIS_BUILD_NUMBER;
   docker push $REPO:$TRAVIS_BUILD_NUMBER;
   fi 
 - docker push $REPO:$TAG

This is a great start, we now have a public repository with our Docker image which we can pull down and run after every commit. Most companies will not want their Docker images available publically, therefore in the next blog post, I’ll show how you can push the images to AWS EC2 Container Registry from Travis-CI.