4. CRI 接口核心数据结构:PodSandbox、Container、Image 等核心 protobuf 消息定义、字段含义与生命周期映射

CRI(Container Runtime Interface)是 kubelet 与容器运行时之间的核心 gRPC 协议。其底层数据模型通过 Protocol Buffers(protobuf)定义,涵盖 PodSandbox、Container、Image 三大核心对象。理解这些消息的字段含义及其与 Kubernetes 资源生命周期的映射关系,是进行 CRI 适配与运行时开发的基础。

4.1 核心 protobuf 消息概览

CRI 的 protobuf 定义位于 k8s.io/cri-api/pkg/apis/runtime/v1 包中。以下表格列出了最核心的五个消息类型及其用途:

消息名称 用途 对应 Kubernetes 资源
PodSandbox 描述 Pod 级别的隔离环境(如 Linux Namespace、Cgroup 父路径) Pod(逻辑沙箱)
PodSandboxConfig 创建 PodSandbox 时传入的配置参数 PodSpec 中的部分字段
Container 描述运行中的容器实例 Pod 内的 Container
ContainerConfig 创建容器时传入的配置参数 Container 的 Image、Command、Env 等
Image 描述容器镜像的元数据 镜像仓库中的镜像

4.2 PodSandbox 消息定义与生命周期映射

PodSandbox 是 CRI 中最重要的抽象之一,它代表一个“沙箱环境”,通常对应一个 Linux 网络命名空间和一组共享资源。其核心字段如下:

message PodSandbox {
  // 沙箱的唯一标识符,由运行时生成
  string id = 1;

  // 沙箱的元数据,包含 Pod 名称、UID、Namespace 等
  PodSandboxMetadata metadata = 2;

  // 沙箱当前状态
  PodSandboxState state = 3;

  // 沙箱创建后的标签(Labels)
  map<string, string> labels = 4;

  // 沙箱的注解(Annotations)
  map<string, string> annotations = 5;

  // 运行时特定的沙箱状态信息(如 Linux Namespace 路径)
  PodSandboxStatus runtime_status = 6;
}

enum PodSandboxState {
  SANDBOX_READY    = 0;  // 沙箱已就绪,可以创建容器
  SANDBOX_NOTREADY = 1;  // 沙箱未就绪(如正在初始化或已停止)
}
关键映射:PodSandbox 的 state 字段直接映射到 Kubernetes Pod 的 PodStatus.Phase。当 PodSandbox 处于 SANDBOX_READY 时,Pod 才能进入 Running 阶段;若沙箱失败,Pod 将进入 Failed 或 Pending。

4.3 Container 消息定义与生命周期映射

Container 消息描述运行在 PodSandbox 内的单个容器实例。其字段设计紧密对应 Docker/containerd 的容器状态机:

message Container {
  // 容器唯一标识符
  string id = 1;

  // 容器所属的 PodSandbox ID
  string pod_sandbox_id = 2;

  // 容器元数据(名称、尝试次数等)
  ContainerMetadata metadata = 3;

  // 容器镜像的引用(如 "nginx:1.21")
  ImageSpec image = 4;

  // 容器当前状态
  ContainerState state = 5;

  // 容器创建时间(Unix 时间戳)
  int64 created_at = 6;

  // 容器标签与注解
  map<string, string> labels = 7;
  map<string, string> annotations = 8;
}

enum ContainerState {
  CONTAINER_CREATED    = 0;  // 容器已创建但未启动
  CONTAINER_RUNNING    = 1;  // 容器正在运行
  CONTAINER_EXITED     = 2;  // 容器已退出(正常或异常)
  CONTAINER_UNKNOWN    = 3;  // 状态未知(如运行时重启后丢失状态)
}
生命周期映射:Container 的 state 与 Kubernetes 的 ContainerStatus 中的 State 字段一一对应。例如,CONTAINER_RUNNING 对应 Running 状态,CONTAINER_EXITED 对应 Terminated 状态。适配时需确保状态转换的原子性。

4.4 Image 消息定义与生命周期映射

Image 消息描述容器镜像的元数据,主要用于镜像拉取、列表查询和状态检查:

message Image {
  // 镜像的唯一标识(通常是镜像 ID 的 SHA256 摘要)
  string id = 1;

  // 镜像的仓库标签列表(如 ["nginx:1.21", "nginx:latest"])
  repeated string repo_tags = 2;

  // 镜像的摘要列表(如 ["nginx@sha256:abc123"])
  repeated string repo_digests = 3;

  // 镜像大小(字节)
  uint64 size = 4;

  // 镜像的配置信息(如 Entrypoint、Cmd、Env 等)
  ImageSpec spec = 5;

  // 镜像的创建时间(Unix 时间戳)
  int64 created_at = 6;
}
适配注意:Image 的 id 字段在 CRI 中必须是全局唯一的,通常使用镜像的 manifest digest。如果运行时返回的镜像 ID 不唯一(如 Docker 的短 ID),会导致 kubelet 的镜像缓存冲突。建议在适配层将短 ID 扩展为完整 SHA256。

4.5 生命周期映射关系图

下图展示了 PodSandbox、Container、Image 三者与 Kubernetes Pod 生命周期的映射关系:

CRI 核心对象与 Kubernetes Pod 生命周期映射 Pod Pending Pod Running Pod Failed Pod Succeeded PodSandbox (SANDBOX_NOTREADY) PodSandbox (SANDBOX_READY) Container (CONTAINER_EXITED) Container (CONTAINER_EXITED) 说明: PodSandbox 的 SANDBOX_READY 状态是 Pod 进入 Running 阶段的前提;Container 的 CONTAINER_EXITED 状态对应 Pod 的 Failed 或 Succeeded 阶段。

4.6 关键字段对比表

以下表格对比了 PodSandbox、Container、Image 三个消息中容易混淆的字段:

字段 PodSandbox Container Image
id 沙箱 ID(运行时生成) 容器 ID(运行时生成) 镜像 ID(SHA256 摘要)
metadata PodSandboxMetadata(name、uid、namespace、attempt) ContainerMetadata(name、attempt) 无(镜像无元数据)
state READY / NOTREADY CREATED / RUNNING / EXITED / UNKNOWN 无(镜像无状态)
labels/annotations 继承自 Pod 的 Labels/Annotations 继承自 Container 的 Labels/Annotations 无(镜像元数据通过 repo_tags 表达)
runtime_status 包含 Linux Namespace 路径等运行时信息 包含 ExitCode、Reason 等退出信息 无

4.7 生命周期映射的适配实战要点

  • PodSandbox 创建顺序:必须先创建 PodSandbox 并等待其进入 SANDBOX_READY 状态,才能在该沙箱内创建容器。如果沙箱创建失败,所有容器创建请求都应被拒绝。
  • Container 状态转换:容器从 CONTAINER_CREATED 到 CONTAINER_RUNNING 的转换必须是幂等的。如果 kubelet 在状态查询时发现容器仍处于 CREATED 状态,可能会触发重试或超时。
  • Image 缓存一致性:kubelet 会缓存 Image 列表。如果运行时在镜像拉取后返回的 id 与缓存不一致,会导致镜像被重复拉取。建议在适配层使用 repo_digests 作为缓存键。
  • PodSandbox 清理:当 Pod 被删除时,kubelet 会先停止所有容器,然后调用 RemovePodSandbox。运行时必须确保沙箱内的所有资源(网络命名空间、挂载点等)被彻底清理。
实战案例:在适配 containerd 时,PodSandbox 的 id 对应 containerd 的 SandboxID,而 Container 的 id 对应 ContainerID。两者通过 pod_sandbox_id 字段关联。如果 containerd 的沙箱被意外删除,所有关联容器的状态查询应返回 CONTAINER_UNKNOWN,而不是 CONTAINER_EXITED,以避免 kubelet 误判。

4.8 消息字段与 gRPC 接口的对应关系

CRI 的每个核心消息都对应一组 gRPC 接口。以下表格列出了最常用的接口及其输入/输出消息:

gRPC 接口 请求消息 响应消息 核心字段
RunPodSandbox RunPodSandboxRequest RunPodSandboxResponse config(PodSandboxConfig)→ pod_sandbox_id
CreateContainer CreateContainerRequest CreateContainerResponse pod_sandbox_id, config(ContainerConfig)→ container_id
ListImages ListImagesRequest ListImagesResponse filter → images(repeated Image)
ContainerStatus ContainerStatusRequest ContainerStatusResponse container_id → status(ContainerStatus)
PodSandboxStatus PodSandboxStatusRequest PodSandboxStatusResponse pod_sandbox_id → status(PodSandboxStatus)