5.2 KiB
5.2 KiB
tags, create time
| tags | create time | ||||
|---|---|---|---|---|---|
|
2026-05-27 19:30 |
Docker-in-Container 部署模式详解
概述
当 CI/CD 流水线需要在容器化环境中启动和操控另一个容器时(例如 Gitea Actions 中构建产物后部署 Nginx),有两种主流方案:DooD (Docker-out-of-Docker) 与 SID (Socket-In-Docker)。本文对比两种模式的原理、配置和取舍。
核心问题
Workflow 的 run: 命令在某个容器(如 node:22-alpine)内执行,但你需要指挥宿主机上的 Docker daemon 来操作其他容器。问题是:容器里没有 Docker,怎么发命令?
flowchart LR
subgraph HOST["宿主机"]
DAEMON["Docker Daemon\n(:2375 or /var/run/docker.sock)"]
end
subgraph CONTAINER["workflow 运行容器\n(node:22-alpine)"]
CLI["docker-cli ?"]
end
DAEMON <-->|通信方式见下文| CLI
style DAEMON fill:#e8f4fd
style CONTAINER fill:#fff4e8
方案一:DooD — Docker-out-of-Docker
原理
在容器内部安装完整的 docker CLI 包,通过某种方式连接到宿主机的 Docker daemon。
# .github/workflows/example.yml
steps:
- name: Install Docker CLI
run: |
sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
apk add --no-cache docker-cli
- name: Build and Deploy
run: |
docker build -t myapp .
docker run --name web nginx:alpine
docker cp dist/. web:/usr/share/nginx/html/
连接宿主机 daemon 的方式通常有两种:
| 方式 | 实现 | 特点 |
|---|---|---|
| 环境变量 + TCP | 宿主机暴露 DOCKER_HOST=tcp://host.docker.internal:2375 |
需要开放端口,可能有安全风险 |
| Socket 共享 + CLI 并存 | 挂载 /var/run/docker.sock 且容器内装了 docker-cli |
最常用,兼容性好 |
优点
- 功能完整 — 容器内有完整的 docker CLI,任何
docker命令都能用 - 调试友好 — 可以直接
docker exec进入其他容器查日志 - 无需额外配置 — runner 内置支持或简单环境变量即可
缺点
- 安装体积大 — docker-cli 本身约 40-60MB( Alpine 版本)
- 权限较宽 — 通常需要加入
docker用户组才能免 sudo 操作 - 安全面更大 — 容器内能直接操控宿主机所有容器和资源
方案二:SID — Socket-In-Docker
原理
不安装任何 CLI,直接把宿主机的 Docker socket 文件挂载进容器。容器内的进程通过这个 socket 文件和宿主机 daemon 通信。
steps:
- name: Deploy with socket mount
# 关键:runner 配置中将 docker.sock 挂载进工作容器
env:
DOCKER_BUILDKIT: 1
run: |
# 容器内不需要 docker,只需把 docker socket 挂载进来
docker run -d --name web nginx:alpine # 直接用!
docker cp dist/. web:/usr/share/nginx/html/
在 Gitea Actions Runner 层面,需要通过环境变量或配置文件将 socket 挂载:
# Runner 启动时的挂载参数
docker run \
--volume /var/run/docker.sock:/var/run/docker.sock \
node:22-alpine \
pnpm build && docker run ...
优点
- 镜像更轻 — 容器内不需要安装 docker-cli,节省空间和时间
- 资源占用少 — 无额外二进制、无额外进程
- 简洁干净 — workflow 文件看起来更像"普通脚本"
缺点
- 调试困难 — 没有独立 CLI,出错时排查路径更长
- 安全性极低 — 这是致命缺陷。拥有
/var/run/docker.sock等价于拥有 root 权限(可轻易逃逸到宿主机) - 兼容性限制 — 某些需要完整 CLI 特性的操作可能不支持
⚠️ SID 安全警告
mount /var/run/docker.sock → 容器内进程 = root@宿主机
攻击者只需一行代码就能从容器逃逸到宿主机:
docker run -v /:/host alpine chroot /host sh
结论:仅在完全可信的私有 CI/CD 环境中使用 SID,生产环境或有外部贡献者的项目必须用 DooD。
方案对比总结
quadrantChart
title Docker-in-Container 方案对比
x-axis "低安全性" --> "高安全性"
y-axis "轻量便捷" --> "功能强大"
SID: [0.2, 0.8]
"DooD (socket+CLI)": [0.6, 0.9]
DinD (Daemon inside): [0.9, 0.4]
Kaniko: [0.95, 0.3]
| 维度 | DooD (CLI) | SID (Socket Only) | DinD (Daemon Inside) | Kaniko |
|---|---|---|---|---|
| 镜像大小 | ~60MB 额外 | +0MB | ~80MB 额外 | 自包含 |
| 调试体验 | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ |
| 安全性 | ⭐⭐⭐ | ⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 配置复杂度 | 低 | 最低 | 高 | 低 |
| 适用场景 | 通用推荐 | 纯可信私有环境 | 需要完整 Docker 特性 | 仅构建镜像 |
选择建议
| 你的场景 | 推荐方案 |
|---|---|
| Slidev 部署(当前项目) | DooD,简单够用 |
| 多人在跑 CI 的开源项目 | DinD 或 Kaniko |
| 极致追求镜像体积的私有环境 | SID(但要评估风险) |
| 只需要构建镜像不部署 | Kaniko(无需 docker daemon) |
关联笔记
- CICD 在 Slidev 中的应用
- deployment-strategy
- workflow-configuration