runtime-rs 创建pod 流程

现在我已经完整理解了 runtime-rs Sandbox API 创建 pod 的整个流程。让我整理并给出完整的分析。

关键点:在 Sandbox API(CreateSandbox)路径下,创建 pod 的核心调用链是:

  1. containerd 调用 shim 的 CreateSandbox RPC(containerd 的 sandbox service)
  2. → SandboxService::create_sandbox → handler_message
  3. → 类型转换 CreateSandboxRequest → SandboxRequest::CreateSandbox
  4. → RuntimeHandlerManager::handler_sandbox_message
  5. → 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):

  1. 把 ttrpc 的 CreateSandboxRequest 通过 TryFrom 转成内部 SandboxRequest
  2. 调用 handler.handler_sandbox_message(r)
  3. 把返回的 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)关键步骤:

  1. 初始化各 container 类型(LinuxContainer/WasmContainer/VirtContainer 的 init())
  2. load_config(annotations, options) 加载 Toml 配置(hypervisor、runtime 等)
  3. 选择 hypervisor(QEMU/cloud-hypervisor/dragonball/firecracker 等)
  4. rootless 配置处理
  5. InitialSizeManager 静态资源尺寸管理(从 annotations/spec 获取 sandbox 大小)
  6. 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():

  1. 根据 factory.enable_template 决定是模板启动还是新建 hypervisor/agent
  2. 创建 ResourceManager(设备、网络、rootfs 等资源管理)
  3. VirtSandbox::new(...) 创建 sandbox 对象
  4. 创建 VirtContainerManager(容器管理)
  5. 返回 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():

  1. 状态检查:仅当 state == SandboxState::Init 时继续
  2. hypervisor.prepare_vm(...)(sandbox.rs:1015-1023):准备 VM(netns、annotations、selinux label)
  3. 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
  4. resource_manager.prepare_before_start_vm(resources)(sandbox.rs:1031-1034):把设备接入 device manager(提前插入,供 VM boot 时使用)
  5. hypervisor.start_vm(10_000)(sandbox.rs:1037):真正启动 VMM(QEMU/CH 等),传入 10s 超时
  6. 后台 tokio::spawn 监听 wait_vm(),VM 退出时记录状态
  7. 执行 OCI prestart / createRuntime hooks
  8. 若配置了网络,执行 resource_manager.handle_network(...) 进行网络配置(netns 处理)
  9. 连接 agent(sandbox.rs:1110-1121):hypervisor.get_agent_socket() 获取 vsock 地址 → agent.start(&address) 建立到 guest 内 kata-agent 的 ttrpc 连接 → set_agent_policy() 下发策略
  10. resource_manager.setup_after_start_vm():VM 启动后的设备设置
  11. agent.create_sandbox(req)(sandbox.rs:1128-1153):向 guest 内 agent 发送 CreateSandboxRequest(hostname、dns、storages、sandbox_id、guest_hook_path、kernel_modules 等)——这才是真正在 guest 中创建 sandbox 环境
  12. 更新状态为 SandboxState::Running,记录 created_at
  13. store_guest_details():向 agent 查询 guest 详情(内存块大小、hotplug 支持)
  14. 启动 OOM watcher(后台循环 agent.get_oom_event)
  15. monitor.start(id, agent):健康检查
  16. 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

关键要点:

  1. Sandbox API 下,创建 Pod 是"先 Create(建对象)→ 后 Start(启 VM)"的两阶段过程,区别于 Task API 的 CreateContainer 会在创建时就 sandbox.start() 启动 VM。
  2. 沙箱内真正的 agent.create_sandbox 是在 StartSandbox 阶段、VM 启动并连上 agent 之后才调用的。
  3. 所有 hypervisor 细节(QEMU/CH/dragonball 等)通过 hypervisor: Hypervisor trait 抽象,guest 内交互通过 agent: Agent trait 抽象(crates/agent/src/lib.rs:60 定义了 create_sandbox)。

需要我进一步深入某一部分(例如 hypervisor.start_vm 具体如何拉起 QEMU、或 agent.create_sandbox 如何与 guest 内 kata-agent 交互)吗?

posted @ 2026-10-21 10:52  rincloud  阅读(10)  评论(0)    收藏  举报