Docker in Docker

Docker in Docker (DinD) 深入解析

Docker 技术在现代软件开发中已经成为一种标准,尤其是在微服务架构和持续集成/持续部署(CI/CD)流程中。随着 Docker 的普及,开发者们开始探索在容器内部运行 Docker 的能力,这种技术被称为 Docker in Docker(DinD)。

什么是 Docker in Docker?

Docker in Docker 是指在一个 Docker 容器内部运行 Docker 守护进程的技术。这意味着你可以在一个容器中创建、管理和操作其他 Docker 容器。这种模式通常用于 CI/CD 流程中,允许在隔离环境中构建和测试应用程序。

DinD 的实现方式

1. 直接挂载 Docker.sock

最简单的实现方式是将宿主机的 Docker 套接字(/var/run/docker.sock)挂载到容器中,这样容器内的应用可以直接与宿主机的 Docker 守护进程进行交互。

验证

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
root@devops:~# docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock docker:latest sh
/ # docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete
Digest: sha256:1408fec50309afee38f3535383f5b09419e6dc0925bc69891e79d84cc4cdcec6
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/get-started/

/ #

这样启动的 hello-world 会再宿主机上真实的启动一个容器

1
2
3
4
5
6
7

root@devops:~/poc# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
174757c01f08 hello-world "/hello" About a minute ago Exited (0) 59 seconds ago inspiring_morse
c0d09eee09a6 docker:latest "dockerd-entrypoint.…" About a minute ago Up About a minute 2375-2376/tcp sharp_dubinsky
e244d45174b3 dockerhub.qingcloud.com/doubao/rancher:latest "entrypoint.sh" 2 weeks ago Up 39 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp rancher
root@devops:~/poc#

优点

  • 简单性:设置和使用非常简单,快速上手。
  • 性能:直接使用宿主机的 Docker 守护进程,避免了性能开销。

缺点

  • 安全风险:容器内应用拥有对宿主机 Docker 的完全控制,存在安全隐患。
  • 环境污染:容器内的操作可能影响宿主机上的 Docker 环境,导致不一致性。

2. 使用 Docker 的 DinD 镜像

Docker 官方提供了一个名为 docker:dind 的镜像,专门用于在容器中运行 Docker。这种方式允许你在容器内启动一个独立的 Docker 守护进程。

验证

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
root@devops:~#  docker run --privileged  --name dind-container -d docker:latest
8cecade0bdc3e3a8de171caaf5706253bdc7920473512692a5f5ea6610725ef2
root@devops:~# docker exec -it dind-container sh
/ # docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete
Digest: sha256:1408fec50309afee38f3535383f5b09419e6dc0925bc69891e79d84cc4cdcec6
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/get-started/

/ # docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1253974190f1 hello-world "/hello" About a minute ago Exited (0) About a minute ago thirsty_mclean
/ #

这样启动的 hello-world 只会在容器内启动,宿主机上是没有 hello-world 的容器。

1
2
3
4
5
root@devops:~/poc# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8cecade0bdc3 docker:latest "dockerd-entrypoint.…" About a minute ago Up About a minute 2375-2376/tcp dind-container
e244d45174b3 dockerhub.qingcloud.com/doubao/rancher:latest "entrypoint.sh" 2 weeks ago Up 56 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp rancher
root@devops:~/poc#

踩坑

第一种 DinD 的启动方式,是直接挂载 sock,然后直接 docker run -it 容器 sh 就直接进入到容器中了,就可以直接操作容器内部的 docker。

第二种 DinD 的启动方式,是没有挂载 sock,所以不可以直接 docker run -it 容器 sh,这样进入到容器中,docker 的 sock 还没有完全启动,所以需要先 docker run 启动容器,然后再 docker exec 进入容器,然后就可以操作容器内的 docker 了。

优点

  • 官方支持:由 Docker 官方维护,确保兼容性和稳定性。
  • 隔离性:每个 DinD 容器都有自己的 Docker 守护进程,彼此之间完全隔离。

缺点

  • 性能开销:由于在容器内管理 Docker,可能会引入额外的性能开销。
  • 复杂性:需要配置网络和存储,以确保容器间的通信。

3. 使用 Sysbox

Sysbox 是一个增强型容器运行时,允许你在容器中运行完整的系统服务,包括 Docker。与直接挂载 Docker.sock 相比,Sysbox 提供了更好的隔离性。

验证

由于需要更改 docker 的 runtime,由于现在的默认 runtimes 是 runc,更改成 sysbox 有挑战。目前没有技术知识,等后续。。。

优点

  • 增强的隔离性:容器内的操作不会影响宿主机 Docker 环境。
  • 支持复杂应用:可以在容器中运行更复杂的应用程序和服务。

缺点

  • 安装复杂性:需要额外的步骤来安装和配置 Sysbox。
  • 性能开销:可能引入额外的性能开销。