docker架构设计
以下是基于当前技术实现的 Docker 架构原理图(适配 Linux 系统,2024年最新组件关系),配合核心组件详解:
Docker架构设计剖析
Docker 架构图(文字描述 + 伪代码结构)
1 | graph TD |
核心组件功能说明
1. Docker Client/CLI
1 | # 用户命令行操作(例如创建容器) |
- 作用:用户与 Docker 交互的入口
- 通信方式:通过
/var/run/docker.sock
UNIX socket 或 TCP 端口连接 dockerd
2. Docker Daemon (dockerd)
1 | flowchart TB |
- 核心职责:
- 提供 REST API 服务端 (监听
/var/run/docker.sock
) - 管理 Docker 对象生命周期:镜像/容器/网络/卷
- 插件体系管理:网络驱动(CNM)、存储驱动、日志驱动
- 用户命令行接口(CLI)的后端执行器
- 提供 REST API 服务端 (监听
3. containerd (核心容器运行时)
角色:容器生命周期总控中心
功能模块 | 说明 |
---|---|
任务分发 | 接收dockerd指令,调度容器操作 |
镜像管理 | 从仓库拉取镜像,管理本地存储(/var/lib/containerd) |
存储插件 | 通过Snapshotter管理文件系统快照(支持overlayfs、zfs等) |
事件监听 | 暴露容器状态变更事件(gRPC接口) |
指标采集 | 收集cgroups资源使用数据 |
1 | flowchart TD |
核心职责:
- 容器生命周期管理:
create/start/stop/delete
- 镜像全周期管理:
pull/push/build
- 关键组件:
- Content Store:存储镜像层 blob(
/var/lib/containerd/io.containerd.content.v1.content
) - Snapshotter:构建容器读写层(支持 overlayfs、zfs 等)
- Diff Service:计算镜像层差异
- Content Store:存储镜像层 blob(
- 关键组件:
- 存储快照管理:(操作
/var/lib/containerd
)- 特性支持:
volume-driver
插件扩展(nfs、ceph)- Volume 生命周期管理(create/remove)
- SELinux 上下文自动标记
- 特性支持:
- 网络资源编排
- 实现路径:
- 调用 CNI 插件创建网卡
- 通过 PublishPort 暴露端口
- 整合网络策略(如 Calico、Flannel)
- 实现路径:
- 实现 CRI (Container Runtime Interface) 标准
- 容器生命周期管理:
关键路径:
/var/lib/containerd
通信协议:gRPC(高性能二进制通信)
4. containerd-shim (隔离层)
角色:容器进程守护代理
- 核心职责:
- 进程托管:成为容器进程的实际父进程
- 信号中继:转发 SIGTERM/SIGKILL 等信号
- IO 传递:连接容器 stdio 和日志驱动
- 状态维持:保留退出容器状态直至被清理
存在意义:解决父进程管理和守护进程解耦问题
1 | flowchart LR |
关键机制:
- 每启动一个容器生成独立 shim 进程
- 转发 IO 流(STDIN/STDOUT/STDERR 到 containerd 日志驱动)
关键特征
1
2
3# 每个容器对应独立 shim 进程
$ pstree -p | grep shim
containerd(1001)───containerd-shim(2031)───nginx(2040)
5. runC:轻量级容器执行引擎
角色:OCI 标准实现者
- 核心职责:
- 实现 OCI 运行时规范 (runtime-spec)
- 调用 Linux 内核接口创建容器环境:
- 命名空间隔离 (namespace)
- 资源限制 (cgroups v2)
- 文件系统挂载 (OverlayFS)
- 安全策略 (AppArmor/seccomp)
- 执行容器启动进程 (ENTRYPOINT/CMD)
内核交互原理:
1 | sequenceDiagram |
OCI Spec 实现:解析
config.json
创建符合规范的容器,影响调用顺序的关键配置文件runc
的执行步骤由config.json
决定(通过runc spec
生成),核心顺序锁定在:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15{
"process": {
"args": ["/bin/sh"],
"user": {"uid": 0, "gid": 0}, // Step6: 权限设置
"capabilities": {...} // Step3: 能力集
},
"root": {"path": "rootfs"}, // Step2: 根文件系统
"mounts": [...], // Step2: 挂载点
"linux": {
"namespaces": [...], // Step1: 命名空间
"seccomp": {...}, // Step3: Seccomp规则
"cgroupsPath": "container01" // Step2: Cgroups路径
}
}必须:文件系统(Mount) → 主机标识(UTS) → 进程间通信(IPC) → 进程树(PID) → 网络(Network) → 用户权限(User)
关键操作:
runc create
→runc start
(分离容器初始化与启动)
6. Linux 内核核心组件
技术 | 作用 | 容器中查看命令 |
---|---|---|
Namespaces | 资源隔离 | ls -l /proc/$$/ns |
Cgroups v2 | 资源限制 | cat /sys/fs/cgroup/memory.max |
OverlayFS | 分层文件系统 | `mount |
Netfilter | NAT/端口映射 | iptables -t nat -vnL DOCKER |
全流程示例:创建容器时发生了什么?
1 | sequenceDiagram |
架构演进关键点(2024更新)
去 Docker 单点化:
1
2
3
4timeline
2017年: docker-containerd 插件
2021年: containerd 独立运行时
现在: K8s 可直接调用 containerd(绕过 daemon)安全增强:
- Rootless Mode:无 root 权限运行
- Seccomp/AppArmor:强制访问控制
1
docker run --security-opt seccomp=/profile.json ...
问题剖析
问题一:dockerd 与 containerd 在容器/镜像管理上的本质区别
职责边界对比(关键差异表)
功能维度 | dockerd 职责 | containerd 职责 |
---|---|---|
镜像管理 | • 解析 Dockerfile • 镜像标签语义 • 多架构镜像分发策略 |
• 镜像分片(blobs)存储 • 内容寻址(SHA256) • 快照磁盘驱动管理 |
容器管理 | • 端口映射规则 • 卷绑定挂载路径 • 容器别名设置 |
• 容器运行时配置(OCI spec) • Linux namespace/cgroups 构建 • 容器状态机转换 |
通信协议 | REST API (HTTP/JSON) | gRPC (高性能二进制协议) |
数据存储位置 | /var/lib/docker/ | /var/lib/containerd/ |
核心价值差异 | 用户友好抽象层 | 标准化运行时控制层 |
▶ 实例解析:docker pull nginx 的执行流
1 | sequenceDiagram |
问题二:containerd-shim 如何实现运行时解耦
1. 关键机制:双进程孤儿化保护
1 | graph BT |
- 进程树原理:
- containerd 调用
fork()
生成 containerd-shim 子进程 - shim 进程调用
runc create
创建容器进程 - shim 进程脱离父进程管控:
1
2
3
4
5
6
7// 伪代码:shim 自孤儿化
if (fork() == 0) { // shim 子进程创建容器
daemon(0, 0); // 脱离终端
pid = runc_create("nginx");
} else {
exit(0); // 父进程终止→shim成孤儿归init托管
}
- containerd 调用
- 结果:shim 进程被 Linux init 进程(PID1)接管,与 containerd 解除父子关系
2. 通信解耦:Unix Socket 长连接
1 | flowchart LR |
- socket 路径:
/run/containerd/shim.{version}.sock
- 存活凭证:即使 containerd 重启, socket 文件保留 → 新 containerd 通过路径自动重连
containerd和containerd-shim的差异:
角色 | 技术身份 | 操作模式 |
---|---|---|
containerd | gRPC 客户端 | 发起控制请求 |
containerd-shim | gRPC 服务端 | 监听并响应 |
containerd-shim 和 containerd 通过 Socket 的分工协作,实现了:
- 稳定与控制分离:shim 作为状态锚点护卫容器进程
- 弹性能力:containerd 可热升级/重启不影响业务
- 安全边界:Unix Socket 比 TCP 减少 40% 的安全攻击面
3. 核心场景:containerd 重启时的容器保护流程
1 | sequenceDiagram |
4. 关键进程状态验证
1 | # 重启 containerd 前后的进程树对比 |
深度技术原理剖析
为何容器进程不会成为僵尸进程?
- shim 的 waitpid 机制:
1
2
3
4
5
6
7
8// containerd-shim 核心处理循环
for {
status, err := syscall.Wait4(-1, &rusage, 0) // 等待任何子进程退出
if err == syscall.ECHILD {
break // 无子进程时退出
}
saveContainerExitCode(status) // 记录容器退出状态
} - 多级孤儿化保护:
- 容器进程退出 → shim 调用
wait()
回收 → 避免僵尸 - shim 自身退出 → 由 init 进程回收
- 容器进程退出 → shim 调用
IO 流如何保持不中断?
1 | flowchart LR |
- 持久化管道:
/run/containerd/fifo/
下的命名管道文件保持打开
这种精妙的 进程孤儿化 + Unix Socket重连 机制,实现了容器运行时的热升级与故障恢复能力,是 Docker/containerd 高可用的架构基石。
All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.