Files
cs-note/hzh/CICD/CICD 在 Slidev 中的应用/Docker-in-Container 部署模式详解.md

5.2 KiB
Raw Permalink Blame History

tags, create time
tags create time
CI/CD
Docker
DooD
Deployment
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