Create your own Travis CI-like server with Jenkins and Docker
Recently, I’ve started to begin implementing some modern automation practices into my personal software projects using Travis CI. The experience has been positive, but I realized later on that lock in to Travis CI’s way of doing things was going to cause problems in the future when I wanted to apply CI to private projects. So I began experimenting with Docker and Jenkins to see if I could accurately replicate Travis-like functionality on my own server. I picked Jenkins for the CI server for a few reasons:
- Jenkins has the most mature ecosystem and community among CI servers.
- The Blue Ocean plugin closely replicates the UI experience of Travis.
- The Pipeline plugin allows you to build out a declarative Jenkinsfile that is similar to the .travis.yml file for Travis CI.
- Docker support in Jenkins allows multi-language support without having to maintain a server with a bunch of different programming language runtimes or compilers installed.
I wanted this environment to be as portable as possible so I looked to Docker to run the Jenkins server itself. This type of configuration initially proved challenging because the Docker socket on the host is owned by the Docker group on the host. Using boot2docker on windows made it almost impossible to automate the Docker socket connection to the inside of the Jenkins container. To combat this I switched to an Ubuntu VM with Docker pre-installed instead of the busybox based boot2docker VM I used initially. I also had some trouble with permissions on the jenkins_home folder so I took a shortcut during development and set the permissions to 777 on it. After that was finished I just ran the container with this command:
<pre class="lang:default decode:true">
docker run -d –name my_jenkins -v /home/vagrant/jenkins_home:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock -p 8080:8080 shimmi/jenkins`
After a few minutes Jenkins was ready and I was ready to start setting up the Jenkins server. To start you have to get the initial admin password, you can do this by running one of two commands:
docker exec my_jenkins cat /var/jenkins_home/secrets/initialAdminPassword
or
cat ~/jenkins_home/secrets/initialAdminPassword
This will output the initial password to the terminal so you can setup your Jenkins admin user. Once you’ve done that you can start setting up a pipeline in the blue ocean UI. Click the open blue ocean navigation option on the left hand navigation pane.
This will take you to the pipeline builder where we can start building out our Jenkinsfile. A Jenkinsfile is a Groovy script file that declaratively or programmatically declares your pipeline. This is sort of like a CI-process as code implementation similar to the .travis.yml file. The difference here, however, is that Groovy is a full scripting language that runs on the JVM and is capable of multiple steps and conditions and ultimately can produce a much more complex pipeline. To get your Jenkinsfile into your repository you will need to set up a source control repository and give Jenkins commit access to it so that it can write the new Jenkinsfile to your source repository. We’ll give Jenkins access to a Github repo through a Github personal access token. You can generate this token through the handy link on the initial add project page.
Once your token is created and plugged into Jenkins it will take you to the first step in your build where you can start defining your pipeline. I did a relatively simple example with an elixir container for my identicon project on my Github page. You can see the Jenkinsfile below.
pipeline {
agent {
docker {
image 'elixir'
}
}
stages {
stage('Build') {
steps {
sh '''mix deps.get
mix compile
mix test'''
}
}
}
}
This will pull the elixir image from Docker hub and run the shell script in a “build” stage. The great thing about this approach to CIaC is that the pipeline can be reviewed, changed, and put under source control by all the team members!