Docker in Docker(DinD)是一种在 Docker 容器内部运行 Docker 守护进程的技术。本章节将带你深入了解 DinD 的概念、使用场景和实际应用。
随着容器化技术在 CI/CD 流水线和云原生开发中的广泛应用,开发团队面临着一个核心挑战: 如何在已经容器化的环境中安全地执行 Docker 操作, 比如在运行于容器中的 Jenkins 或 GitLab CI 中构建和推送 Docker 镜像, 或者为开发者提供隔离的 Docker 环境而不影响宿主机。 为了解决这个"容器中的容器"问题,社区发展出了两种主要技术路线: DinD(Docker in Docker)通过在容器内运行完整的 Docker 守护进程实现真正的隔离,适合需要完全独立环境的场景; 而 DooD(Docker outside of Docker)则通过挂载宿主机的 Docker socket 来复用宿主机的 Docker 环境,在保持简单性的同时牺牲了部分隔离性。
DinD vs DooD
| 特性 | DinD | DooD |
|---|---|---|
| Docker Daemon | 容器内独立运行 | 使用宿主机的 |
| 隔离性 | 完全隔离 | 共享宿主机环境 |
| 特权要求 | 需要 --privileged | 只需挂载 socket |
使用场景
1. 持续集成/持续部署(云原生构建)
- CI 控制器(如 Jenkins、GitLab CI)运行在容器中
- 为每个构建任务提供独立的 Docker 环境
2. 开发/测试环境隔离(云原生开发)
- 开发人员可以在容器内部进行 Docker 操作
- 不影响本地主机环境
实践一:DinD 实战案例
Docker in Docker 架构图
┌─────────────────────────────────────────────────────────────┐
│ 宿主机环境 │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ DinD 容器 │ │
│ │ │ │
│ │ ┌─────────────┐ ┌───────────────────┐ │ │
│ │ │ Docker CLI │ API调用 │ Docker Daemon │ │ │
│ │ │ 客户端 │ ──────────→ │ 守护进程 │ │ │
│ │ └─────────────┘ │ │ │ │
│ │ │ ┌─────────────┐ │ │ │
│ │ │ │ App 容器1 │ │ │ │
│ │ │ ├─────────────┤ │ │ │
│ │ │ │ App 容器2 │ │ │ │
│ │ │ ├─────────────┤ │ │ │
│ │ │ │ App 容器3 │ │ │ │
│ │ │ └─────────────┘ │ │ │
│ │ └───────────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
说明:
- 外层框:宿主机环境
- 中层框:DinD 容器(运行在特权模式下)
- 内层:Docker CLI 与 Docker Daemon 通过 API 通信
- 最内层:由 Docker Daemon 管理的应用容器
如上图,可以在 Container 中直接运行一个 Docker Daemon , 然后使用 Container 中的 Docker CLI 工具操作容器, 这种方式下,容器中的 Docker Daemon 完全独立于外部,具有良好的隔离特性。
步骤 1:启动 DinD 容器
docker:dind 是 Docker 官方提供的专门用于 Docker in Docker 场景的镜像。它基于 Alpine Linux 构建,包含了完整的 Docker 守护进程和客户端工具。
注意:在使用 DinD 时,需要使用 --privileged 参数来启动容器, 因此如下实验不能在 cnb 环境中进行,需要在本地环境中进行
# 启动一个 DinD 容器作为 Docker 守护进程
docker run --privileged --name dind-daemon -d docker:dind
步骤 2:在容器内操作 Docker
# 查看 Docker 版本
docker version
# 拉取镜像
docker pull hello-world
# 运行容器
docker run hello-world
# 查看镜像列表
docker images
# 查看容器列表
docker ps -a
步骤 3:在 DinD 容器中构建镜像
先使用 Bind Mounts 将当前目录的 app 目录挂载到 DinD 容器的 /app 目录下,然后在 DinD 容器中执行 docker build 命令构建镜像。
docker run --privileged -v $(pwd)/app:/app --name dind-daemon -d docker:dind
然后在 DooD 容器中构建镜像并运行。
docker build -t app:dind .
docker run -it -d --name app-dind app:dind
dind 的缺点
dind 由于需要 --privileged 参数来启动容器,因为无法被直接应用在 ci 或者 云端开发环境的场景中,比如,可以在容器内挂载宿主机根目录开访问宿主机的所有文件。
实践二:DooD(Docker outside of Docker)实战案例
Docker outside of Docker 架构图
Docker outside of Docker
┌─────────────────────────────────────────────────────────────┐
│ 宿主机环境 │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Docker 容器 │ │
│ │ │ │
│ │ ┌─────────────┐ │ │
│ │ │ Docker CLI │ │ │
│ │ │ 客户端 │ │ │
│ │ └─────────────┘ │ │
│ │ │ │ │
│ │ │ Request │ │
│ │ ▼ │ │
│ │ /var/run/docker.sock ────────────────────────────────┼──┐
│ │ │ Mount │ │
│ └─────────┼───────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Docker Daemon │ │
│ │ 守护进程 │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ 容器 1 │ │ 容器 2 │ │ 容器 3 │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
说明:
- 外层框:宿主机环境
- 上层容器:运行 Docker CLI 的容器
- 下层:宿主机的 Docker Daemon
- Socket 挂载:通过 /var/run/docker.sock 实现通信
- 容器管理:所有容器都由宿主机的 Docker Daemon 管理
如上图,Docker 以 C/S 模式工作,使用时用户关注的是 C 端, 而生命周期的管理在 S 端,因此,只需要将 Container 的外部 Docker Daemon 服务挂载到 Container 。让 Container 误以为本地运行了 Docker Daemon, 使用 Docker CLI 命令操作时,外部的 Docker Daemon 会响应请求。
步骤 1:启动 DooD 容器
# 启动容器并挂载 Docker 套接字
docker run -it \
-v /var/run/docker.sock:/var/run/docker.sock \
--name dood-client \
docker:latest sh
步骤 2:在客户端容器内操作 Docker
# 查看 Docker 版本
docker version
# 拉取镜像
docker pull hello-world
# 运行容器
docker run hello-world
# 查看镜像列表
docker images
# 查看容器列表
docker ps -a
步骤 3:在 DooD 容器中构建镜像
先使用 Bind Mounts 将当前目录的 app 目录挂载到 DooD 容器的 /app 目录下,然后在 DooD 容器中执行 docker build 命令构建镜像。
docker run -it \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(pwd)/app:/app \
docker:latest sh
然后在 DooD 容器中构建镜像并运行。
docker build -t app:dood .
docker run -it -d -p 3000:3000 --name app-dood app:dood
dood 的缺点
dood 由于是直接挂载宿主机的 socket 文件,因此,dood 容器可以访问宿主机的所有容器,这在多用户环境下是不安全的,用户可以看到其他用户的容器,甚至可以删除其他用户的容器。
广告:











暂无评论内容