4. CRI 消息流:PodSandbox 创建、容器启动、容器停止、PodSandbox 删除的完整消息序列
CRI(Container Runtime Interface)定义了 kubelet 与容器运行时之间的 gRPC 契约。理解 PodSandbox 与容器的完整生命周期消息序列,是掌握容器运行时控制面测试验证的基础。本章以标准 CRI 接口为蓝本,按时间顺序拆解四个核心阶段的消息交互。
4.1 消息序列总览
一个 Pod 从创建到销毁,在 CRI 层面经历四个关键阶段:PodSandbox 创建 → 容器创建与启动 → 容器停止与删除 → PodSandbox 停止与删除。每个阶段由若干 gRPC 请求/响应组成,kubelet 作为客户端,容器运行时作为服务端。
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 |
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 容器停止流程
- kubelet 调用 StopContainer,传入 container_id 和 timeout。
- 运行时发送 SIGTERM 给容器主进程(PID 1)。
- 等待 timeout 秒,检查容器是否自行退出。
- 超时后发送 SIGKILL,强制终止容器进程。
- 运行时更新容器状态 为
CONTAINER_EXITED。 - 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 规范。