runtime-rs 创建pod 流程
现在我已经完整理解了 runtime-rs Sandbox API 创建 pod 的整个流程。让我整理并给出完整的分析。
关键点:在 Sandbox API(CreateSandbox)路径下,创建 pod 的核心调用链是:
- containerd 调用 shim 的
CreateSandboxRPC(containerd 的 sandbox service) - →
SandboxService::create_sandbox→handler_message - → 类型转换
CreateSandboxRequest → SandboxRequest::CreateSandbox - →
RuntimeHandlerManager::handler_sandbox_message - →
sandbox_init_runtime_instance→try_init→init_runtime_handler→VirtContainer::new_instance→VirtSandbox::new
关键区别:在 Sandbox API 的 CreateSandbox 阶段,只创建了 sandbox 对象(VirtSandbox::new),并没有启动 VM。VM 的启动(hypervisor.prepare_vm、start_vm、agent.create_sandbox)发生在后续的 StartSandbox RPC 中,通过 sandbox.start() 完成。
这与普通 task API(CreateContainer)不同——后者在 CreateContainer 时就会通过 task_init_runtime_instance → sandbox.start() 启动 VM。
现在给出完整的分析回答。
runtime-rs Sandbox API 创建 Pod 流程分析
runtime-rs 是 Kata Containers 用 Rust 实现的 runtime,基于 containerd shim v2 协议(ttrpc)。在 Sandbox API(containerd 的 sandbox service)模式下,创建 Pod(PodSandbox)被拆成两个 RPC:CreateSandbox 和 StartSandbox。下面按完整调用链梳理。
一、总体架构与入口
runtime-rs 的 crate 分布:
shim(crates/shim):containerd shim v2 入口(start/delete/run 命令)service(crates/service):ttrpc 服务注册(SandboxService + TaskService)runtimes(crates/runtimes):运行时处理器(默认 VirtContainer)
入口启动:containerd 启动 shim 进程 → main.rs → ShimExecutor::run() → do_run() → ServiceManager::new() + ServiceManager::run()(crates/shim/src/shim_run.rs:47-61)。
ServiceManager::registry_service()(crates/service/src/manager.rs:140-152)注册两类服务:
SandboxService(实现sandbox_async::Sandbox,处理 create/start/stop sandbox 等)TaskService(实现shim_async::Task,处理容器级操作)
二、CreateSandbox 调用链(第一段:仅建 sandbox 对象)
1. ttrpc 请求进入 SandboxService
containerd 调用 shim 的 CreateSandbox RPC。
crates/service/src/sandbox_service.rs:67-76 通过宏 impl_service! 为每个 API 生成 SandboxService 的方法,全部转发到 handler_message()。
impl_service!(
create_sandbox | sandbox_api::CreateSandboxRequest | sandbox_api::CreateSandboxResponse,
start_sandbox | sandbox_api::StartSandboxRequest | sandbox_api::StartSandboxResponse,
...
);
handler_message()(sandbox_service.rs:27-53):
- 把 ttrpc 的
CreateSandboxRequest通过TryFrom转成内部SandboxRequest - 调用
handler.handler_sandbox_message(r) - 把返回的
SandboxResponse转回 ttrpc 响应
2. 请求类型转换
crates/runtimes/common/src/types/trans_from_shim.rs:53-91:
impl TryFrom<sandbox_api::CreateSandboxRequest> for SandboxRequest {
// 从 options.value 解析 CRI 的 PodSandboxConfig
let config = cri_api_v1::PodSandboxConfig::parse_from_bytes(&from.options.value)?;
Ok(SandboxRequest::CreateSandbox(Box::new(SandboxConfig {
sandbox_id: from.sandbox_id.clone(),
hostname: config.hostname,
dns, ...
network_env: SandboxNetworkEnv { netns: Some(from.netns_path), network_created: false },
annotations, hooks: None,
state: State { id, status: Creating, bundle: from.bundle_path, ... },
shm_size: DEFAULT_SHM_SIZE,
})))
}
这里从 CRI 的 PodSandboxConfig(type_url 必须为 SANDBOX_API_V1)提取 hostname、dns、annotations、netns_path 等,构建内部的 SandboxConfig。
3. RuntimeHandlerManager 分发
crates/runtimes/src/manager.rs:482-496:
pub async fn handler_sandbox_message(&self, req: SandboxRequest) -> Result<SandboxResponse> {
if let SandboxRequest::CreateSandbox(sandbox_config) = req {
let config = sandbox_config.deref().clone();
self.sandbox_init_runtime_instance(config)
.await
.context("init sandboxed runtime")?;
Ok(SandboxResponse::CreateSandbox)
## CreateSandbox 特殊在哪(为什么不能一起放进 handler_sandbox_request)
## 1. **生命周期地位完全不一样**
## CreateSandbox = **沙箱的构造阶段**:这一步要新建 runtime 实例、拉起虚拟机、创建沙箱资源,是从无到有的过程。
## - 其它操作(销毁、启停、查询):都是**沙箱已经存在之后,对已有沙箱实例做操作**;
## - 其它操作都依赖沙箱实例已经存在;CreateSandbox 是负责**把这个实例造出来**。
## 所以它的前置依赖、校验逻辑、资源模型天然不同。
} else {
self.handler_sandbox_request(req).await.context("handler request")
}
}
4. 初始化 runtime 实例(sandbox 专用路径)
sandbox_init_runtime_instance(manager.rs:471-479)→ try_init(sandbox_config, None, &None)。
注意这里与普通 task API 的 task_init_runtime_instance(CreateContainer 路径)不同:sandbox API 路径没有 OCI spec(spec=None),只传 SandboxConfig。
try_init(manager.rs:171-278)关键步骤:
- 初始化各 container 类型(LinuxContainer/WasmContainer/VirtContainer 的
init()) load_config(annotations, options)加载 Toml 配置(hypervisor、runtime 等)- 选择 hypervisor(QEMU/cloud-hypervisor/dragonball/firecracker 等)
- rootless 配置处理
InitialSizeManager静态资源尺寸管理(从 annotations/spec 获取 sandbox 大小)init_runtime_handler(...)→ 创建真正的 runtime 实例
init_runtime_handler(manager.rs:122-168)根据 config.runtime.name 选择 handler(默认 VirtContainer),调用:
let runtime_instance = runtime_handler.new_instance(
&self.id, self.msg_sender.clone(), config.clone(),
init_size_manager, sandbox_config,
).await?;
5. VirtContainer 创建 RuntimeInstance
crates/runtimes/virt_container/src/lib.rs:136-191 new_instance():
- 根据
factory.enable_template决定是模板启动还是新建 hypervisor/agent - 创建
ResourceManager(设备、网络、rootfs 等资源管理) VirtSandbox::new(...)创建 sandbox 对象- 创建
VirtContainerManager(容器管理) - 返回
RuntimeInstance { sandbox, container_manager }
6. VirtSandbox::new —— 仅构造对象,不启动 VM
crates/runtimes/virt_container/src/sandbox.rs:173-201:
pub async fn new(...) -> Result<Self> {
Ok(Self {
sid, msg_sender, agent, hypervisor, resource_manager,
monitor: Arc::new(HealthCheck::new(true, keep_abnormal)),
sandbox_config: Some(sandbox_config),
factory: Some(factory),
...
})
}
重要:new() 只是把 sandbox 对象构造出来并保存 sandbox_config,此时 VM 尚未创建/启动。inner.state 处于 SandboxState::Init。
try_init 最后还会启动一个 shim 管理 HTTP server(manager.rs:268-275,MgmtServer)。
CreateSandbox 到此结束,返回 SandboxResponse::CreateSandbox(空响应)给 containerd。
三、StartSandbox 调用链(第二段:真正启动 VM 与 guest sandbox)
1. 请求分发
containerd 随后调用 StartSandbox RPC → SandboxService → SandboxRequest::StartSandbox → handler_sandbox_request(manager.rs:597-615):
SandboxRequest::StartSandbox(_) => {
sandbox.start().await.context("start sandbox in sandbox handler")?;
Ok(SandboxResponse::StartSandbox(...))
}
2. VirtSandbox::start() —— 核心启动流程
crates/runtimes/virt_container/src/sandbox.rs:991-1214 start():
- 状态检查:仅当
state == SandboxState::Init时继续 hypervisor.prepare_vm(...)(sandbox.rs:1015-1023):准备 VM(netns、annotations、selinux label)prepare_for_start_sandbox(...)(sandbox.rs:230-334):生成各类设备资源配置- vm socket 配置(vsock/hybrid vsock)
- 网络配置(network_resource)
- sharefs(virtiofs 等共享文件系统)
- VM rootfs(initrd 或 image 块设备)
- guest extension images
- protection device(confidential guest 的 initdata)
- CDI / VFIO 冷插拔设备
- PCIe port
resource_manager.prepare_before_start_vm(resources)(sandbox.rs:1031-1034):把设备接入 device manager(提前插入,供 VM boot 时使用)hypervisor.start_vm(10_000)(sandbox.rs:1037):真正启动 VMM(QEMU/CH 等),传入 10s 超时- 后台
tokio::spawn监听wait_vm(),VM 退出时记录状态 - 执行 OCI prestart / createRuntime hooks
- 若配置了网络,执行
resource_manager.handle_network(...)进行网络配置(netns 处理) - 连接 agent(sandbox.rs:1110-1121):
hypervisor.get_agent_socket()获取 vsock 地址 →agent.start(&address)建立到 guest 内 kata-agent 的 ttrpc 连接 →set_agent_policy()下发策略 resource_manager.setup_after_start_vm():VM 启动后的设备设置agent.create_sandbox(req)(sandbox.rs:1128-1153):向 guest 内 agent 发送CreateSandboxRequest(hostname、dns、storages、sandbox_id、guest_hook_path、kernel_modules 等)——这才是真正在 guest 中创建 sandbox 环境- 更新状态为
SandboxState::Running,记录 created_at store_guest_details():向 agent 查询 guest 详情(内存块大小、hotplug 支持)- 启动 OOM watcher(后台循环
agent.get_oom_event) monitor.start(id, agent):健康检查self.save():持久化 sandbox 状态
StartSandbox 完成后,Pod(VM + guest sandbox)才算真正创建并运行。
四、后续 Pod 内容器
Pod 里的业务容器通过 Task API 创建(而非 Sandbox API):containerd 对每个容器调用 shim 的 CreateContainer → TaskService → TaskRequest::CreateContainer → task_init_runtime_instance + container_manager.create_container(...)(manager.rs:499-566)。此时 runtime_instance 已存在(由 CreateSandbox 创建),sandbox.start() 会因状态已为 Running 而直接返回,然后创建容器并调用 guest agent 的 create_container。
五、流程图总结
containerd (Sandbox API)
│
├─ CreateSandbox RPC
│ → SandboxService::create_sandbox (ttrpc)
│ → SandboxRequest::CreateSandbox (转 CRI PodSandboxConfig)
│ → RuntimeHandlerManager::handler_sandbox_message
│ → sandbox_init_runtime_instance → try_init
│ → VirtContainer::new_instance
│ → VirtSandbox::new ← 仅构造对象 (state=Init),不启动 VM
│ → 返回 CreateSandboxResponse
│
└─ StartSandbox RPC
→ SandboxService::start_sandbox
→ SandboxRequest::StartSandbox
→ handler_sandbox_request → VirtSandbox::start()
├─ hypervisor.prepare_vm()
├─ prepare_for_start_sandbox() (设备/网络/rootfs/sharefs)
├─ resource_manager.prepare_before_start_vm()
├─ hypervisor.start_vm() ← 启动 VMM
├─ agent.start(agent_socket) ← 连接 guest agent
├─ resource_manager.setup_after_start_vm()
├─ agent.create_sandbox(req) ← 在 guest 中创建 sandbox
├─ 状态 → Running + store_guest_details + OOM watcher + monitor
└─ 返回 StartSandboxResponse
关键要点:
- Sandbox API 下,创建 Pod 是"先 Create(建对象)→ 后 Start(启 VM)"的两阶段过程,区别于 Task API 的 CreateContainer 会在创建时就
sandbox.start()启动 VM。 - 沙箱内真正的
agent.create_sandbox是在StartSandbox阶段、VM 启动并连上 agent 之后才调用的。 - 所有 hypervisor 细节(QEMU/CH/dragonball 等)通过
hypervisor: Hypervisortrait 抽象,guest 内交互通过agent: Agenttrait 抽象(crates/agent/src/lib.rs:60定义了create_sandbox)。
需要我进一步深入某一部分(例如 hypervisor.start_vm 具体如何拉起 QEMU、或 agent.create_sandbox 如何与 guest 内 kata-agent 交互)吗?

浙公网安备 33010602011771号