Continuous Delivery (and Deployment) with Travis CI and Docker Services
Feb 1, 2017 14:53 · 641 words · 4 minute read
We are doing a lot of Kubernetes and Openshift at work, having our Continuous Integration / Continuous Delivery environment set up with Jenkins. For my playground here I wanted to try out something different and so I checked out Travis CI which looked just appropriate.
I had four goals in mind when I started with Travis:
- I want to build my software with each change in the repository
- I want to package this software in a Docker container
- I want to publish this Docker image to my private registry (r.3r1.co)
- I want to to deploy the latest version of this image continuously
In this blog post I show an example of a password generator written in Go, but the packaging and deployment part is technology agnostic.
Building your project with Travis works like a charm and is extremely straight forward. All you have to do is register online with your Github account, link the repository that you want to build and add a .travis.yml file to it.
Build the Go App
In order to build the Go app after each push to your repo you have to add the following two lines to .travis.yml:
language: go
script: go build -o main
Package and Push the Docker Container
This was quite simple, so as a next step we would like to package this Go binary into a Docker container. The Dockerfile is shown below:
FROM centos
LABEL maintainer "Eric Muellenbach"
RUN useradd -ms /bin/bash myuser
USER myuser
WORKDIR /home/myuser
ADD main main
EXPOSE 9090
ENTRYPOINT ["./main"]
Good for us, Travis CI supports the build of Docker containers since a while, so we just have to extend the travis.yml a bit:
sudo: required
services:
- docker
env:
- DOCKER_IMAGE_NAME="$DOCKER_REGISTRY/gowebapp:$TRAVIS_BUILD_NUMBER"
after_success:
- docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" $DOCKER_REGISTRY
- docker build -t $DOCKER_IMAGE_NAME .
- docker push $DOCKER_IMAGE_NAME
As you can see, there are three environment variables listed which don’t appear in the “env” section: DOCKER_REGISTRY, DOCKER_USERNAME and DOCKER_PASSWORD. These environment variables are stored in the settings of the Travis Job, so I can store my secret credentials for the Docker Registry in the CI environment and don’t need to worry about their security. Also, when people want to fork my repository, they just change their Travis Job settings and they are good to go.
Deploy the image
After building and pushing the image, I would like to deploy the image directly on my VM in order to have access to the latest version of my service. For this one Docker Services come in handy. I initially created a Docker service like that on the VM:
docker service create --replicas 1 --name passwordservice passwordservice:1.0-1
To update the service with the next version of my image, I would use the Docker CLI like that:
docker service update --image passwordservice:1.0-2 passwordservice
In order to automate this during my build, we have the following flow:
- Log in to my VM
- Pull the latest image to make it available locally
- Run the docker service update command
Lucky again, travis supports the encrypted storage of files in the repository, which I found in this excellent tutorial here. The Travis CLI automatically changes the .travis.yml and adds the decryption part of your file in the before_install step. I changed that like in the example below to have it in the before_deploy step. That way, I can more easily see if the build succeeded and deployment failed for whatever reason.
addons:
ssh_known_hosts: xxx.xxx.xxx.xxx
before_deploy:
- openssl aes-256-cbc -K $encrypted_d3751784d1e2_key -iv $encrypted_d3751784d1e2_iv -in deploy_rsa.enc -out /tmp/deploy_rsa -d
- eval "$(ssh-agent -s)"
- chmod 600 /tmp/deploy_rsa
- ssh-add /tmp/deploy_rsa
deploy:
- provider: script
script: ssh user@ xxx.xxx.xxx.xxx 'docker pull $DOCKER_IMAGE_NAME && docker service update --image $DOCKER_IMAGE_NAME passwordservice'
With this workflow I can easily package and deploy all my projects on Github with very little effort. Starting just with a sample Password Generator, I’m going to use this flow in all my projects to deploy them continuously.