Containerizing Jenkins, DooD and DinD
Learn to containerize Jenkins and run Docker commands with Docker inside Docker (DinD) or Docker outside of Docker (DooD). DinD makes Jenkins' containers invisible to the host, while DooD makes them visible as siblings. Each has its own advantages and drawbacks.
Note: The sections below use the latest version of the Jenkins image i.e. jenkins/jenkins:latest-jdk17
. You should lock this version down.
All source code is available on GitHub.
Basic Configuration
The goal is to run a Jenkins container without the requirement of
creating any additional containers from Jenkins.
Alternatively, create a docker-compose.yaml
as follows:
And start up the container:
Executing docker commands from a Jenkins container
Being able to run docker commands is useful for situations when Docker images need to be built or dockerized tests such as those based on test containers need to be executed.
There are two approaches to configure a containerized Jenkins to communicate with docker:
Docker Outside of Docker (DooD)
- The basic idea is that containers talk to the Docker Daemon/Server process running on the host system.
- To enable this, the
/var/run/docker.sock
UNIX socket is mounted against a container to allow the container to communicate with the Docker daemon on the host. - According to the docker documentation:
By default, a unix domain socket (or IPC socket) is created at /var/run/docker.sock, requiring either root permission, or docker group membership.
- Essentially, the Docker daemon listens on the
/var/run/docker.sock
UNIX socket. This is used by Docker clients to communicate with the server process.
We'll build a custom Jenkins Image with docker installed. Assuming the following directory structure and working directory:
We'll first create our custom Dockerfile which adds docker to the base Jenkins image:
And then from the dood
working directory:
Alternatively, create a docker-compose.yaml
as follows:
And start up the container:
Consequences of DooD
- Any containers started by the Jenkins containers will be its siblings and visible to the host system.
Docker inside of Docker (DinD)
- The basic idea is that docker itself is started as a container and networked with the Jenkins container.
We'll build a custom Jenkins Image with docker installed. Assuming the following directory structure and working directory:
We'll first create our custom Dockerfile which adds docker to the base Jenkins image:
And then from the dind
working directory:
Alternatively, create a docker-compose.yaml
as follows:
And start up the container:
Consequences of DinD
- Any containers started by the containers are its children and not visible to the host system.
- The privileged flag is required for DinD. As per the docker documentation:
--privileged flag gives all capabilities to the container, and it also lifts all the limitations enforced by the device cgroup controller. In other words, the container can then do almost everything that the host can do. This flag exists to allow special use-cases, like running Docker within Docker.
In other words, if the container gets compromised, the host gets compromised as well.
Jenkins Configuration and Docker Validation
Regardless of whether DinD or DooD was used, you'll want to install a couple of plugins to use Docker i.e.
After installing the plugins, you can create a pipeline job to verify that everything is working:
Verifying DooD Sibling Relationship
Assuming you hadn't already pulled the latest node image, running the docker image ls
command on the host will show
you the node image in the image listing.
Verifying DinD Parent/Child Relationship
Assuming you hadn't already pulled the latest node image, running the docker image ls
command on the host will not show the latest node image in the listing.
However, if you connect to the jenkins container and check the listing
there it will show up in the image listing pulled from the container:
#!/usr/bin/env bash
docker container exec -it jenkins bash
References / Additional Reading
-
"A Case for Docker-in-Docker on Kubernetes", applatix, https://applatix.com/case-docker-docker-kubernetes-part
-
"Do not use docker in docker for ci", jpetazzo, https://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/
-
"Docker Architecture", Docker, https://docs.docker.com/get-started/overview/#docker-architecture
-
"Docker", Jenkins, https://www.jenkins.io/doc/book/installing/docker/
-
“Dockerd; Daemon Socket Option”, Docker Documentation, https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-socket-option.
-
"Dockerhub Jenkins Image", DockerHub, https://hub.docker.com/r/jenkins/jenkins/tags
-
"Enhanced Container Isolation (ECI)", Docker, https://docs.docker.com/desktop/hardened-desktop/enhanced-container-isolation/how-eci-works
-
“How to Add Java Arguments to Jenkins?”, CloudBees Documentation, https://docs.cloudbees.com/docs/cloudbees-ci-kb/latest/client-and-managed-masters/how-to-add-java-arguments-to-jenkins#_running_jenkins_inside_docker.
-
"How to Setup Docker Containers As Build Agents for Jenkins", CloudBeesTV, https://www.youtube.com/watch?v=ymI02j-hqpU
-
"Privileged Flag", Docker, https://docs.docker.com/engine/reference/commandline/run/#privileged
-
"Rootless Containers", Docker, https://docs.docker.com/engine/security/rootless/
-
"Sysbox", nestybox, https://github.com/nestybox/sysbox
-
"What is the purpose of docker-in-docker when using a dockerized Jenkins?", Jenkins Community, https://community.jenkins.io/t/what-is-the-purpose-of-docker-in-docker-when-using-a-dockerized-jenkins/1370/1
-
"Why running privileged containers is a bad idea", trendmicro, https://www.trendmicro.com/en_us/research/19/l/why-running-a-privileged-container-in-docker-is-a-bad-idea.html
-
"var run docker.sock", Educative, https://www.educative.io/answers/var-run-dockersock
Attributions
- "Jenkins is the way", Jenkins, https://www.jenkins.io,
licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License