2. Containerd 基础回顾:架构、核心组件与 CRI 接口
在深入集群节点扩容的状态同步与回滚设计之前,必须牢固掌握 Containerd 的底层架构。Containerd 并非一个简单的二进制文件,而是一组高度模块化的守护进程集合,它通过标准化的 CRI 接口与 Kubernetes 交互,并依赖 shim、snapshotter 等核心组件完成容器的完整生命周期管理。本节将系统梳理这些基础概念,为后续章节的复杂流程设计奠定技术基石。
2.1 Containerd 与 Docker 的关系
许多从业者容易混淆 Containerd 与 Docker 的边界。简单来说,Docker 是一个完整的容器化平台,而 Containerd 是 Docker 剥离出的核心容器运行时组件。自 Docker 1.11 版本起,Docker 引擎内部就已经使用 Containerd 来管理容器生命周期。在 Kubernetes 生态中,Containerd 作为 CRI 兼容的运行时,直接替代了 Docker 作为 kubelet 的底层依赖。
| 对比维度 | Docker | Containerd |
|---|---|---|
| 定位 | 完整的容器平台(含 CLI、构建、编排) | 轻量级容器运行时(仅核心生命周期) |
| 对外接口 | Docker API、CLI | CRI(Kubernetes)、gRPC API |
| 镜像构建 | 内置 Dockerfile 构建引擎 | 不提供构建能力,仅管理镜像拉取与存储 |
| 进程模型 | dockerd + containerd + containerd-shim | containerd + containerd-shim |
| Kubernetes 集成 | 通过 dockershim(已弃用) | 原生 CRI 实现,无需额外适配层 |
2.2 Containerd 整体架构
Containerd 采用 C/S 架构,核心守护进程 containerd 通过 gRPC 对外暴露服务。它不直接管理容器进程,而是通过 containerd-shim 作为中间层,将容器进程与 containerd 守护进程的生命周期解耦。这种设计使得 containerd 可以重启或升级,而不影响正在运行的容器。
2.3 核心组件详解
2.3.1 containerd-shim:容器进程的守护者
shim 是 Containerd 架构中最具特色的组件。当 kubelet 通过 CRI 请求创建容器时,containerd 会为每个容器启动一个独立的 shim 进程。shim 的主要职责包括:
- 生命周期解耦:即使 containerd 守护进程崩溃或重启,shim 仍然保持运行,确保容器进程不受影响。
- 标准输入输出管理:shim 负责将容器的 stdout/stderr 重定向回 containerd,进而传递给 kubelet 的日志收集管道。
- 退出码收集:当容器进程退出时,shim 捕获退出码并持久化,等待 containerd 或 kubelet 查询。
- 运行时桥接:shim 调用底层的 runc(或其它 OCI 运行时)来实际创建和运行容器。
2.3.2 snapshotter:容器镜像的层管理引擎
snapshotter 是 Containerd 管理容器文件系统的核心组件。它基于 Linux 的 overlayfs 或其它快照技术,实现镜像层的挂载与复用。每个容器镜像由多个只读层叠加而成,snapshotter 负责将这些层合并为一个可写层供容器使用。
| 快照类型 | 说明 | 节点扩容场景影响 |
|---|---|---|
| overlayfs | 默认快照器,使用内核 overlay 文件系统 | 镜像层共享效率高,新节点拉取镜像时只需下载缺失层 |
| native | 直接复制文件,不使用联合挂载 | 磁盘占用大,扩容时镜像拉取速度较慢 |
| devmapper | 基于 Device Mapper 的快照 | 适用于需要块设备隔离的场景,但配置复杂 |
| stargz | 支持延迟拉取的快照器(需额外插件) | 可显著加速节点扩容时的镜像分发,按需加载 |
2.4 CRI 接口:Kubernetes 与 Containerd 的通信桥梁
CRI(Container Runtime Interface)是 Kubernetes 定义的 gRPC 协议,用于 kubelet 与容器运行时之间的标准化通信。Containerd 通过内置的 cri-plugin 实现了完整的 CRI 接口,这也是它能够直接替代 Docker 成为 Kubernetes 默认运行时的根本原因。
2.4.1 CRI 核心接口方法
CRI 接口主要分为两类服务:RuntimeService 和 ImageService。以下是与节点扩容最相关的关键方法:
| 服务类型 | 接口方法 | 功能描述 | 扩容场景关联 |
|---|---|---|---|
| RuntimeService | RunPodSandbox |
创建 Pod 沙箱(即容器网络命名空间) | 新节点上创建 Pod 的第一步 |
CreateContainer |
在沙箱内创建容器 | 扩容时调度到新节点的 Pod 会触发此调用 | |
StartContainer |
启动已创建的容器 | 容器状态从 Created 转为 Running | |
| ImageService | PullImage |
拉取容器镜像到本地存储 | 新节点缺少镜像时触发,是扩容延迟的主要来源 |
ImageStatus |
查询镜像是否存在 | kubelet 判断是否需要拉取镜像 |
2.4.2 CRI 调用流程(节点扩容视角)
当 kubelet 接收到调度器分配的 Pod 创建请求时,会按照以下顺序调用 CRI 接口:
- ImageStatus 检查:kubelet 首先查询镜像是否已存在于节点本地。如果存在,跳过拉取步骤。
- PullImage 拉取:若镜像不存在,kubelet 调用 PullImage 方法。containerd 通过 snapshotter 将镜像层解压到本地存储。
- RunPodSandbox 创建沙箱:为 Pod 创建独立的网络命名空间和 cgroup 资源隔离。
- CreateContainer 创建容器:在沙箱内创建容器,此时容器处于 Created 状态。
- StartContainer 启动容器:调用 runc 启动容器进程,容器进入 Running 状态。