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.