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:

  1. Install docker in your container.
  2. Expose docker socket (/var/run/docker.sock) to the Jenkins container.
  3. 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