Docker in Docker (also known as dind) is, as the name implies, running Docker on top of a Docker container. Controlling containers from a Docker container is not a particular use case but is often necessary to run CI tools such as Jenkins on top of a Docker container. It is not a specific use case but is often needed to run CI tools such as Jenkins on Docker containers.
This article describes two approaches to achieving Docker in Docker and introduces some points to consider when using Docker in Docker.
The benefit of Docker in Docker
There are two prominent use cases for Docker-in-Docker, and the advantage is that it can achieve them.
The first is to run CI systems such as GitLab or Jenkins on-premises on Docker containers. In this case, you can run Docker containers for each programming language/dependent middleware in the container and then create Docker images for production use, etc. This has the advantage of nested usage of Docker.
Another common use case is when you want to use Docker as a sandbox environment. Suppose you want to use a Docker container as an environment isolated from the host environment where the actual work is done. In that case, you can launch a Docker container within that container. Building a sandbox environment inside a container is very easy because the environment can be disposed of by simply destroying the container.
Two approaches to running Docker in Docker
There are two ways to achieve Docker in Docker.
- Launching a Docker container inside a Docker container
- Sharing the Docker daemon with the host machine and increasing the number of containers in the same hierarchy as the host environment. In the following, we will discuss the details and Pros and Cons.
Example 1: Docker in Docker Using dind
This method uses a container with Docker installed and runs a Docker daemon in the container separately from the host. Alpine based Docker official image and ubuntu based teracy/ubuntu are available as images for DinD. (dind tag) based on alpine, and teracy/ubuntu based on ubuntu.
The following is an example of the command for Docker-in-Docker using the official Docker image
$ docker run --privileged --name dind -d docker:stable-dind
$ docker exec -it dind /bin/ash
Since the Docker daemon on the host machine and the Docker daemon on the docker:dind container are separate, the containers on the container are not visible from the host machine, and vice versa. This means that containers can have a hierarchical structure, making them easier to manage.
There are many problems with this method. For example
- You need to boot with the privileged option.
- Data volume is implicitly used, so data is left on the host machine.
- docker-compose cannot be used by default.
Also, according to jpetazzo, a contributor to Docker's privileged flag and the developer of the docker:dind image, it was created to streamline the development of Docker itself and has some drawbacks for other uses. There are some drawbacks for other uses. Specifically, there are the following problems
- It does not work well with Linux Security Modules (LSM).
- A mismatch in the file system causes problems for containers created inside the parent container.
Thus, there are a lot of problems when using Docker-in-Docker images for standard use cases, and it is advisable to use the following methods to achieve this.
Example 2: Docker in Docker Using [/var/run/docker.sock]
This method does not use a Docker-in-Docker image; it uses the Docker daemon on the host machine from a Docker container.
This method is sometimes referred to as DooD (Docker outside of Docker) because it uses Docker outside of Docker. This method is sometimes called DooD (Docker outside of Docker). Since it only mounts the socket of the host environment, the Docker image to be used is not
dind, and the -privileged option is not required. To be more specific, just run the following command:
$ docker run -ti --rm -v /var/run/docker.sock:/var/run/docker.sock docker /bin/ash
The system is easy to understand since it only mounts the socket of the host machine, and it saves disk space since it does not have duplicate Docker images. It saves disk space by not having duplicate Docker images. It is also secure as it does not require the privileged option to be given.
You can see other Docker containers running on the host machine. If you don't want to be seen by the host machine, you need to be careful. For example, you can stop other Jenkins containers on the Jenkins container.
Also, from the host machine, you can see the Docker container being operated from the Docker container and the container running on the host machine in parallel (no parent-child relationship), which is difficult to manage.
Recursive Docker in Docker
If you can do Docker-in-Docker, why can't you do something like Docker-in-Docker-in-Docker? In conclusion, it is possible. As it turns out, it is possible.
In the case of the dind image shown in the first example, you can always nest one more level when you are inside a privileged container. The pattern of using /var/run/docker.sock is also possible, of course, since it is the Docker daemon on the host side that handles this. You can launch multiple Docker containers in parallel by referring to the same host-side Docker daemon.
Is running Docker in Docker secure?
We cannot say that it is not secure in general, but there is a risk when using the privileged flag.
In the first example, we need to use the privileged flag, which can be a big problem in some cases. The privileged flag can give you critical access to the host's resources. However, if the privileged container is abused, cybercriminals can also access the resources. When an attacker exploits a privileged container to launch an attack, it does not necessarily involve remote code execution, often unnoticed and risky.
There is no need to use the privileged flag in the second example, and there is no security risk in the mechanism. Therefore, it is recommended to use this one for simple security reasons.
How to run docker in docker in Jenkins?
As it turns out, this can be achieved by writing a setting in dokcer-compose.yml to mount /var/run/docker.sock. The following is an example of the volumes part.
How to run docker in docker in GitLab?
As it turns out, one way to do this is to use the local system for the configuration volume that will be mounted in the gitlab-runner container; the official GItLab documentation gives the following as an example of a startup command.
docker run -d --name gitlab-runner --restart always \
-v /srv/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
See the GitLab documentation for details.