Docker in Docker (DinD) 实践

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

特性DinDDooD
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 容器可以访问宿主机的所有容器,这在多用户环境下是不安全的,用户可以看到其他用户的容器,甚至可以删除其他用户的容器。


广告:

THE END
喜欢就支持一下吧
点赞10打赏 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容