在容器里用 docker 命令,是起一个嵌套的 dockerd 还是把宿主的 socket 挂进去?两种做法看到的东西、创建的容器、需要的权限各不相同。这篇用 docker:dind 镜像逐项实测。
不指定存储驱动直接起 dockerd,0.3 秒后就绪:29.9.0 storage=overlayfs cgroup=2 containers=0 images=0 嵌套 dockerd 的启动日志里选存储驱动的行:630Z" level=info msg="loading plugin" id=io.containerd.snapshotter.v1.native type=io.containerd.snapshotter.v1 | Z" level=info msg="loading plugin" id=io.containerd.snapshotter.v1.overlayfs type=io.containerd.snapshotter.v1 | xtension=org.mobyproject.storage.bbolt.v0 origin=builtin providers="[org.mobyproject.extension.storage.kv.v0]" 外层 Docker 的容器数 8;嵌套的 docker ps 看到的容器数:0(嵌套的 docker 看不到外层的任何容器) 嵌套的镜像库一开始是空的,需要自己 load/pull;load 之后:alpine:3.20 嵌套里运行一个容器:2abc8b975abb037fc9f1b1602dc574c4376f62c2dbbe04bb32297669ab9d0de0 inner1 Up Less than a second 外层 docker ps 能看到 inner1 吗?不能(它只存在于嵌套的 dockerd 里) 嵌套容器的主进程在外层容器(dind 容器)里的进程列表中:390 sleep 120
容器里起一个独立的 dockerd,有自己的镜像库和容器,和外层互不可见;镜像要重新 pull 或 load。需要 --privileged,否则 iptables 等操作被拒绝。镜像是 docker:dind(29.9.0),存储驱动在这里是 overlayfs。
docker run -v /var/run/docker.sock:/var/run/docker.sock:退出码 0 这个容器里的 docker ps -q 看到 9 个容器;外层 docker ps -q 是 9 个——看到的是同一个 dockerd 管的所有容器(含 Docker Desktop 里别的项目的) docker info 的 Name / ServerVersion / Driver:docker-desktop 29.8.2 overlayfs 在容器里 docker run 一个 sibling86:退出码 0 外层 docker ps 能看到 sibling86 吗?能(它是外层 dockerd 的兄弟容器,不是子容器)
把宿主的 /var/run/docker.sock 挂进去,容器里的 docker 命令就是在操作外层的 dockerd:看到所有容器,创建的是外层的兄弟容器,共享镜像缓存。不需要 --privileged,但这个容器等于拥有了控制整个 Docker 的能力——它能列出甚至操作别的项目的容器。这里我只做了 ps 和创建删除一个带标签的容器。
不加 --privileged:15 秒内 dockerd 就绪了吗?False;dockerd 日志最后几行:KER: iptables v1.8.13 (nf_tables): Could not fetch rule set generation id: Permission denied (you must be root)\n (exit status 4)" | -wait -t nat -N DOCKER: iptables v1.8.13 (nf_tables): Could not fetch rule set generation id: Permission denied (you must be root) | (exit status 4) 对比:DooD 不加 --privileged 就能用(上一节);DinD 在上一节用了 --privileged,嵌套 dockerd 0.3 秒就绪。
要隔离(CI 里每个任务一个干净的 Docker)用 DinD,代价是特权和冷缓存;要共享缓存、轻量用 DooD,代价是权限过大且容器互相可见。哪种适合,取决于你信任里面的代码到什么程度(本文没有做更多的安全测试)。