Using Jenkins in a Docker container

I remember being at a DoxLon meetup back in the days of Docker 1.0, and one of the presenters said that they’d found Jenkins leaked resources so was unsuitable for containerization. Happy news – there’s an official Jenkins image.


To use it, simply

docker run -p 8080:8080 -p 50000:50000 jenkins

but this way means that you lose you Jenkins data if you restart the container (either on purpose or by accident!). It’s also missing some useful plugins – like Git and Jenkins Swarm.

The official Jenkins image is supposed to enable the use of a plugins.txt file to extend the image to include plugins as part of the build, but I found the Docker Hub sometimes failed to download the plugins without failing the build, so it was a bit hit and miss as to whether the build worked.

So instead, I bypassed their plugins script, and got the Dockerfile to download the files directly as part of the build.

To address the persistence problem, you need an image with /var/jenkins_home writeable by the Jenkins user (UID 1000 at the time of writing).

To run the main Jenkins image, you should create a data container from the image first.

docker run -name data-jenkins andrewgortonuk/dockerjenkinswithgit

This image does nothing and exits, leaving behind a container named “data-jenkins”. Do not remove this container with “docker rm” as this will delete your data.

To start the main Jenkins image with the pre-installed plugins, use

docker run --rm -p 8080:8080 -p 50000:50000 -volumes-from data-jenkins andrewgortonuk/dockerjenkinswithgit

This says to run up the image up, remove the image when it exits (the ‘rm’ bit – because the data is saved into the data image), mapping port 8080 on the host to the container (the web ui), port 50000 to port 50000 (for build agents), and mapping volumes from the ‘data-jenkins’ image for storage.

This means you can restart the main Jenkins image and you’ll retain your data.

If you want to back it up, then the following code backs up your Jenkins data to the backup directory.

mkdir -p backup
docker run --rm -volumes-from data-jenkins -v $(pwd)/backup:/backup ubuntu tar cvf /backup/backup.tar /var/jenkins_home

To run a build agent which connects to the Jenkins master, use this (substitute the username and password if you’ve secured your Jenkins instance):-

docker run --rm -e JENKINS_USERNAME=<username> -e JENKINS_PASSWORD=<password> -e JENKINS_MASTER=http://<jenkins_master>:<web_port> maestrodev/build-agent

Bonus points – if you’re using CoreOS and/or SystemD, here’s a startup unit for the main Jenkins image:-

$ cat /etc/systemd/system/jenkins-master.service
[Unit]
Description=Jenkins Master
After=docker.service
Requires=docker.service
 
[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker kill jenkins-master
ExecStartPre=-/usr/bin/docker rm jenkins-master
ExecStartPre=-/usr/bin/docker pull andrewgortonuk/dockerjenkinswithgitdata
ExecStartPre=-/usr/bin/docker run -name data-jenkins andrewgortonuk/dockerjenkinswithgitdata
ExecStartPre=-/usr/bin/docker pull andrewgortonuk/dockerjenkinswithgit
ExecStart=/usr/bin/docker run --rm -p 8080:8080 -p 50000:50000 -volumes-from data-jenkins -name jenkins-master andrewgortonuk/dockerjenkinswithgit
Restart=always
 
[Install]
WantedBy=multi-user.target
 
$ sudo systemctl enable /etc/systemd/system/jenkins-master.service
$ sudo systemctl start jenkins-master.service

[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker kill jenkins-master
ExecStartPre=-/usr/bin/docker rm jenkins-master
ExecStartPre=-/usr/bin/docker pull andrewgortonuk/dockerjenkinswithgitdata
ExecStartPre=-/usr/bin/docker run -name data-jenkins andrewgortonuk/dockerjenkinswithgitdata
ExecStartPre=-/usr/bin/docker pull andrewgortonuk/dockerjenkinswithgit
ExecStart=/usr/bin/docker run –rm -p 8080:8080 -p 50000:50000 -volumes-from data-jenkins -name jenkins-master andrewgortonuk/dockerjenkinswithgit
Restart=always

[Install]
WantedBy=multi-user.target

$ sudo systemctl enable /etc/systemd/system/jenkins-master.service
$ sudo systemctl start jenkins-master.service

and here’s one for the Jenkins Build Agents :-

$ cat /etc/systemd/system/jenkins-agent.service
[Unit]
Description=Jenkins Build Agent
After=docker.service
Requires=docker.service
 
[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker pull maestrodev/build-agent 
ExecStart=/usr/bin/docker run --rm -e JENKINS_USERNAME=<username> -e JENKINS_PASSWORD=<password> -e JENKINS_MASTER=http://<jenkins_master>:<web_port> maestrodev/build-agent
Restart=always
 
[Install]
WantedBy=multi-user.target
 
$ sudo systemctl enable /etc/systemd/system/jenkins-agent.service
$ sudo systemctl start jenkins-agent.service

[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker pull maestrodev/build-agent
ExecStart=/usr/bin/docker run –rm -e JENKINS_USERNAME=<username> -e JENKINS_PASSWORD=<password> -e JENKINS_MASTER=http://<jenkins_master>:<web_port> maestrodev/build-agent
Restart=always

[Install]
WantedBy=multi-user.target

$ sudo systemctl enable /etc/systemd/system/jenkins-agent.service
$ sudo systemctl start jenkins-agent.service

If you’re running the build agent on the same machine as the Jenkins Master, you might want to change the “After” and “Requires” lines to “jenkins-master.service”.

And yes, this is how I’ve got my Jenkins build farm configured, and it’s been stable for me so far.

WARNING: Jenkins have issued a security advisory around 1.605 and below, and 1.596.1 and below for LTS versions. The current official image uses LTS 1.596.1, so is likely vulnerable.

Edit 2015-10-14: If you’re using Maven and build agents, then you’ll want a different build agent.

This is my personal blog - all views are my own.

Tagged with: , ,