3. CRI 接口详解:kubelet 与运行时交互的 gRPC 协议、ImageService 与 RuntimeService

容器运行时接口(CRI,Container Runtime Interface)是 kubelet 与容器运行时之间的核心通信协议。它基于 gRPC 定义,将容器生命周期管理抽象为两个独立的服务:RuntimeService(运行时服务)和 ImageService(镜像服务)。理解 CRI 的接口设计,是掌握容器运行时治理与集群生命周期运维的基础。

3.1 CRI 架构与 gRPC 协议基础

CRI 采用客户端-服务器(C/S)架构,kubelet 作为 gRPC 客户端,容器运行时(如 containerd、CRI-O)作为 gRPC 服务端。两者通过 Unix Socket(默认路径 /var/run/crio/crio.sock 或 /var/run/containerd/containerd.sock)建立通信。

关键协议特征:
  • 传输层:基于 HTTP/2 的 gRPC 协议,支持双向流、多路复用。
  • 序列化:使用 Protocol Buffers(protobuf)定义消息结构,确保跨语言兼容性。
  • 认证与安全:通过 Unix Socket 文件权限控制访问,不依赖 TLS 证书。
  • 超时控制:kubelet 为每个 gRPC 调用设置超时时间(默认 2 分钟),防止运行时挂死。
CRI 架构与 gRPC 通信流程 CRI 架构与 gRPC 通信流程 kubelet gRPC 客户端 Unix Socket /var/run/*.sock 容器运行时 gRPC 服务端 containerd / CRI-O CRI 接口 RuntimeService ImageService gRPC 调用流程(以创建 Pod 为例) 1. kubelet 构造 RunPodSandboxRequest(protobuf 消息) 2. 通过 gRPC 通道发送至运行时 Unix Socket 3. 运行时解析请求,调用底层容器引擎(runc / kata) 4. 返回 RunPodSandboxResponse(含 PodSandboxID) 5. kubelet 继续调用 CreateContainer / StartContainer ⏱ 每个 RPC 调用默认超时 2 分钟,失败则重试或上报事件

3.2 RuntimeService:容器生命周期管理

RuntimeService 负责管理 Pod 沙箱(Sandbox)和容器的完整生命周期。它定义了 kubelet 与运行时之间关于容器运行状态的核心操作。

RPC 方法 方向 功能描述 关键参数
RunPodSandbox kubelet → 运行时 创建 Pod 沙箱(隔离环境,如网络命名空间) PodSandboxConfig(元数据、DNS、端口映射)
StopPodSandbox kubelet → 运行时 停止 Pod 沙箱,回收网络资源 PodSandboxId
RemovePodSandbox kubelet → 运行时 删除 Pod 沙箱,清理存储 PodSandboxId
CreateContainer kubelet → 运行时 在指定沙箱内创建容器(不启动) ContainerConfig(镜像、命令、挂载、资源限制)
StartContainer kubelet → 运行时 启动已创建的容器进程 ContainerId
StopContainer kubelet → 运行时 停止容器(支持优雅关闭超时) ContainerId, GracePeriodSeconds
RemoveContainer kubelet → 运行时 删除容器及其存储层 ContainerId
ContainerStatus kubelet → 运行时 查询容器当前状态(Running/Exited/Unknown) ContainerId
ListContainers kubelet → 运行时 列出所有容器(支持按沙箱 ID 过滤) ContainerFilter
运维要点:当集群节点出现“容器一直 Terminating”或“Pod 删除卡住”时,通常是 RuntimeService 的 StopContainer 或 RemovePodSandbox 调用超时。此时应检查运行时日志(如 journalctl -u containerd)确认底层容器引擎是否响应。

3.3 ImageService:镜像拉取与管理

ImageService 专注于容器镜像的拉取、查询和删除操作。它不参与容器运行,但直接影响 Pod 启动速度与磁盘空间治理。

RPC 方法 方向 功能描述 关键参数
PullImage kubelet → 运行时 拉取镜像到本地存储(支持认证) ImageSpec(镜像名+标签)、AuthConfig
ListImages kubelet → 运行时 列出本地已存储的镜像列表 ImageFilter
ImageStatus kubelet → 运行时 查询单个镜像的详细信息(大小、层数) ImageSpec
RemoveImage kubelet → 运行时 删除本地镜像,释放磁盘空间 ImageSpec
ImageFsInfo kubelet → 运行时 获取镜像文件系统使用情况(容量、已用) 无
安全合规提示:ImageService 的 PullImage 调用是镜像供应链攻击的主要入口。务必在 kubelet 配置中启用 --image-pull-progress-deadline 和镜像签名验证(如 Notary / Cosign),防止拉取被篡改的恶意镜像。

3.4 RuntimeService 与 ImageService 的协作关系

在 Pod 启动过程中,两个服务按严格顺序协作:

  1. 镜像准备阶段:kubelet 调用 ImageService.PullImage 确保镜像已缓存。
  2. 沙箱创建阶段:kubelet 调用 RuntimeService.RunPodSandbox 创建隔离环境。
  3. 容器创建阶段:kubelet 调用 RuntimeService.CreateContainer(引用已拉取的镜像)。
  4. 容器启动阶段:kubelet 调用 RuntimeService.StartContainer 运行进程。
  5. 状态监控阶段:kubelet 定期调用 RuntimeService.ContainerStatus 和 ImageService.ImageFsInfo 进行健康检查与 GC。
RuntimeService 与 ImageService 协作时序 Pod 启动:RuntimeService 与 ImageService 协作时序 kubelet ImageService RuntimeService PullImage 响应 RunPodSandbox PodSandboxID CreateContainer ContainerID StartContainer 启动成功 💡 若 PullImage 失败,kubelet 不会继续调用 RunPodSandbox,Pod 状态为 ImagePullBackOff

3.5 CRI 接口版本与兼容性治理

CRI 接口采用语义化版本(SemVer),kubelet 与运行时之间通过版本协商确保兼容性。版本不匹配是集群生命周期运维中常见的故障来源。

版本字段 说明 示例值
RuntimeName 运行时标识符 containerd / cri-o
RuntimeVersion 运行时自身版本 1.6.20
ApiVersion CRI API 版本(kubelet 与运行时协商) v1 / v1alpha2
版本协商流程:
  1. kubelet 启动时调用 Version() RPC 获取运行时支持的 API 版本列表。
  2. kubelet 选择自身支持的最高版本进行通信。
  3. 若版本不兼容(如 kubelet v1.24 要求 CRI v1,但运行时仅支持 v1alpha2),kubelet 将报错并拒绝启动节点。

3.6 生产环境 CRI 接口监控与故障排查

在集群生命周期运维中,CRI 接口的稳定性直接影响节点健康。以下是关键监控指标与排查方法:

  • gRPC 调用延迟:监控 RunPodSandbox 和 PullImage 的 P99 延迟,超过 10 秒应告警。
  • 调用失败率:统计 CreateContainer 和 StartContainer 的失败次数,失败率超过 1% 需介入。
  • Socket 连接状态:检查 /var/run/*.sock 文件是否存在且权限为 660。
  • 常见错误码:
    • NotFound:容器或镜像不存在(通常由并发删除导致)。
    • AlreadyExists:重复创建沙箱或容器(kubelet 重试逻辑异常)。
    • DeadlineExceeded:操作超时(底层引擎卡死或磁盘 I/O 瓶颈)。
快速诊断命令:使用 crictl 工具直接调用 CRI 接口,绕过 kubelet 进行测试:
# 查看运行时版本
crictl version

# 列出所有 Pod 沙箱
crictl pods

# 列出所有容器(含已退出)
crictl ps -a

# 拉取镜像测试
crictl pull nginx:latest