docker组件拆分解读
Docker 在近几年做的一次关键架构拆分:把原来全堆在一个进程里的容器管理逻辑,拆成了分层、可独立升级的结构,从而避免“一升级 Docker,所有容器一起抖三抖”的历史问题。

早期 Docker 的问题是:
Docker Daemon 自己既管镜像、又管容器、还负责启动进程。
结果就是:
- Docker 守护进程挂了,容器跟着出问题
- 升级 Docker,可能影响正在运行的容器
- 架构耦合严重,难以扩展
于是 Docker 做了关键一步:把容器运行时剥离出去。
containerd:中间层容器管理者
Docker 把真正负责容器生命周期的部分拆出来,变成独立项目 containerd。
containerd 的职责:
- 管理容器生命周期(创建、运行、暂停、删除)
- 管理镜像
- 管理 snapshot
- 与底层 runtime 通信
Docker Daemon 不再直接控制容器,而是通过 gRPC 调用 containerd。
containerd 后来被捐赠给了 Cloud Native Computing Foundation,成为云原生核心基础设施之一。
现在 Kubernetes、Docker 等都可以直接用 containerd。
containerd-shim:每个容器一个“保姆进程”
图中下面这一层:
每个容器都会有一个:
containerd-shim
它的存在很巧妙。
作用是:
- 让容器进程 脱离 containerd 父子关系
- containerd 重启时容器不会死
- 接管容器 IO 和退出状态
也就是说:
containerd 死了
↓
shim 还活着
↓
容器继续运行
所以现在:
- 升级 Docker
- 重启 containerd
容器仍然继续跑。
runc:真正创建容器的执行者
再往下是:
runc
这是符合 OCI 标准的 runtime。
它干的事情非常“底层”:
- 设置 namespace(隔离)
- 设置 cgroups(资源限制)
- 挂载 rootfs
- 启动容器进程
换句话说:
runc = 用 Linux 内核功能真正启动容器
runc 项目来自 Open Container Initiative,保证不同 runtime 兼容。
总结
当你执行:
1 | docker run nginx |
真实链路变成:
docker cli
↓
Docker Engine
↓ gRPC
containerd
↓
containerd-shim
↓
runc
↓
Linux namespace + cgroup
↓
容器进程启动
简化架构图
flowchart TB
A[Docker CLI] --> B[Docker Engine]
B --> C[containerd]
C --> D1[containerd-shim]
C --> D2[containerd-shim]
D1 --> E1[runc]
D2 --> E2[runc]
E1 --> F1[Container Process]
E2 --> F2[Container Process]
架构升级真正解决了什么?
核心收益有三点:
- Docker 重启不再影响容器
- runtime 可以独立升级
- Kubernetes 等系统可以绕过 Docker 直接用 containerd
这也就是为什么 Kubernetes 后来宣布 弃用 dockershim,直接对接 containerd。
现在服务器上经常能看到:
containerd
containerd-shim-runc-v2
runc
一台机器几十上百个 shim 进程其实很正常——一个容器一个 shim。
当你看到 containerd 崩了但容器还在跑,不是 bug,而正是这个架构设计的成功。