在构建 CI/CD 平台、发布系统或容器管理工具时,经常会看到如下配置:
plain
volumes:
- /var/run/docker.sock:/var/run/docker.sock
这个配置看似简单,却涉及 Docker 的核心通信机制。本文从底层原理出发,系统梳理:
- docker.sock 的作用
- 容器如何控制宿主机 Docker
- CLI 与 SDK 两种调用方式的区别
- DoD 与 DinD 架构差异
- 生产环境需要关注的安全问题
一、docker.sock 的本质
在 Linux 系统中,Docker Daemon 通过 Unix Socket 提供 API 服务:
plain
/var/run/docker.sock
Docker CLI(docker 命令)并不直接管理容器,它只是一个客户端工具。
当执行:
plain
docker ps
实际发生的是:
plain
docker CLI
↓
/var/run/docker.sock
↓
Docker Daemon
↓
返回容器列表
也就是说,真正执行容器操作的是 Docker Daemon,而 CLI 只是 API 调用者。
二、容器挂载 docker.sock 后发生了什么?
当在容器中挂载:
plain
- /var/run/docker.sock:/var/run/docker.sock
意味着容器内部也可以访问宿主机的 Docker API。
此时,在容器内执行:
plain
docker ps
控制的仍然是宿主机的 Docker Daemon,而不是容器内部的 Docker。
这也是很多发布平台和容器管理工具的实现方式。
三、两种常见调用方式
1️⃣ 通过 Docker CLI
示例:
plain
import { exec } from "child_process";
exec("docker ps", (err, stdout) => {
console.log(stdout);
});
特点:
- 需要在容器中安装 docker-cli
- 本质仍然是 CLI 调用 docker.sock
- 错误处理依赖字符串解析
调用链:
plain
Agent
↓
docker CLI
↓
/var/run/docker.sock
↓
Docker Daemon
2️⃣ 通过 Docker SDK(如 dockerode)
示例:
plain
import Docker from "dockerode";
const docker = new Docker({
socketPath: "/var/run/docker.sock",
});
const containers = await docker.listContainers();
调用链:
plain
Agent
↓
Docker SDK
↓
/var/run/docker.sock
↓
Docker Daemon
对比:
| 维度 | CLI 方式 | SDK 方式 |
|---|---|---|
| 是否需要 docker-cli | 是 | 否 |
| 调用方式 | shell | HTTP API |
| 错误处理 | 字符串 | JSON |
| 日志流支持 | 复杂 | 原生支持 |
| 可扩展性 | 一般 | 强 |
两种方式最终控制的都是宿主机 Docker,差异仅在调用层。
四、DoD 与 DinD 的区别
Docker outside of Docker(DoD)
特点:
- 容器内部没有 Docker Daemon
- 通过挂载 docker.sock 控制宿主机 Docker
- 资源消耗低
- 启动速度快
架构模型:
plain
Container (Agent)
↓
/var/run/docker.sock
↓
Host Docker Daemon
常见于:
- Portainer
- Watchtower
Docker in Docker(DinD)
特点:
- 容器内部运行独立 Docker Daemon
- 通常需要 privileged 模式
- 适用于 CI 构建隔离环境
架构模型:
plain
Container
↓
Docker Daemon(容器内部)
↓
子容器
常见于:
- GitLab CI
- Jenkins
五、安全风险分析
挂载 docker.sock 等价于授予容器对宿主机 Docker 的完全控制权。
通过 Docker API 可以:
- 启动特权容器
- 挂载宿主机目录
- 修改网络配置
- 删除任意容器
一旦容器被入侵,宿主机安全将受到影响。
常见缓解方式:
- 不暴露 Agent 至公网
- 使用内网通信
- 使用强认证 Token
- 尽量使用 SDK 而非 shell 执行
六、核心结论
- docker.sock 是 Docker Daemon 的通信入口
- 挂载 docker.sock 后,容器可以直接控制宿主机 Docker
- CLI 与 SDK 本质相同,区别在调用方式
- DoD 是生产环境常见方案
- DinD 适用于需要隔离 Docker 环境的场景
理解 docker.sock 的通信模型,有助于在设计 CI/CD 或容器管理系统时做出正确的架构选择。