Build docker images with Jenkins running in docker
Building docker images within the CI pipeline isn’t something new or unusual these days. Normally it’s super easy - you just have to install Docker in your Jenkins CI environment and add jenkins
user to the docker
group. Problems appear when your Jenkins instance is a docker container itself.
Inception begins
How to make docker
available inside your container? Here is a three steps solution:
- Install docker in your container.
- Expose docker socket (
/var/run/docker.sock
) to the Jenkins container. - Expose host’s docker executable (
/usr/bin/docker
) to the container.
At this point, you should already be able to use docker inside Jenkins’ container. Nevertheless, all docker commands executing by Jenkins must start with sudo docker
. That requirement has been greatly explained in Post-installation steps for Linux chapter of docker’s docs. If it fits your needs & security restriction you can stop here - but please know, that there is a better solution ;)
Non-sudo approach
To allow access for the non-privileged user we need to add jenkins
user to the docker
group. To make things work, you must assure that docker
group inside the container has the same GID as the group on the host. That’s an effect of exposing host’s docker
instance to the container - group id check takes place on the host’s side.
Firstly, you need to find docker
GID:
$ getent group docker
docker:x:999:mkowalski
Using docker
GID it’s now possible to add jenkins
user to it:
$ groupadd docker -g 999
$ usermod -a -G docker jenkins
Complete example
Let’s start building custom Jenkins image from the official one - Dockerfile
might look as follows:
# In general, you should always provide exact version (eg. 2.89.3)
# rather than some more general tag (latest/lts)
FROM jenkins/jenkins:lts
ARG HOST_DOCKER_GROUP_ID
USER root
# Create 'docker' group with provided group ID
# and add 'jenkins' user to it
RUN groupadd docker -g ${HOST_DOCKER_GROUP_ID} && \
usermod -a -G docker jenkins
# Install 'docker-ce' and it's dependencies
# https://docs.docker.com/engine/installation/linux/docker-ce/debian/
RUN apt-get update && \
apt-get install -y --no-install-recommends \
apt-transport-https \
ca-certificates \
curl \
software-properties-common && \
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg | apt-key add - && \
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") $(lsb_release -cs) stable" && \
apt-get update && \
apt-get install -y --no-install-recommends \
docker-ce && \
apt-get clean
# Run Jenkins as dedicated non-root user
USER jenkins
Because we need host’s docker
GID at container build process, I’ll use docker build
with additional --build-arg
parameter:
docker build \
--build-arg HOST_DOCKER_GROUP_ID=\
"`getent group docker | cut -d':' -f3`" \
-t jenkins-with-docker .
To run the container all parameters mentioned before will be required:
docker run \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/bin/docker:/usr/bin/docker \
-p 8080:8080 \
--name jenkins-with-docker \
jenkins-with-docker
Now, the last thing to do is to verify if everything runs as expected:
$ docker exec -it jenkins-with-docker whoami
jenkins
$ docker exec -it jenkins-with-docker docker run hello-world
If you’re able to see Hello from Docker!
you’re free to go!
Pros & cons
Pros
- you can now build docker images with Jenkins running in docker
- you don’t need to run Jenkins or
docker
command as root - no need to install any magic scripts/tools
- you can combine this approach with automated plugins installation (see
install-plugins.sh
script) to build easy recoverable Jenkins instance
Cons
- building dedicated image to the CI host - image is not stateless (in fact there is a way to overcome this using container’s startup script with
docker run
parameters but it has its own limitations) docker
inside the container has access to all host’s container (including managing them) - think twice before deploying this image on the mission-critical host with multiple containers!- in general, you should only build images using this solution not run them