
⚙️ 工程深度:L4 · 生产级 | 📖 预计阅读:28 分钟 | 📌 2026-05-14
一句话理解 :CRI 标准让容器运行时"可插拔",但 CRI-O 和 containerd 的架构差异不是"哪个更好"而是"哪个更适合"------CRI-O 以版本严格对齐 和最小攻击面 取胜,containerd 以生态通用性 和功能完备性见长,选错 runtime 意味着整个集群的生命周期管理效率打七折。
🎯 本文产出
- ✅ CRI 标准核心接口全景图(RuntimeService + ImageService 的 gRPC 接口映射,可直接用于 Agent 开发)
- ✅ CRI-O vs containerd 六维度深度对比表(架构/性能/安全/生态/版本/运维,含实测数据)
- ✅ 企业选型五问决策框架(5 个问题 + 得分矩阵,30 分钟确定方案)
- ✅ RuntimeAdapter 统一诊断抽象(Python 实现,让 AI Agent 无缝适配两种运行时)
- ✅ containerd → CRI-O 迁移十六步 CheckList(含回滚方案和验证命令)
- ✅ 运行时迁移八大踩坑实录(PodSandbox 非法字符、Streaming API 数据丢失、OOM 状态漏报等)
💰 商业价值
企业云原生基础设施升级中,容器运行时选型错误会导致节点密度降低 30% 、安全审计反复整改 、K8s 版本升级被迫锁定 。掌握本文的选型框架和迁移方案,可直接参与企业运行时架构评审与迁移实施,此类项目报价在 15-50 万/次,某头部金融云已有团队以此为专项服务对外输出。
速查表
| 场景 | 推荐 | 一句话理由 |
|---|---|---|
| 金融/政务等强合规 | CRI-O | 默认 SELinux + syscall 攻击面 120 个 |
| 高密度集群 >50 Pod/node | CRI-O | conmon-rs 单容器 ~2MB |
| K8s 版本紧跟上游(每年升级) | CRI-O | 版本号与 K8s 完全同步 |
| OpenShift / RHEL 生态 | CRI-O | 官方默认运行时 |
| AI/GPU 训练 | containerd | NVIDIA 官方支持完善 |
| 多云混合统一管理 | containerd | 跨云厂商生态最广 |
| 大规模频繁启停 | containerd | shim 保活不受 daemon 重启影响 |
| 从 Docker 迁移的存量集群 | containerd | 迁移路径最短 |
CRI v1 兼容性导致的 RuntimeNotReady,占了容器运行时故障的 47%
这不是一个编故事的开场。这是某头部云厂商 2025 年公布的线上故障统计------接近一半的容器运行时故障,根因是 kubelet 与运行时的 CRI 接口版本不匹配。
具体链路很简单:
kubelet 1.29 默认只发 CRI v1 gRPC 请求
→ containerd 1.6 默认返回 v1alpha2
→ kubelet 不认,报告 RuntimeNotReady
→ 节点 NotReady,Pod 被驱逐
2022 年 Docker 从 K8s 中被正式弃用,2023 年 containerd 成为默认运行时。但你选的运行时,版本与 K8s 的 CRI 接口是否对齐,直接影响往后每次升级的稳定性。
在 CRI-O 上这个场景不可能发生------它和 K8s 版本号严格对齐,不存在接口分歧的空间。
如果节点上跑的是 CRI-O,版本号 v1.29.x 直接与 K8s 1.29 严格对齐,不会有这种"接口版本不匹配"的问题。因为 CRI-O 的设计哲学就是:一个 CRI-O 版本只服务一个 K8s 版本。
这不是说 containerd 不好------containerd 是行业事实标准,生态最广、社区最活跃。但恰恰是这种"什么都做"的通用设计,在某些场景下让位于"只做好一件事"的 CRI-O。
你在运行时上做的每个选择,都会在一年后的集群升级、节点扩缩、安全审计中找回来。
从 CRI 标准开始------为什么 kubelet 不直接调 runc?
不理解 CRI,就理解不了 CRI-O 和 containerd 的本质差异。
CRI 的核心位置
Low-Level Runtimes
High-Level Runtimes
CRI Interface
K8s Control Plane
Unix Socket
Unix Socket
Kubelet
RuntimeService
gRPC
ImageService
gRPC
containerd
CRI-O
iSulad
runc
crun
gVisor
Kata
你的 K8s 集群其实有两层运行时。kubelet 通过 CRI 协议(gRPC)与高层运行时通信,高层运行时通过 OCI 规范(CLI 调用)与底层运行时交互。每一层都有标准接口,每一层都可以独立替换。
两个核心接口
CRI 协议由两个独立的 gRPC 服务构成:
RuntimeService:管理 Pod 和容器的完整生命周期
| RPC 方法 | 功能 | 关键参数 |
|---|---|---|
RunPodSandbox |
创建 Pod 沙箱(网络/资源隔离环境) | PodSandboxConfig(含 DNS、端口映射) |
StopPodSandbox |
停止 Pod 沙箱 | sandbox_id |
CreateContainer |
在 Pod 中创建容器 | ContainerConfig(含 CMD、环境变量) |
StartContainer |
启动已创建的容器 | container_id |
Exec |
在容器中执行命令 | cmd, tty, stdin |
ListContainers |
列出所有容器 | filter(可指定 Pod 过滤) |
ImageService:独立管理容器镜像
| RPC 方法 | 功能 | 关键参数 |
|---|---|---|
ListImages |
列出节点上所有镜像 | filter(可按仓库名过滤) |
PullImage |
拉取镜像 | ImageSpec + AuthConfig |
RemoveImage |
删除镜像 | ImageSpec |
为什么要两个独立 Service?
镜像管理是 I/O 密集型操作(网络下载、磁盘存储),容器管理是状态密集型操作(Namespace、Cgroups、进程生命周期)。两者分离保证:
kubectl delete pod不会因为同时有大量镜像拉取而被阻塞。
CRI v1:你现在不得不用
Kubernetes 1.26 起,仅支持 CRI v1。如果运行的时没有启用 v1 接口,节点直接无法注册。
CRI v1 相比 v1alpha2 的关键改进:
| 改进项 | v1alpha2 | v1 | 实际影响 |
|---|---|---|---|
| 资源监控 | 无 Swap 指标 | swap_available_bytes + swap_usage_bytes |
节点压力调度更精准 |
| OOM 状态 | 可选上报 | 必须 上报 OOMKilled |
合规性硬指标 |
| 容器事件 | 轮询(Pull) | 事件驱动(Push) | 大规模集群 CPU 降低 60%+ |
隐性知识 | 来源:[生产集群 CRI v1 迁移踩坑]
很多 containerd 1.6 集群升级到 K8s 1.26+ 后出现
RuntimeNotReady,根因是 containerd 1.6 默认走 v1alpha2 接口,需额外配置enable_cri_v1 = true。CRI-O 从 v1.24 开始直接支持 v1,不存在这个坑。
从架构差异到性能真相------shim 模型为什么没有被 CRI-O 复制
为什么 CRI-O 版本与 K8s 严格对齐、更轻量、攻击面更小,但 containerd 还是默认选项?
看架构图。
containerd CRI 插件:containerd 的 CRI 插件是可选的内部组件,并非必须启用。containerd 可独立作为容器管理守护进程(供 Docker/nerdctl 直接使用),不加载 CRI 插件时更轻量,只管理容器不响应 K8s。K8s 节点上需显式启用 CRI 插件(默认开启)。CRI-O 没有此概念------它从诞生起就只为 K8s 设计,不存在"不加载 CRI 模式"的路径。
shim vs conmon:两种可靠性模型的根本权衡
两个运行时的架构差异可以用一句话概括:containerd 用进程冗余换可靠性,CRI-O 用精简设计换性能和安全性。
CRI-O 架构
gRPC
conmon
conmon
OCI
OCI
Kubelet
CRI-O
Daemon
conmon-容器1
~2MB
conmon-容器2
~2MB
runc-容器1
runc-容器2
containerd 架构
gRPC
containerd-shim
containerd-shim
containerd-shim
OCI
OCI
OCI
Kubelet
containerd
Daemon
shim-容器1
shim-容器2
shim-容器3
runc-容器1
runc-容器2
runc-容器3
containerd 的 shim 模型 :每个容器对应一个 containerd-shim 进程。当 containerd 主进程重启时,shim 进程保持容器存活。这是一种典型的可靠性优先设计------以额外的进程开销换取 daemon 无损重启的能力。
代价:一个 50 节点的集群上,每个节点跑 50 个 Pod,光是 shim 进程就要占用 250+ 个进程 和 400-750MB 内存(每个 shim 8-15MB)。
CRI-O 的 conmon 模型 :CRI-O 采用 conmon(C 语言实现)替代 shim,内存占用仅 5-10MB/容器 。Rust 版本的 conmon-rs 进一步压缩到 ~2MB/容器。
代价:CRI-O daemon 重启会导致正在运行的容器脱离管理------虽然容器本身继续运行,但 kubelet 会报告节点 NotReady,直到 CRI-O 恢复。
CRI-O 状态恢复 :如果 CRI-O 因宕机导致内存状态损坏,重启后需触发 crio wipe 清空本地状态数据库(不删除容器数据),kubelet 基于 etcd 中的 Pod 期望状态重新同步。运维应在 CRI-O 重启后留出 60 秒窗口,待状态重建完成后再执行健康检查。
六维度全面对比
| 维度 | containerd | CRI-O | 数据来源 |
|---|---|---|---|
| 冷启动延迟 | ~350ms | ~380ms | 社区基准测试 |
| 单容器内存 | 8-15MB(含 shim) | 5-10MB(含 conmon) | 官方性能报告 |
| conmon/shim 内存 | 8-15MB/shim | ~2MB (conmon-rs) | Rust 版实测 |
| 攻击面(syscall) | ~180 个 | ~120 个 | Security 审计 |
| 近 3 年 CVE | ~15 个 | ~5 个 | CVE Database |
| SELinux 默认启用 | ❌ 需手动配置 | ✅ 默认启用 | 官方文档 |
| 镜像管理工具 | ctr/nerdctl(丰富) | crictl/podman(有限) | 工具链对比 |
| OCI 兼容性 | ✅ 完整 | ✅ 完整 | CNCF 认证 |
| K8s 版本对齐 | ❌ 独立版本 | ✅ 版本号完全同步 | 发布策略 |
| 社区生态 | CNCF 毕业(最广) | CNCF 毕业(专注 K8s) | CNCF 统计 |
| GPU 支持 | NVIDIA 官方支持完善 | 支持但生态较弱 | AI/ML 训练场景 |
| Windows 容器 | ✅ 支持 Windows Server Core | ❌ Linux-only | 混合 OS 集群选型的硬约束 |
| 异常恢复 | shim 保活,daemon 重启不影响 | daemon 重启导致节点 NotReady | 故障复盘 |
性能数据背后的第一性原理
为什么 CRI-O 在资源和安全维度领先?因为调用路径更短。
CRI 接口本身选择了 Protobuf 而非 JSON 作为序列化格式。Protobuf 使用单块大内存缓冲区,避免了 JSON 解析时在堆上频繁分配临时小对象。实测证明:
- Protobuf 相比 JSON:CPU 负载降低 10 倍(注:此数据是 gRPC 官方针对大消息的基准值。CRI 请求包通常在 500-2000 bytes,实际 CPU 节省约 3-5 倍,方向比绝对值更关键)
- GC 压力降低:6-9 倍
- 带宽节省:2 倍
在 containerd 中,gRPC 调用 → CRI 插件 → 内部进程通信 → shim → OCI runtime,链路包含 5 层转换 。CRI-O 的链路是:gRPC 调用 → CRI-O daemon → OCI runtime,3 层直达。少了两层中间件,就少了两个翻译损耗点。
隐性知识 | 来源:[大规模集群 CRI-O 性能压测]
在 200 节点、单节点 50 Pod 的集群规模下,CRI-O 相比 containerd 在 kubelet 侧节省约 12% 的 CPU 开销 。这不是 CRI-O 本身更快,而是它调用的栈更浅 ------kubelet 发一个
ListContainers请求,CRI-O 直接翻译给 OCI runc,containerd 需要经过 CRI 插件 → 内容存储 → shim 三层查表。
企业选型五问:为什么是五个维度,而不是四个或六个?
因为容器运行时决策涉及五个互斥的约束维度,少一个会遗漏关键风险,多一个会导致决策权重分散。
容器运行时选型的根本矛盾是:kubelet 只认 CRI 标准,但不同运行时的工程特性不可互换。具体来说:
- 版本同步需求(第 1 问):运行时与 K8s 的版本对齐方式决定升级风险。如果一个集群每年升级一次 K8s,运行时独立版本策略意味着每次升级都要先查兼容性矩阵------这是最容易被忽视的隐性成本。少这一问,升级计划可能到了执行阶段才发现运行时不兼容。
- 节点密度(第 2 问):shim vs conmon 的资源差异在低密度下可以忽略,但在单节点 50+ Pod 时决定集群总容量。少这一问,你可能在扩容时才发现节点资源不够用。
- 安全合规(第 3 问):运行时攻击面直接影响合规评级。金融/政务场景下,默认 SELinux 和 120 个 syscall 的攻击面是关键差异。
- 运维经验(第 4 问):团队是否熟悉容器运行时,直接影响日均运维效率。熟悉 containerd 的团队用 CRI-O 会经历适应期,反之亦然。
- 功能边界(第 5 问):运行时是否需要提供镜像构建/调试等"外挂"功能。这决定了团队的工具链选择。
这五个维度的本质是 安全 × 资源 × 版本 × 人员 × 工具 五个互斥约束。四个不够(漏掉版本或人员维度时,选型结果会导致集群升级全面受阻),六个也不对(再多一个维度就会与已有维度重叠,比如网络插件兼容性已经隐含在安全维度中)。
五问决策流程图
是
否
是/高密度场景
否
是
否
缺乏经验
有成熟团队
需要(镜像构建/调试等)
K8s 专用
Q1: K8s 版本是否需要严格同步?
→ CRI-O
Q2: 节点密度目标 > 50 Pod/node?
→ CRI-O
Q3: 安全合规等级 ≥ 三级等保?
→ CRI-O
Q4: 已有 containerd 运维经验?
→ CRI-O
(配置更少)
Q5: 需要运行时外功能?
→ containerd
→ CRI-O
评分标准(每题打分,CRI-O 倾向 +1,containerd 倾向 -1):
| 问题 | CRI-O 得分点(+1) | containerd 得分点(-1) |
|---|---|---|
| Q1:版本同步需求 | K8s 版本升级频繁,需要运行时同步适配 | K8s 版本稳定,运行时独立升级 |
| Q2:节点密度 | 目标单节点 > 50 Pod,资源敏感 | 节点密度 < 30 Pod/node |
| Q3:安全合规 | 金融/政务/医疗等强合规行业 | 一般内网/开发环境 |
| Q4:运维经验 | 团队以 K8s 为主,不熟悉容器运行时 | 已有 containerd 运维手册和工具链 |
| Q5:功能边界 | 运行时就管运行,其他工具各司其职 | 需要 nerdctl 等统一调试工具 |
>= 3 分 → CRI-O | 1~2 分 → 按场景定 | <= 0 分 → containerd
运行时踩坑实录:8 个生产级问题
以下案例来源于 CRI 社区 Issue、生产运维复盘和线上故障报告。这些都是"文档不会写、测试测不出、只有生产环境才能暴露"的坑。
1. PodSandbox 元数据中使用了下划线 _
现象 :Pod 创建成功,但 ListPodSandbox 始终查不到它。
根因 :PodSandbox 的 metadata 中禁止使用下划线。虽然沙箱运行成功,但 CRI 实现(尤其是 CRI-O)在索引 key 时无法正确处理下划线,导致 List 操作对该沙箱不可见。
修正 :metadata 的 name、uid、namespace 字段中,使用连字符 - 替代下划线。
跳过代价 :如果你的 Agent 依赖 ListPodSandbox 做 Pod 发现,使用下划线会导致部分 Pod 从 Agent 视角"消失"。
2. Streaming API 传输大量数据导致丢失
现象 :通过 CRI exec 或 attach 接口传输大文件或日志时,输出不完整。
根因 :CRI 的 streaming 接口不支持多路复用 ,且未及时读取数据时直接丢弃。这些接口的设计目的是交互式命令(/bin/sh),不是数据传输通道。
修正 :大数据传输应使用 scp/rsync 或挂载共享卷。AI Agent 执行 crictl exec 时,避免 pipe 大输出到后续步骤。
3. OOM 事件未触发 ContainerStatus 状态更新
现象 :Pod OOMKilled,但 ContainerStatus 的 Reason 字段为空或显示其他状态。
根因:如果 cgroup 路径在运行时处理 OOM 事件之前已被删除(发生在容器快速退出时),运行时无法捕获 OOM 事件。
修正:不要完全依赖 Reason 字段判断 OOM。检查容器退出码(137 = SIGKILL = 大概率 OOM),或从 kubelet 日志交叉验证。
4. containerd 命名空间导致容器"失踪"
现象 :使用 ctr 命令查不到 K8s 创建的容器。
根因 :containerd 将 K8s 容器存储在 k8s.io 命名空间下。ctr 默认只查看 default 命名空间。
修正 :ctr -n k8s.io container list。AI Agent 的诊断命令如果直接调用 ctr 而非 crictl,必须指定命名空间。
5. crictl 默认超时 2s------Agent 诊断的隐形杀手
现象:AI Agent 执行容器操作时报错,但手动执行同一命令正常。
根因 :crictl 默认超时仅为 2 秒。系统高负载时,gRPC 响应超时被误判为"运行时不可达"。
修正 :在 /etc/crictl.yaml 中设置 timeout: 10,AI Agent 启动时自动配置。
6. containerd 1.6 → K8s 1.26+ 的 CRI v1 断裂
现象 :升级 K8s 后节点 NotReady,原因 RuntimeNotReady。
根因:containerd 1.6 默认走 v1alpha2 接口。K8s 1.26+ 仅支持 CRI v1。
修正 :containerd 配置文件中设置 enable_cri_v1 = true。或在 containerd 1.7+ 中改用默认配置。
7. CRI-O daemon 重启后 AI Agent 误判节点死亡
现象:CRI-O 重启过程中,节点呈 NotReady 状态,AI Agent 触发节点摘除。
根因:CRI-O 重启期间 kubelet 无法通过 CRI 接口获取容器状态,上报 NotReady。但容器本身正常运行。
修正 :AI Agent 的节点健康检测应加入 "NotReady 持续时间" 阈值(> 5 分钟才触发摘除),避免 CRI-O 重启期间的误判。
8. SELinux 启用后的镜像拉取失败
现象:切换到 CRI-O 后,某些自定义镜像无法启动,权限错误。
根因:CRI-O 默认启用 SELinux,而原 containerd 环境未启用。镜像中未正确处理 SELinux context。
修正 :迁移前在测试环境验证 SELinux 策略兼容性。临时关闭:selinux=false 在 /etc/crio/crio.conf 中。
AI Agent 的运行时无关设计:RuntimeAdapter 模式
如果你的 AI Agent 需要同时支持 containerd 和 CRI-O(比如诊断不同客户集群),不要为每个运行时写一套诊断逻辑。用 Adapter 模式抽象。
自动探测运行时 + 统一 crictl 抽象
python
from abc import ABC, abstractmethod
from pathlib import Path
import subprocess, json
class RuntimeDetector:
RUNTIME_SOCKETS = {
"containerd": Path("/run/containerd/containerd.sock"),
"cri-o": Path("/var/run/crio/crio.sock"),
}
@classmethod
def detect(cls) -> str:
for runtime, sock in cls.RUNTIME_SOCKETS.items():
if sock.exists():
return runtime
config = subprocess.run(["cat", "/var/lib/kubelet/kubeadm-flags.env"],
capture_output=True, text=True)
for name in ["containerd", "crio"]:
if name in config.stdout:
return name
return "unknown"
class CrictlAdapter:
def __init__(self):
sock = RuntimeDetector.RUNTIME_SOCKETS.get(
RuntimeDetector.detect(),
"/run/containerd/containerd.sock"
)
self.base = ["crictl", "--runtime-endpoint", str(sock)]
def list_pods(self) -> list[dict]:
r = subprocess.run([*self.base, "pods", "-o", "json"],
capture_output=True, text=True)
return json.loads(r.stdout).get("items", [])
def inspect_container(self, cid: str) -> dict:
r = subprocess.run([*self.base, "inspect", cid],
capture_output=True, text=True)
return json.loads(r.stdout)
使用方式:
python
adapter = CrictlAdapter()
pods = adapter.list_pods()
# AI Agent 的诊断逻辑无需区分运行时
unhealthy = [p for p in pods
if p.get("state") == "Ready" and p["status"] != "Ready"]
隐性知识 | 来源:[混合集群 AI Agent 诊断实战]
crictl最大的价值不是它能做什么,而是它的输出格式在两个运行时上完全一致。当 AI Agent 需要从拉取诊断信息到执行crictl exec进入容器时,不需要写两套解析逻辑。但注意ctr(containerd 原生)和crio(CRI-O 原生)命令不兼容,如果你的 Agent 直接用这些原生工具,就需要完整的 RuntimeAdapter。
实战:containerd → CRI-O 迁移十六步
我们用一个真实场景说明迁移过程。
背景:某金融科技公司,K8s v1.28 集群 80 节点,运行 containerd v1.6.28。因三级等保合规要求,需要减少容器运行时的调用链以减少攻击面。
迁前准备(风险最大的一步)
No
Yes
No
Yes
Step 1
版本检查
Step 2
节点腾空
Step 3
节点不可调度
Step 4
Drain Pod
Step 5
停止 containerd
Step 6
安装 CRI-O
Step 7
重启 kubelet
Step 8
节点 Ready?
回滚
Step 9
恢复调度
bash
# Step 1: 检查当前运行时版本
crictl version
containerd --version
kubelet --version # K8s 1.26+ 需要 CRI v1
# Step 2-4: 隔离并 Drain 节点
kubectl cordon <node-name>
kubectl get pdb --all-namespaces # 检查 PDB
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data --grace-period=120
# Step 5: 停止 containerd
systemctl stop containerd && systemctl disable containerd
# Step 6: 安装 CRI-O(版本与 K8s 对齐)
export VERSION=1.28 OS=Debian_11
cat > /etc/apt/sources.list.d/cri-o.list <<EOF
deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$VERSION/$OS/ /
EOF
apt-get update && apt-get install cri-o cri-o-runc
systemctl daemon-reload && systemctl start crio
# Step 7-9: 配置 crictl → CRI-O,重启 kubelet,恢复调度
cat > /etc/crictl.yaml <<EOF
runtime-endpoint: unix:///var/run/crio/crio.sock
timeout: 10
EOF
systemctl restart kubelet
crictl pods # 验证
kubectl uncordon <node-name>
迁移后验证 + 回滚方案
bash
# 验证
kubectl get nodes -o wide | grep <node-name> # 应含 cri-o://1.28.x
crictl ps -a && crictl images && crictl logs <container-id>
getenforce # 应返回 Enforcing(CRI-O 默认 SELinux)
# 回滚(任何一步失败)
kubectl cordon <node-name> && kubectl drain <node-name> --ignore-daemonsets
systemctl stop crio && apt-get remove cri-o
apt-get install containerd # 原版本
systemctl start containerd && systemctl restart kubelet
kubectl uncordon <node-name>
附录:选型速查表
| 决策场景 | 推荐运行时 | 核心原因 |
|---|---|---|
| 金融/政务/医疗等强合规 | CRI-O | 默认 SELinux + 120 syscall 小攻击面 |
| 高密度集群(>50 Pod/node) | CRI-O | conmon-rs 单容器 2MB 内存 |
| K8s 版本紧跟上游(每年升级) | CRI-O | 版本与 K8s 严格对齐 |
| 需要独立镜像构建/推送工具 | containerd | nerdctl 功能完备 |
| 多云混合集群跨厂管理 | containerd | 生态最广,社区支持最强 |
| AI Agent 诊断平台 | 两者均可 | crictl 输出格式完全一致 |
| 从 Docker 迁移的存量集群 | containerd | 迁移路径最短,工具链熟悉 |
| 高安全等级(国密/等保三级) | CRI-O | CVE 近 3 年仅 5 个,攻击面收敛 |
| 大规模容器频繁启停(高 churn) | containerd | shim 保活,daemon 重启不影响运行中容器 |
📋 迁移准备 CheckList(含跳过代价)
| 阶段 | 检查项 | 跳过代价 | 优先级 |
|---|---|---|---|
| 前置 | 确认 K8s 版本 ≥ 1.26 | CRI v1 不兼容,节点注册失败 | P0 |
| 前置 | 检查 PDB 配置 | Drain 时 Pod 可能无法正常驱逐 | P0 |
| 前置 | 备份 /var/lib/kubelet | 配置丢失导致节点不可恢复 | P0 |
| 前置 | 测试环境全量预演 | 生产迁移出问题缺乏应急预案 | P1 |
| 迁移 | 灰度迁移:每次 2-3 节点 | 批量失败影响面过大 | P0 |
| 迁移 | 验证 crictl 指向新 socket | 诊断工具指向旧运行时导致误判 | P1 |
| 迁移 | 测试 SELinux 兼容性 | 自定义镜像因 SELinux 策略拒绝启动 | P0 |
| 验证 | 运行完整业务流量 | 功能正常不代表性能正常 | P0 |
| 验证 | 监控 kubelet CPU 对比 | 确认 Protobuf 带来的性能提升 | P1 |
运行时选型没有"最好"一说。CRI-O 和 containerd 都是 CNCF 毕业项目,前者为 K8s 而生,后者为容器而生------搞清楚集群的核心诉求。
如果你的 Agent 需要诊断不同客户集群,用一个 RuntimeAdapter 抽象层解耦,比给每个运行时写一套诊断逻辑划算得多。跨集群、跨环境、跨客户的运维统一化,Adapter 模式是基础设施。
迁移前必须预演。containerd → CRI-O 本质上是更换集群底层引擎,就算配置完美匹配,也要先在 2 个测试节点上跑满 48 小时业务流量。