--- tags: [CI/CD, Docker, DooD, Deployment] 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,怎么发命令?** ```mermaid 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。 ```yaml # .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 通信。 ```yaml 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 挂载: ```bash # 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@宿主机 ``` 攻击者只需一行代码就能从容器逃逸到宿主机: ```bash docker run -v /:/host alpine chroot /host sh ``` **结论:仅在完全可信的私有 CI/CD 环境中使用 SID,生产环境或有外部贡献者的项目必须用 DooD。** ## 方案对比总结 ```mermaid 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]]