3. 容器运行时生态:对比 containerd、CRI-O、Docker(通过 dockershim)的架构差异与选型
Kubernetes 自 v1.24 正式移除 dockershim 后,容器运行时的选型从“默认 Docker”转变为“直接对接 CRI 的实现”。当前主流的生产级 CRI 实现包括 containerd、CRI-O 以及已退役的 Docker + dockershim。理解三者的架构差异,是进行运行时性能调优和集群规划的前提。
3.1 架构演进:从 dockershim 到原生 CRI
Docker 本身并未实现 CRI 接口,Kubernetes 通过 kubelet 内置的 dockershim 组件将 CRI 调用转换为 Docker API。这种“中间层”架构增加了调用链长度和资源开销。containerd 和 CRI-O 则直接实现 CRI gRPC 接口,无需额外适配层。
关键演进节点: Kubernetes v1.24 正式移除 dockershim,标志着容器运行时进入“原生 CRI 时代”。生产集群应避免使用 Docker + dockershim 模式。
3.2 核心差异对比
| 维度 | containerd | CRI-O | Docker + dockershim |
|---|---|---|---|
| 项目归属 | CNCF 毕业项目,Docker 捐赠 | CNCF 孵化项目,Red Hat 主导 | Docker Inc.,已从 K8s 移除 |
| CRI 实现方式 | 原生 CRI gRPC 接口 | 原生 CRI gRPC 接口 | 通过 dockershim 适配层转换 |
| 镜像管理 | 内置镜像存储、拉取、挂载 | 依赖 containers/image 库 | Docker 镜像管理全套 |
| 容器进程管理 | containerd-shim | conmon | docker-containerd-shim |
| OCI 兼容性 | 完全兼容 OCI 镜像和运行时 | 完全兼容 OCI,更早支持 | 通过 containerd 间接兼容 |
| 调用链长度 | kubelet → containerd → shim → runc | kubelet → CRI-O → conmon → runc | kubelet → dockershim → Docker → containerd → shim → runc |
| 资源占用 | 低(单一守护进程) | 低(单一守护进程) | 高(多守护进程 + 适配层) |
| CLI 工具 | ctr / nerdctl | crictl(通用) | docker CLI |
3.3 性能特征对比
在同等硬件条件下,三种运行时的性能差异主要体现在 Pod 启动延迟、镜像拉取吞吐 和 内存占用 三个维度。
| 性能指标 | containerd | CRI-O | Docker + dockershim |
|---|---|---|---|
| Pod 启动延迟(P50) | ~350ms | ~380ms | ~520ms |
| Pod 启动延迟(P99) | ~800ms | ~850ms | ~1200ms |
| 镜像拉取(500MB) | ~4.2s | ~4.5s | ~5.8s |
| 空闲内存占用 | ~50MB | ~45MB | ~180MB |
| API 响应延迟 | 低 | 低 | 中 |
性能选型建议: 对于大规模集群(>500 节点)或对 Pod 启动延迟敏感的场景(如 Serverless、批量任务),优先选择 containerd 或 CRI-O。Docker + dockershim 在 v1.24 后已不再支持,不应在新集群中使用。
3.4 选型决策矩阵
根据集群规模、运维团队技术栈和业务场景,以下矩阵可辅助选型决策:
| 场景 | 推荐运行时 | 理由 |
|---|---|---|
| 通用生产集群(<500 节点) | containerd | 生态成熟、社区活跃、性能稳定、与 Docker 共享底层 |
| 大规模集群(>500 节点) | containerd 或 CRI-O | 资源占用低、启动延迟小、控制面压力小 |
| Red Hat OpenShift 环境 | CRI-O | OpenShift 默认运行时,深度集成,安全策略支持好 |
| 边缘计算 / 资源受限环境 | CRI-O | 内存占用最低(~45MB),依赖组件少 |
| 需要 Docker CLI 调试 | containerd + nerdctl | nerdctl 提供与 docker CLI 几乎一致的体验 |
| 安全合规要求高(FIPS、SELinux) | CRI-O | Red Hat 主导,安全加固更完善 |
3.5 关键架构差异详解
3.5.1 containerd 架构特点
- 单一守护进程: containerd 以单一 gRPC 服务对外提供 CRI 接口,内部通过插件架构管理镜像、容器、快照和元数据。
- containerd-shim: 每个容器对应一个 shim 进程,负责管理容器生命周期、上报状态、处理 stdin/stdout 流。shim 在容器退出后仍存活,直到 kubelet 确认清理。
- 镜像加速: 支持 stargz、estargz、nydus 等镜像加速格式,适合大规模镜像分发场景。
- 调试工具链: ctr(低级 CLI)、nerdctl(Docker 兼容 CLI)、crictl(通用 CRI 调试)。
3.5.2 CRI-O 架构特点
- OCI 原生: CRI-O 从设计之初就完全围绕 OCI 规范构建,不依赖任何 Docker 组件。
- conmon: 容器监控组件,负责收集容器日志、监控容器进程、处理信号转发。conmon 在容器生命周期内持续运行。
- 镜像管理: 使用 containers/image 库,支持多种镜像仓库协议,与 Podman、Buildah 共享底层库。
- 安全特性: 默认集成 SELinux 标签、seccomp 策略、ReadOnlyRootFilesystem 等安全配置。
3.5.3 Docker + dockershim 架构(已退役)
- 调用链过长: kubelet → dockershim → Docker API → dockerd → containerd → containerd-shim → runc,共 6 层调用。
- 资源浪费: dockerd 作为独立守护进程,额外占用内存和 CPU;dockershim 作为 kubelet 内置组件,增加 kubelet 内存占用。
- 版本耦合: dockershim 与 Docker 版本绑定,升级 Docker 可能引入兼容性问题。
- 退役影响: Kubernetes v1.24 移除 dockershim 后,使用 Docker 作为运行时的集群必须迁移至 containerd 或 CRI-O。
迁移警告: 如果当前集群仍在使用 Docker + dockershim,请立即规划迁移。Kubernetes v1.24+ 不再支持 dockershim,继续使用将导致 kubelet 无法启动。建议使用
crictl 验证新运行时的 CRI 接口可用性后再进行全量迁移。
3.6 运行时组件对比总结
3.7 选型检查清单
在最终确定运行时选型前,建议逐项确认以下条件:
- Kubernetes 版本兼容性: 确认所选运行时支持当前集群的 K8s 版本(建议 ≥ v1.24)。
- 操作系统支持: containerd 和 CRI-O 均支持主流 Linux 发行版;CRI-O 在 RHEL/CentOS 上集成更紧密。
- 镜像仓库兼容性: 确认运行时能正常拉取私有仓库镜像(含认证、TLS 配置)。
- 监控与日志集成: 确认运行时与 Prometheus、Fluentd、Filebeat 等组件的集成方式。
- 安全策略支持: 确认 PodSecurityPolicy(PSP)或 OPA/Kyverno 策略在目标运行时上的生效情况。
- 团队技术储备: 评估运维团队对 nerdctl(containerd)或 crictl(通用)的熟悉程度。
- 迁移成本: 评估从现有运行时迁移的停机时间、数据迁移和回滚方案。
生产建议: 对于新建集群,推荐默认使用 containerd。它拥有最广泛的社区支持、最成熟的生态工具链(nerdctl、ctr、crictl),且与 Docker 共享底层镜像格式,迁移成本最低。对于 Red Hat 技术栈或安全合规要求极高的场景,选择 CRI-O。