4. CRI 消息流:PodSandbox 创建、容器启动、容器停止、PodSandbox 删除的完整消息序列

CRI(Container Runtime Interface)定义了 kubelet 与容器运行时之间的 gRPC 契约。理解 PodSandbox 与容器的完整生命周期消息序列,是掌握容器运行时控制面测试验证的基础。本章以标准 CRI 接口为蓝本,按时间顺序拆解四个核心阶段的消息交互。

4.1 消息序列总览

一个 Pod 从创建到销毁,在 CRI 层面经历四个关键阶段:PodSandbox 创建 → 容器创建与启动 → 容器停止与删除 → PodSandbox 停止与删除。每个阶段由若干 gRPC 请求/响应组成,kubelet 作为客户端,容器运行时作为服务端。

关键概念:PodSandbox 是 CRI 中用于隔离 Pod 的沙箱环境(如 Linux 的 Network Namespace、PID Namespace),容器必须依附于某个 PodSandbox 运行。PodSandbox 的生命周期独立于容器,但容器不能脱离 PodSandbox 存在。
CRI 消息流总览:PodSandbox 与容器生命周期 CRI 消息流总览:PodSandbox 与容器生命周期 阶段 1 PodSandbox 创建 RunPodSandbox → PodSandboxID 阶段 2 容器创建与启动 CreateContainer → StartContainer 阶段 3 容器停止与删除 StopContainer → RemoveContainer 阶段 4 PodSandbox 删除 StopPodSandbox → RemovePodSandbox 时间轴(t →) 详细消息序列(kubelet → 容器运行时) ① RunPodSandbox(config) → PodSandboxID ② CreateContainer(podSandboxID, config) → ContainerID ③ StartContainer(containerID) → 空响应 ④ StopContainer(containerID, timeout) → 空响应 ⑤ RemoveContainer(containerID) → 空响应 ⑥ StopPodSandbox(podSandboxID) → 空响应 ⑦ RemovePodSandbox(podSandboxID) → 空响应

4.2 阶段一:PodSandbox 创建

PodSandbox 创建是整个 Pod 生命周期的起点。kubelet 调用 RunPodSandbox 接口,容器运行时负责创建隔离环境(如网络栈、PID 命名空间、挂载点等),并返回唯一的 PodSandboxID。

4.2.1 RunPodSandbox 请求参数

参数 类型 说明
config PodSandboxConfig 包含 Pod 名称、UID、命名空间、DNS 配置、端口映射、标签、注解等
runtime_handler string 可选,指定运行时类(如 gvisor、kata)

4.2.2 RunPodSandbox 响应

字段 类型 说明
pod_sandbox_id string 全局唯一的 PodSandbox 标识符,后续所有操作依赖此 ID
测试要点:验证 PodSandbox 创建后,网络命名空间是否独立、端口映射是否生效、DNS 配置是否正确注入。建议在测试中检查 PodSandboxStatus 接口返回的 state 是否为 SANDBOX_READY。

4.3 阶段二:容器创建与启动

PodSandbox 就绪后,kubelet 依次调用 CreateContainer 和 StartContainer。这两个接口必须按顺序调用,CreateContainer 仅创建容器对象(不运行进程),StartContainer 才真正启动容器主进程。

4.3.1 CreateContainer 请求参数

参数 类型 说明
pod_sandbox_id string 目标 PodSandbox 的 ID
config ContainerConfig 镜像、命令、环境变量、挂载卷、资源限制、安全上下文等
sandbox_config PodSandboxConfig 创建 PodSandbox 时使用的配置(用于校验一致性)

4.3.2 StartContainer 请求参数

参数 类型 说明
container_id string CreateContainer 返回的容器 ID
常见陷阱:某些运行时实现中,CreateContainer 可能隐式执行部分初始化(如挂载根文件系统),但主进程必须在 StartContainer 之后才运行。测试时务必验证容器状态转换:CONTAINER_CREATED → CONTAINER_RUNNING。

4.4 阶段三:容器停止与删除

容器停止分为优雅停止和强制停止。kubelet 调用 StopContainer 时传入超时时间,容器运行时先发送 SIGTERM,超时后发送 SIGKILL。容器完全停止后,kubelet 调用 RemoveContainer 清理容器资源。

4.4.1 StopContainer 请求参数

参数 类型 说明
container_id string 要停止的容器 ID
timeout int64 优雅停止等待时间(秒),0 表示立即 SIGKILL

4.4.2 容器停止流程

  1. kubelet 调用 StopContainer,传入 container_id 和 timeout。
  2. 运行时发送 SIGTERM 给容器主进程(PID 1)。
  3. 等待 timeout 秒,检查容器是否自行退出。
  4. 超时后发送 SIGKILL,强制终止容器进程。
  5. 运行时更新容器状态 为 CONTAINER_EXITED。
  6. kubelet 调用 RemoveContainer,清理容器文件系统、日志、挂载等。
测试验证:使用 ContainerStatus 接口检查容器退出码(exit_code)和退出原因(reason)。对于正常停止,exit_code 应为 0;对于超时强杀,exit_code 应为 137(SIGKILL)或 143(SIGTERM)。

4.5 阶段四:PodSandbox 停止与删除

PodSandbox 的停止与删除是 Pod 生命周期的最后阶段。kubelet 先调用 StopPodSandbox 停止 PodSandbox 内的所有网络和资源隔离,再调用 RemovePodSandbox 彻底删除 PodSandbox。

4.5.1 StopPodSandbox 与 RemovePodSandbox 对比

接口 作用 幂等性 典型耗时
StopPodSandbox 停止网络、释放端口、清理网络策略 是(多次调用无副作用) 毫秒级
RemovePodSandbox 删除 PodSandbox 元数据、清理残留资源 是(已删除的 PodSandbox 返回成功) 毫秒级

4.5.2 完整消息序列代码示例

以下代码展示使用 CRI 客户端(伪代码)模拟完整消息序列:

// 阶段 1:创建 PodSandbox
podSandboxConfig := &runtimeapi.PodSandboxConfig{
  Metadata: &runtimeapi.PodSandboxMetadata{
    Name:      "test-pod",
    Uid:       "uid-12345",
    Namespace: "default",
  },
  DnsConfig: &runtimeapi.DNSConfig{
    Servers: []string{"8.8.8.8"},
  },
}
createResp, err := runtimeService.RunPodSandbox(podSandboxConfig, "")
podSandboxID := createResp.PodSandboxId

// 阶段 2:创建并启动容器
containerConfig := &runtimeapi.ContainerConfig{
  Metadata: &runtimeapi.ContainerMetadata{Name: "test-container"},
  Image:    &runtimeapi.ImageSpec{Image: "nginx:latest"},
  Command:  []string{"nginx", "-g", "daemon off;"},
}
createContainerResp, err := runtimeService.CreateContainer(
  podSandboxID, containerConfig, podSandboxConfig,
)
containerID := createContainerResp.ContainerId
err = runtimeService.StartContainer(containerID)

// 阶段 3:停止并删除容器
err = runtimeService.StopContainer(containerID, 10) // 10 秒超时
err = runtimeService.RemoveContainer(containerID)

// 阶段 4:停止并删除 PodSandbox
err = runtimeService.StopPodSandbox(podSandboxID)
err = runtimeService.RemovePodSandbox(podSandboxID)

4.6 消息序列的测试验证策略

针对 CRI 消息序列的测试验证,建议从以下维度设计测试用例:

  • 正向流程验证:按标准顺序执行完整消息序列,验证每个接口返回成功,状态机转换正确。
  • 异常顺序验证:尝试在 PodSandbox 未创建时创建容器、在容器未停止时删除 PodSandbox,验证运行时返回明确的错误码和错误信息。
  • 超时与重试验证:模拟 StopContainer 超时场景,验证 SIGKILL 是否生效;模拟网络抖动,验证 kubelet 重试逻辑。
  • 幂等性验证:多次调用 RemoveContainer、RemovePodSandbox,验证不会产生错误或资源泄漏。
  • 并发验证:同时创建多个 PodSandbox 和容器,验证隔离性和资源竞争处理。
测试工具推荐:使用 crictl 命令行工具手动模拟消息序列,或使用 critest(CRI 验证套件)自动执行标准测试集。对于自定义运行时,建议编写 gRPC 客户端直接调用 CRI 接口进行细粒度测试。

4.7 本章小结

CRI 消息序列定义了 kubelet 与容器运行时之间的严格契约。PodSandbox 创建、容器启动、容器停止、PodSandbox 删除四个阶段必须按顺序执行,每个接口的输入输出都有明确的语义。测试验证时,不仅要覆盖正向流程,还要关注异常顺序、超时处理、幂等性和并发场景,确保容器运行时实现符合 CRI 规范。