Docker内部深度探讨:当你运行 docker run 时到底发生了什么(2025 版)
Source: Dev.to
(未提供需要翻译的正文内容。如需翻译,请提供完整的文本。)
现代容器平台概览
现代容器平台依赖于可预测、模块化的行为。Docker 的架构是围绕标准接口 — REST、gRPC、OCI Runtime 和 Linux‑kernel 原语构建的分层执行管道。了解此流程可消除在调试、扩展或与编排系统集成时的歧义。
1. 核心架构
CLI → dockerd (API + Orchestration) → containerd (Runtime mgmt)
→ containerd‑shim (Process supervisor) → runc (OCI runtime)
→ Linux Kernel (Namespaces, cgroups, fs, net)
Docker CLI
- 面向用户的命令界面
- 将标志转换为 JSON
- 通过
/var/run/docker.sock与 dockerd 通信
dockerd
- REST API 服务器
- 容器生命周期编排
- 网络和卷管理
- 将镜像和运行时操作委派给 containerd
containerd
- 高级运行时管理器
- 管理快照、镜像和内容存储
- 拉取/解压层并创建 OCI 运行时规范
- 为每个容器启动一个
containerd‑shim
镜像存储细节
- 每个层通过 SHA‑256 进行内容寻址
- 相同的层会去重
- OverlayFS 使用硬链接,层在容器之间共享
containerd‑shim
- 容器工作负载的父进程
- 在 dockerd/
containerd重启时保持容器存活 - 管理 I/O 流(日志、附加)
- 将退出码返回给
containerd
runc
- 实现 OCI 运行时规范
- 创建命名空间、应用 cgroup 限制、挂载根文件系统并执行入口点
- 在容器创建后立即退出(shim 仍保持存活)
Linux 内核
- 强制进程隔离(命名空间)
- 控制资源(cgroups)
- 提供分层文件系统(OverlayFS)
- 处理网络(veth 对、桥接、iptables/NAT)
✈️ 机场类比 – 思维模型
| Docker Component | Airport Role | Real‑World Impact |
|---|---|---|
| Docker CLI | 乘客航站楼 | 你输入 docker run,检查状态 |
| dockerd | 机场运营中心 | 管理所有航班、登机口和时刻表 |
| containerd | 地面指挥 | 装载行李(镜像),分配跑道 |
| containerd‑shim | 登机口代理 | 即使运营中心重启,也确保飞机保持就绪 |
| runc | 机师 | 真正驾驶飞机(执行容器) |
| Kernel | 空中交通管制 | 管理空域(资源),防止冲突 |
| Container | 实际航班 | 你的应用在隔离的空域中运行 |
使用此模型在故障排除时记住组件之间的关系。
2. 执行流程:docker run -d -p 8080:80 nginx
| 步骤 | 描述 |
|---|---|
| 1. CLI → dockerd | CLI 解析命令,构建 JSON 负载,并通过 Unix 套接字发送。 |
| 2. dockerd Validation | 验证配置,检查本地镜像,并协调容器创建。 |
| 3. Image Pull (if needed) | containerd 处理注册表认证、清单解析、层下载与校验,并将层存储在内容存储中。 |
| 4. Filesystem Assembly | containerd 准备快照,创建 OverlayFS 的上层/下层布局,并构建包含元数据和运行时配置的 OCI 包。 |
| 5. Networking Setup | dockerd 配置网络命名空间:• 创建 veth 对(主机端连接到 docker0)• 分配容器 IP(例如 172.17.0.2)• 为端口映射添加 iptables DNAT 规则 • 为出站流量添加 MASQUERADE 规则 |
| 6. containerd → containerd‑shim | containerd 启动 shim,交付 OCI 规范,并委托生命周期监督。 |
| 7. shim → runc | runc 创建命名空间,挂载 rootfs,应用 cgroup 限制,执行容器入口点,然后退出(shim 保持运行)。 |
| 8. Container Running | 容器作为隔离的 Linux 进程运行: • Shim 维护生命周期 • dockerd 流式传输日志并报告状态• 内核强制隔离 |

3. 组件职责
| 组件 | 角色 | 委派给 |
|---|---|---|
| CLI | 用户界面,请求创建 | dockerd |
| dockerd | API、编排、网络 | containerd |
| containerd | 镜像管理、快照、生命周期 | runc |
| containerd‑shim | 监督容器进程 | 内核(通过 runc 创建的命名空间) |
| runc | 创建容器环境 | 内核 |
| Kernel | 隔离 + 资源控制 | 硬件 |
相关架构(Kubernetes)
dockerd → kubelet → CRI → containerd
下游的所有部分(containerd → shim → runc → kernel)保持不变。
4. 关键说明
- 容器是 进程,而不是虚拟机。
runc不会 常驻;shim 管理容器的生命周期。- Docker 的分层文件系统是 写时复制,实现高效存储。
- Kubernetes 移除了
dockerd,直接与containerd通信,以实现更简洁的 CRI 流程。 - 实时恢复能够工作是因为 shim 将容器与
dockerd解耦。
Source: …
5. 调试指南(运维版)
一套结构化、分层的序列,用于诊断容器故障。面向 SRE、DevOps 和运行时工程团队。
容器立即退出
方法: 从最高层(应用层)到最低层(内核层)逐层排查。
1. 应用层
严重程度:低 – 大多数故障源于此。
docker logs <container>
检查日志中是否有崩溃、缺失的二进制文件、入口点配置错误等信息。
2. Shim / Runtime 层
- 验证 shim 是否存活:
ps -ef | grep containerd-shim - 检查
runc退出状态:docker inspect --format='{{.State.ExitCode}}' <container>
3. Containerd 层
- 查看 containerd 日志:
journalctl -u containerd,关注快照或 OCI 规范错误。
4. Dockerd 层
- 检查 Docker 守护进程日志:
journalctl -u docker,寻找 API 级别的拒绝或网络设置失败。
5. 内核层
- 确认命名空间创建情况:
lsns或ip netns list - 检查 cgroup 限制:
cat /sys/fs/cgroup/.../memory.max
使用此分层检查清单定位故障发生的具体阶段,然后采取相应的修复措施。
调试 Docker 运行时问题
关注点: 运行时异常、崩溃循环、缺失配置、入口点失败。
运行时层(containerd / OCI)
严重程度:中 – 问题影响容器创建,而非应用逻辑。
journalctl -u containerd
可检测到:
- 无效的 OCI 规范
- 快照 / 解压错误
- 权限问题
- 镜像元数据失败
内核层
严重程度:高 – 内核故障会影响节点上 所有 容器。
dmesg | tail -20
揭示:
- 命名空间创建失败
- cgroup 强制执行错误
- LSM 阻断(AppArmor / SELinux)
- OverlayFS 挂载问题
容器启动缓慢
定位延迟发生在注册表、存储还是运行时。
镜像拉取 / 解压延迟
journalctl -u containerd --since "2 minutes ago" | grep -Ei "pull|unpack"
发现远程拉取慢、层解压延迟、解压缩问题等。
主机存储瓶颈
iostat -dx 1 /var/lib/containerd
检测:
- 高 I/O 等待
- OverlayFS 底层存储饱和
- 磁盘慢或卷负载过高
注册表 / 网络慢
time docker pull alpine:latest
测量:
- 往返延迟
- 下载吞吐量
- 注册表认证或代理延迟
网络问题
追踪 主机 → 桥接 → 容器 的连通性。
验证 NAT / 端口转发规则
sudo iptables -t nat -L DOCKER -n -v
桥接与 veth 拓扑
ip addr show docker0
brctl show
容器命名空间网络
docker exec <container> ip addr show
常见错误模式
| 错误信息 | 可能原因 |
|---|---|
no such file or directory | 缺失入口点或工作目录错误 |
permission denied | 用户命名空间限制、卷权限问题 |
address already in use | 主机端口冲突 |
exec format error | 架构不匹配(如 amd64 与 arm64) |
layer does not exist | 镜像存储损坏、拉取不完整 |
failed to setup network namespace | 内核缺少所需能力 |
恢复操作
镜像拉取失败
- 检查注册表身份验证令牌。
- 验证代理 / SSL 配置。
- 测试与注册表端点的连通性。
OCI 规范 / 运行时错误
- 确保 Docker、containerd 和 runc 版本兼容。
- 验证自定义 seccomp 或 AppArmor 配置文件。
- 重新创建损坏的快照。
内核命名空间 / Cgroup 失败
- 确认内核版本支持所需功能。
- 验证 cgroup v1 与 v2 模式。
- 检查影响命名空间的
sysctl覆盖。

6. 摘要
一次 docker run 调用会沿着一条有纪律、模块化的执行路径进行。每个组件只承担一小块、定义明确的职责,并干净利落地交给下一个组件,从而形成可预测的控制流:
- Dockerd 解析意图并将其转换为运行时指令。
- Containerd 通过稳定的 gRPC API 编排容器生命周期。
- containerd‑shim 将容器的进程管理与守护进程的重启隔离开来。
- runc 将 OCI Runtime Spec 实现为 Linux 原语。
- 内核 通过命名空间、cgroup 和文件系统驱动提供最终的强制执行层。
这些边界由开放标准(REST → gRPC → OCI Spec → 系统调用)治理,确保兼容性、可靠性以及跨层的深度可观测性。隔离、资源治理和性能效率直接源自原生 Linux 构造——没有隐藏的 hypervisor,也没有额外的抽象层。
操作说明
由于进程所有权被委托给 containerd‑shim,dockerd 和 containerd 都可以在不影响正在运行的容器的情况下重启。该设计支持安全的守护进程升级、节点维护以及 不会中断工作负载 的高可用工作流。
快速参考
- 核心架构 – 执行流 → 组件职责
- 关键说明 – 调试指南(运维版)
- 调试树 – 容器立即退出 → 启动缓慢 → 网络问题 → 常见错误模式 → 恢复操作
- 摘要 – 模块化栈的高级回顾