Acknowledgments
Thanks to James Turnbull, the author of “The Docker Book: Containerization is the new virtualization” and Jerome Petazzoni the creator of the “Docker in Docker” Github repository, I learned how to run Docker recursively.
Overview
In this post I would like to describe how to use Docker to create Jenkins slaves which can also build and run Docker containers :)
Let’s assume that Jenkins server is up and running. To connect the slave to the master I will use Jenkins Swarm plugin.
Preparation
Firstly, let’s prepare Dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FROM ubuntu
ADD http://get.docker.io install-docker.sh
RUN chmod +x install-docker.sh
RUN /install-docker.sh
RUN apt-get install -yqq openjdk-7-jdk
VOLUME /var/lib/docker
ADD http://repo.jenkins-ci.org/releases/org/jenkins-ci/plugins/swarm-client/1.22/swarm-client-1.22-jar-with-dependencies.jar /var/lib/jenkins/swarm-client.jar
ADD start.sh /var/lib/jenkins/start.sh
RUN chmod +x /var/lib/jenkins/start.sh
WORKDIR /var/lib/jenkins
ENTRYPOINT ["/var/lib/jenkins/start.sh"]
Description:
- lines 3 - 5 install Docker
- line 7 installs openjdk
- line 9 - creates Docker volume for
/var/lib/docker
directory. It is required for sharing Docker containers between the container and the host. - line 11 - downloads Jenkins Swarm client
- line 12 - 13 - add the start script and make it executable
- line 15 - sets current directory to
/var/lib/jenkins
- line 16 - runs the start script
Then, let’s create start.sh script which will run ‘Docker in Docker’ and start the Jenkins slave.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#!/bin/bash
### DOCKER ###
CGROUP=/sys/fs/cgroup
[ -d $CGROUP ] ||
mkdir $CGROUP
mountpoint -q $CGROUP ||
mount -n -t tmpfs -o uid=0,gid=0,mode=0755 cgroup $CGROUP || {
echo "Could not make a tmpfs mount. Did you use -privileged?"
exit 1
}
for SUBSYS in $(cut -d: -f2 /proc/1/cgroup)
do
[ -d $CGROUP/$SUBSYS ] || mkdir $CGROUP/$SUBSYS
mountpoint -q $CGROUP/$SUBSYS ||
mount -n -t cgroup -o $SUBSYS cgroup $CGROUP/$SUBSYS
done
pushd /proc/self/fd
for FD in *
do
case "$FD" in
[012])
;;
*)
eval exec "$FD>&-"
;;
esac
done
popd
docker -d &
### JENKINS SLAVE ###
function append() {
if [ -n "$2" ]; then
OPTS="$OPTS \
$1 $2"
fi
}
JENKINS_HOME=/var/lib/jenkins
if [ -z "$MASTER_URL" ]; then
echo "You have to specify \$MASTER_URL variable"
exit 1
fi
OPTS=
append "-master" $MASTER_URL
append "-executors" $EXECUTORS
append "-fsroot" $JENKINS_HOME
append "-labels" \"$LABELS\"
append "-name" $JENKINS_SLAVE_NAME
append "-username" $JENKINS_USERNAME
append "-password" $JENKINS_PASSWORD
java -jar $JENKINS_HOME/swarm-client.jar $OPTS
Description:
- lines 4 - 35 configures Docker in Docker and start the Docker daemon. For further details, please have a look at “Docker in Docker” Github repository or chapter 5th in “The Docker Book”.
- lines 38 - 43 a function which configures parameters for Jenkins Swarm client.
- lines 47 - 50 check if MASTER_URL is configured.
- lines 54 - 60 configure other options for Jenkins Swarm client.
- line 62 runs Jenkins Swarm client.
Demo
After you build the image, you have to run the container with --privileged
flag
$ docker run --privileged -ti -e MASTER_URL=http://my.jenkins.com:8080 jks
/proc/self/fd /var/lib/jenkins
/var/lib/jenkins
INFO[0000] +job serveapi(unix:///var/run/docker.sock)
INFO[0000] +job init_networkdriver()
INFO[0000] Listening for HTTP on unix (/var/run/docker.sock)
INFO[0000] -job init_networkdriver() = OK (0)
INFO[0000] Loading containers: start.
INFO[0000] Loading containers: done.
INFO[0000] docker daemon: 1.4.1 5bc2ff8; execdriver: native-0.2; graphdriver: aufs
INFO[0000] +job acceptconnections()
INFO[0000] -job acceptconnections() = OK (0)
Discovering Jenkins master
Attempting to connect to http://my.jenkins.com:8080/ 3549a2c2-b7ae-44ab-8037-e1e402495258
Could not obtain CSRF crumb. Response code: 404
Dec 27, 2014 3:39:00 PM hudson.remoting.jnlp.Main createEngine
INFO: Setting up slave: 687b092b6b14
Dec 27, 2014 3:39:00 PM hudson.remoting.jnlp.Main$CuiListener <init>
INFO: Jenkins agent is running in headless mode.
Dec 27, 2014 3:39:00 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Locating server among [http://my.jenkins.com:8080/]
Dec 27, 2014 3:39:01 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Connecting to acid-jkm.yrdrt.fra.hybris.com:42534
Dec 27, 2014 3:39:01 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Handshaking
Dec 27, 2014 3:39:02 PM hudson.remoting.jnlp.Main$CuiListener status
INFO: Connected
Conclusion
Thanks to that setup we have immutable Jenkins slaves. It is possible to extend it by installing other tools and frameworks. Moreover, it is very easy to remove the slave and create it again. To log in to debug why the Jenkins job fails on the Jenkins slave, just use docker exec -ti <container-id> /bin/bash