Cloud Hypervisor
1 - 启动
1.1 - 启动概述
Kuasar 中 Cloud Hypervisor microVM 启动全流程
1. 整体架构
Kuasar 通过以下组件实现 Cloud Hypervisor microVM 的管理:
- containerd-shim-kuasar-vmm-v2: containerd 的 shim 插件
- kuasar-vmm-sandboxer-clh: Cloud Hypervisor 专用的 sandbox 服务
- CloudHypervisorVM: VM 实例管理器
- CloudHypervisorVMFactory: VM 工厂类
2. 详细启动流程
第一阶段:Sandbox 创建 (create)
-
containerd 调用 shim
- containerd 通过 CRI API 调用
containerd-shim-kuasar-vmm-v2 - shim 连接到
kuasar-vmm-sandboxer-clh服务
- containerd 通过 CRI API 调用
-
Sandboxer 初始化
rustCopy code1// vmm/sandbox/src/bin/cloud_hypervisor/main.rs 2let mut sandboxer: KuasarSandboxer<CloudHypervisorVMFactory, CloudHypervisorHooks> = 3 KuasarSandboxer::new(config.sandbox, config.hypervisor, CloudHypervisorHooks::default()); -
VM 实例创建
rustCopy code1// CloudHypervisorVMFactory::create_vm 2let mut vm = CloudHypervisorVM::new(id, &netns, &s.base_dir, &self.vm_config); -
设备配置
- 添加 rootfs (pmem 设备):
Pmem::new("rootfs", &image_path, true) - 添加随机数生成器:
Rng::new("rng", &entropy_source) - 添加 vsock 设备:
Vsock::new(3, &guest_socket_path, "vsock") - 添加控制台设备:
Console::new(&console_path, "console") - 添加文件系统设备:
Fs::new("fs", &virtiofsd_socket, "kuasar")
- 添加 rootfs (pmem 设备):
-
Cgroup 设置
rustCopy code1sandbox_cgroups = SandboxCgroup::create_sandbox_cgroups(&cgroup_parent_path, &s.sandbox.id)?; 2sandbox_cgroups.update_res_for_sandbox_cgroups(&s.sandbox)?;
第二阶段:VM 启动 (start)
-
网络准备
rustCopy code1if !sandbox.data.netns.is_empty() { 2 sandbox.prepare_network().await?; 3} -
启动 virtiofsd
rustCopy code1async fn start_virtiofsd(&self) -> Result<u32> { 2 create_dir_all(&self.virtiofsd_config.shared_dir).await?; 3 let params = self.virtiofsd_config.to_cmdline_params("--"); 4 let mut cmd = tokio::process::Command::new(&self.virtiofsd_config.path); 5 cmd.args(params.as_slice()); 6 // 在指定的网络命名空间中启动 7 set_cmd_netns(&mut cmd, self.netns.to_string())?; 8} -
启动 Cloud Hypervisor
rustCopy code1async fn start(&mut self) -> Result<u32> { 2 let mut params = self.config.to_cmdline_params("--"); 3 for d in self.devices.iter() { 4 params.extend(d.to_cmdline_params("--")); 5 } 6 7 let mut cmd = tokio::process::Command::new(&self.config.path); 8 cmd.args(params.as_slice()); 9 set_cmd_netns(&mut cmd, self.netns.to_string())?; 10 let child = cmd.spawn()?; 11} -
API 客户端创建
rustCopy code1match self.create_client().await { 2 Ok(client) => self.client = Some(client), 3 Err(e) => return Err(e), 4};
第三阶段:Guest 系统配置
-
内核启动参数
rustCopy code1const DEFAULT_KERNEL_PARAMS: &str = "console=hvc0 \ 2root=/dev/pmem0p1 \ 3rootflags=data=ordered,errors=remount-ro \ 4ro rootfstype=ext4 \ 5task.sharefs_type=virtiofs"; -
Agent 通信建立
- VM 通过 vsock 设备与 host 通信
- agent_socket:
"hvsock://{guest_socket_path}:1024"
-
文件系统挂载
- rootfs 通过 pmem 设备挂载
- 共享目录通过 virtiofs 挂载
第四阶段:监控和管理
-
进程监控
rustCopy code1let sandbox_clone = sandbox_mutex.clone(); 2monitor(sandbox_clone); -
Cgroup 管理
rust Copy code 1sandbox.add_to_cgroup().await?;
3. 关键配置参数
VM 配置示例
tomlCopy code1[hypervisor]
2path = "/usr/local/bin/cloud-hypervisor"
3vcpus = 1
4memory_in_mb = 1024
5kernel_path = "/var/lib/kuasar/vmlinux.bin"
6image_path = "/var/lib/kuasar/kuasar.img"
7hugepages = true
8entropy_source = "/dev/urandom"
9
10[hypervisor.virtiofsd]
11path = "/usr/local/bin/virtiofsd"
12log_level = "info"
13cache = "never"
14thread_pool_size = 4
生成的 Cloud Hypervisor 命令行
bashCopy code1cloud-hypervisor \
2 --api-socket /path/to/api.sock \
3 --cpus boot=1 \
4 --memory size=1073741824,shared=on,hugepages=on \
5 --kernel /var/lib/kuasar/vmlinux.bin \
6 --cmdline "console=hvc0 root=/dev/pmem0p1 ..." \
7 --pmem file=/var/lib/kuasar/kuasar.img,id=rootfs,readonly=on \
8 --rng src=/dev/urandom,iommu=off \
9 --vsock cid=3,socket=/path/to/task.vsock \
10 --console file=/tmp/task.log \
11 --fs tag=kuasar,socket=/path/to/virtiofs.sock
4. 设备热插拔支持
Cloud Hypervisor 还支持设备的热插拔:
rustCopy code1async fn hot_attach(&mut self, device_info: DeviceInfo) -> Result<(BusType, String)> {
2 let client = self.get_client()?;
3 let addr = client.hot_attach(device_info)?;
4 Ok((BusType::PCI, addr))
5}
6
7async fn hot_detach(&mut self, id: &str) -> Result<()> {
8 let client = self.get_client()?;
9 client.hot_detach(id)?;
10 Ok(())
11}
5. 错误处理和恢复
系统具备完整的错误处理和恢复机制:
- 启动失败时自动清理网络和 cgroup
- 支持 sandbox 状态恢复
- 进程监控和异常退出处理
这个流程展示了 Kuasar 如何将 Cloud Hypervisor 集成到 containerd 生态系统中,提供了完整的 microVM 容器运行时解决方案。
1.2 - 文件
内容来自 code wiki.
文件传递
问: 仅仅考虑通过 cloud hypervisor 启动 microvm 的过程,kuasar 是如何将容器镜像,虚拟机 rootfs,kernel 等文件传递给 cloud hypervisor 并启动 microvm 实例的?
在 Kuasar 中,通过 Cloud Hypervisor 启动 MicroVM 实例时,容器镜像、虚拟机 rootfs 和 kernel 等文件通过以下方式传递和使用:
- 内核 (Kernel):
- Kuasar 针对不同的 VMMs(包括 Cloud Hypervisor)提供定制化的 Linux 内核构建框架,目的是优化内存占用和启动时间,主要采用“增量定制”方法,即只添加必要的特性,而不是从完整配置中删除功能,如 Linux Kernel Customization for MicroVMs 所述。
- 构建好的内核文件(例如
vmlinux.bin)会被放置在宿主机上的特定路径,例如/var/lib/kuasar/vmlinux.bin。 - 在启动
cloud-hypervisor进程时,kernel_path参数会被设置为这个文件的路径,从而将内核文件传递给 hypervisor,如vmm/sandbox/src/cloud_hypervisor/config.rs和vmm/sandbox/src/cloud_hypervisor/mod.rs中的CloudHypervisorConfig结构体所示。CloudHypervisorConfig的kernel字段直接引用了kernel_path。
- 虚拟机 Root Filesystem (Rootfs):
- Kuasar 提供了构建 guest OS rootfs 的脚本,如
vmm/scripts/image/build.sh,支持生成 rootfs 或完整的引导镜像,如 Guest OS Image and Root Filesystem Generation 所述。 - 构建好的 rootfs 镜像文件(例如
kuasar.img)也会被放置在宿主机上的特定路径,例如/var/lib/kuasar/kuasar.img。 CloudHypervisorVMFactory在创建 VM 实例时,如果image_path不为空,会将这个镜像文件作为Pmem设备添加到虚拟机中,其path字段指向image_path,如vmm/sandbox/src/cloud_hypervisor/factory.rs所示。这意味着 rootfs 是以持久内存(Pmem)设备的形式挂载到 MicroVM 中的,具体通过Pmem::new("rootfs", &self.vm_config.common.image_path, true)实现。
- Kuasar 提供了构建 guest OS rootfs 的脚本,如
- 容器镜像 (Container Image):
- Kuasar 的 MicroVM 沙箱通过
vmm-task进程在 guest VM 内部管理容器生命周期。容器镜像的内容通过virtio-fs设备共享给 guest VM,如 [MicroVM Sandbox (VMM)](#microvm-sandbox-vmm) 和 VMM Orchestration and Hypervisor Integration 所述。 - 在
CloudHypervisorVM::new方法中,会创建一个virtiofsd_config,其中socket_path和shared_dir会被配置,如vmm/sandbox/src/cloud_hypervisor/mod.rs所示。shared_dir通常是base_dir下的SHARED_DIR_SUFFIX目录。 CloudHypervisorVMFactory会在创建 VM 时,如果virtiofsd_config.socket_path不为空,则添加一个virtio-fs设备 (Fs::new),将其socket路径设置为virtiofsd_config.socket_path,并使用tag="kuasar"。这个virtio-fs设备允许 guest VM 访问宿主机上的共享目录,即容器镜像层叠文件系统等内容,如vmm/sandbox/src/cloud_hypervisor/factory.rs所示。virtiofsd进程由CloudHypervisorVM负责启动,它监听一个 socket 并在宿主机上共享virtiofsd_config.shared_dir目录,如vmm/sandbox/src/cloud_hypervisor/mod.rs中的start_virtiofsd方法所示。guest VM 通过virtio-fs驱动与virtiofsd进程通信,从而访问共享文件。DEFAULT_KERNEL_PARAMS中包含task.sharefs_type=virtiofs,这表明 guest 内核会启用 virtiofs 相关的参数,如vmm/sandbox/src/cloud_hypervisor/config.rs所示。
- Kuasar 的 MicroVM 沙箱通过
总结来说,Kuasar 通过以下机制将这些文件传递给 Cloud Hypervisor 并启动 MicroVM:
- 文件路径配置:内核和 rootfs 的路径在
CloudHypervisorVMConfig配置中指定,并在CloudHypervisorVM实例创建时用于构建cloud-hypervisor的命令行参数。 - 设备抽象:rootfs 作为
Pmem设备添加到虚拟机中,由CloudHypervisorVMFactory处理。 - VirtioFS 共享:容器镜像内容通过
virtio-fs共享机制传递。宿主机上运行的virtiofsd进程将宿主机上的共享目录暴露给 guest VM,guest VM 通过virtio-fs设备访问这些数据。
KuasarSandboxer 的 Cloud Hypervisor 后端入口点位于 vmm/sandbox/src/bin/cloud_hypervisor/main.rs。它加载配置 vmm/sandbox/src/cloud_hypervisor/config.rs,并使用 CloudHypervisorVMFactory vmm/sandbox/src/cloud_hypervisor/factory.rs 来创建和管理 CloudHypervisorVM 实例。实际启动 cloud-hypervisor 进程和 virtiofsd 进程的逻辑则在 CloudHypervisorVM vmm/sandbox/src/cloud_hypervisor/mod.rs 中实现。
Kernel 生成与传递
graph TD
subgraph Host System
A[Kuasar Build Environment] --> B(Linux Kernel Customization)
B --> C{build-kernel.sh}
C --Tailored Kernel Configuration--> D[Kernel Source Directory]
D --Kernel fragments--> E(Merge Kernel Fragments)
E --Final .config--> F(Kernel Compilation)
F --vmlinux.bin--> G[Kernel Binary: /var/lib/kuasar/vmlinux.bin]
end
subgraph Cloud Hypervisor VM Launch
G --> H{Cloud Hypervisor Process}
H --kernel_path parameter--> I[MicroVM Instance]
end
- 内核定制:Kuasar 为 Cloud Hypervisor 定制 Linux 内核。此过程采用“增量定制”方法,即只添加必要的特性,以优化内存占用和启动时间,如 Linux Kernel Customization for MicroVMs 所述。
- 构建脚本:
build-kernel.sh脚本 (vmm/scripts/kernel/build-kernel/build-kernel.sh) 负责整个内核构建过程。它根据目标架构 (--arch) 和内核类型 (--kernel-type) 动态选择配置片段。 - 配置合并:脚本使用 kernel build system utilities 合并这些配置片段,生成最终的
.config文件 (vmm/scripts/kernel/build-kernel/build-kernel.sh)。 - 内核编译:
.config文件用于编译内核,生成vmlinux.bin或bzImage,具体取决于 VMM 和架构,例如 Cloud Hypervisor 的vmlinux.bin。 - 文件放置:编译好的内核文件(如
vmlinux.bin)会被放置在宿主机上的特定路径,通常是/var/lib/kuasar/vmlinux.bin(docs/vmm/README.md)。 - 传递给 MicroVM:在启动
cloud-hypervisor进程时,Kuasar 会将kernel_path参数设置为这个内核文件的路径 (CloudHypervisorConfig在vmm/sandbox/src/cloud_hypervisor/config.rs)。Cloud Hypervisor 会加载此内核并启动 MicroVM 实例。
sequenceDiagram
participant KuasarBuildEnv as Kuasar Build Environment
participant BuildKernelSh as build-kernel.sh
participant KernelSourceDir as Kernel Source Directory
participant CloudHypervisorConfig as Cloud Hypervisor Config
participant CloudHypervisorVMM as Cloud Hypervisor VMM Process
participant MicroVMGuest as MicroVM Guest
KuasarBuildEnv->>BuildKernelSh: 启动内核构建
BuildKernelSh->>KernelSourceDir: 根据架构/类型选择内核片段
KernelSourceDir-->>BuildKernelSh: 提供内核片段
BuildKernelSh->>BuildKernelSh: 合并片段生成 .config
BuildKernelSh->>BuildKernelSh: 编译内核 (vmlinux.bin)
BuildKernelSh-->>KuasarBuildEnv: 输出 vmlinux.bin 到 /var/lib/kuasar/vmlinux.bin
KuasarBuildEnv->>CloudHypervisorConfig: 配置 kernel_path = /var/lib/kuasar/vmlinux.bin
CloudHypervisorConfig->>CloudHypervisorVMM: 将 kernel_path 作为参数传递给 Cloud Hypervisor
CloudHypervisorVMM->>MicroVMGuest: 加载 vmlinux.bin 作为 guest 内核
Root Filesystem 生成与传递
graph TD
subgraph Host System
A[Kuasar Build Environment] --> B(Guest OS Rootfs Build Scripts)
B --build.sh--> C{Containerized Build Environment}
C --centos/build_rootfs.sh--> D[Install Rust & Go]
D --> E(Compile vmm-task & runc)
E --> F[Populate Rootfs Directory: /tmp/kuasar-rootfs]
F --build_image.sh--> G[Validate Rootfs]
G --> H(Create Raw Disk Image)
H --> I(Partition & Format)
I --Copy Rootfs Content--> J[Rootfs Image: /var/lib/kuasar/kuasar.img]
J --Optional: nsdax utility--> K(NVDIMM/DAX Header Configuration)
K --> J
end
subgraph Cloud Hypervisor VM Launch
J --> L{Cloud Hypervisor Process}
L --image_path parameter (as Pmem device)--> M[MicroVM Instance]
end
- Rootfs 构建脚本:Kuasar 使用
vmm/scripts/image/build.sh脚本来构建 guest OS 的 root filesystem。这个脚本可以在容器化环境中执行构建过程 (vmm/scripts/image/build.sh)。 - 具体构建过程:CentOS-based 的 rootfs 通过
vmm/scripts/image/centos/build_rootfs.sh脚本构建 (vmm/scripts/image/centos/build_rootfs.sh)。这包括:- 安装 Rust 工具链 (
install_rust.sh) (vmm/scripts/image/install_rust.sh)。 - 构建 Kuasar 的
vmm-task(vmm/scripts/image/centos/build_rootfs.sh)。 - 安装 Go 运行时,编译
runc(vmm/scripts/image/centos/build_rootfs.sh)。 - 将这些二进制文件、
glibc库和 RPM 包复制到临时 rootfs 目录 (vmm/scripts/image/centos/build_rootfs.sh)。
- 安装 Rust 工具链 (
- 镜像生成:
vmm/scripts/image/build_image.sh脚本 (vmm/scripts/image/build_image.sh) 负责将 rootfs 目录转换为一个完整的 Kata Containers 兼容的 rootfs 镜像:- 它验证 rootfs 内容,计算所需磁盘大小。
- 使用
qemu-img创建原始磁盘镜像。 - 使用
parted对镜像进行分区并格式化 (vmm/scripts/image/build_image.sh)。 - 将 rootfs 内容复制到分区中。
- 可选地,使用
nsdax工具配置 NVDIMM/DAX 头,以支持持久内存功能 (vmm/scripts/image/build_image.sh)。
- 文件放置:生成的 rootfs 镜像文件(如
kuasar.img)会被放置在宿主机上的特定路径,通常是/var/lib/kuasar/kuasar.img(docs/vmm/README.md)。 - 传递给 MicroVM:
CloudHypervisorVMFactory在创建 VM 实例时,如果image_path不为空,会将这个镜像文件作为Pmem设备添加到虚拟机中。这意味着 rootfs 是以持久内存设备的形式挂载到 MicroVM 中的。
sequenceDiagram
participant KuasarBuildEnv as Kuasar Build Environment
participant BuildSh as build.sh
participant CentOSBuildRootfsSh as centos/build_rootfs.sh
participant TmpRootfsDir as /tmp/kuasar-rootfs (Temporary)
participant BuildImageSh as build_image.sh
participant QemuImgParted as qemu-img / parted
participant NsdaxUtility as nsdax utility (Optional)
participant CloudHypervisorConfig as Cloud Hypervisor Config
participant CloudHypervisorVMM as Cloud Hypervisor VMM Process
participant MicroVMGuest as MicroVM Guest
KuasarBuildEnv->>BuildSh: 启动 rootfs 构建 (例如通过 containerd/docker 容器)
BuildSh->>CentOSBuildRootfsSh: 在容器内执行具体构建逻辑
CentOSBuildRootfsSh->>CentOSBuildRootfsSh: 安装 Rust & Go
CentOSBuildRootfsSh->>CentOSBuildRootfsSh: 编译 vmm-task & runc
CentosBuildRootfsSh->>TmpRootfsDir: 填充临时 rootfs 目录
CentosBuildRootfsSh-->>BuildSh: 完成临时 rootfs 准备
BuildSh->>BuildImageSh: 启动镜像生成脚本
BuildImageSh->>BuildImageSh: 验证 rootfs
BuildImageSh->>QemuImgParted: 创建原始磁盘镜像,分区,格式化
QemuImgParted->>TmpRootfsDir: 从临时 rootfs 目录复制内容到磁盘镜像
BuildImageSh->>NsdaxUtility: (可选) 配置 NVDIMM/DAX 头
NsdaxUtility-->>BuildImageSh: 返回更新后的镜像
BuildImageSh-->>KuasarBuildEnv: 输出 kuasar.img 到 /var/lib/kuasar/kuasar.img
KuasarBuildEnv->>CloudHypervisorConfig: 配置 image_path = /var/lib/kuasar/kuasar.img
CloudHypervisorConfig->>CloudHypervisorVMM: 将 image_path 作为 Pmem 设备参数传递
CloudHypervisorVMM->>MicroVMGuest: 挂载 Pmem 设备作为 guest rootfs
容器镜像传递到 MicroVM
graph TD
subgraph Host System
A[Containerd / iSulad Runtime] --> B(Container Image Layers)
B --> C{Shared Directory: /base_dir/shared_dir_suffix}
C --virtiofsd process--> D[virtiofsd Socket: /base_dir/virtiofs.sock]
D --vmm-sandboxer configures--> E{Cloud Hypervisor Process}
E --virtio-fs device--> F[MicroVM Instance]
end
subgraph Guest MicroVM
F --virtio-fs driver in guest kernel--> G[Guest OS]
G --Mount Shared Directory--> H[Container Rootfs Available]
H --vmm-task process in guest--> I(Container Runtime Operations)
end
- 共享目录:Kuasar 的 MicroVM 沙箱通过
virtio-fs设备共享机制将容器镜像内容传递给 guest VM。在CloudHypervisorVM的new方法中,会为virtiofsd配置一个shared_dir(vmm/sandbox/src/cloud_hypervisor/mod.rs),这个目录通常是 sandbox 的base_dir下的SHARED_DIR_SUFFIX(vmm/common/src/lib.rs)。容器镜像的层叠文件系统等内容会被准备到这个共享目录中。 - 启动 virtiofsd:
CloudHypervisorVM(vmm/sandbox/src/cloud_hypervisor/mod.rs) 负责启动virtiofsd进程 (start_virtiofsd方法)。virtiofsd进程监听一个 Unix socket (socket_path) (vmm/sandbox/src/cloud_hypervisor/mod.rs) 并将宿主机上的shared_dir暴露出来。 - Virtio-FS 设备添加:
CloudHypervisorVMFactory(vmm/sandbox/src/cloud_hypervisor/factory.rs) 在创建 VM 时,会添加一个virtio-fs设备,将其socket路径设置为virtiofsd_config.socket_path,并使用tag="kuasar"。 - Guest VM 访问:Guest VM 中的
vmm-task(docs/vmm/README.md) 作为 PID 1 进程,负责管理容器生命周期。它会利用 guest 内核中的virtio-fs驱动通过这个virtio-fs设备与宿主机上的virtiofsd进程通信,从而访问共享文件,并作为容器的 rootfs 使用。DEFAULT_KERNEL_PARAMS中包含task.sharefs_type=virtiofs(vmm/sandbox/src/stratovirt/config.rs) 确保 guest 内核启用virtiofs相关参数。
sequenceDiagram
participant HostSystem as Host System
participant ContainerdIsuadRuntime as Containerd/iSulad Runtime
participant SharedDirectory as Shared Directory (/base_dir/shared_dir_suffix)
participant VirtiofsdProcess as virtiofsd Process
participant VMMController as vmm-sandboxer (CloudHypervisorVM)
participant CloudHypervisorVMM as Cloud Hypervisor VMM Process
participant MicroVMGuest as MicroVM Guest
participant VMMTaskProcess as vmm-task (PID 1 in Guest)
ContainerdIsuadRuntime->>SharedDirectory: 准备/提取容器镜像层叠文件系统到共享目录
VMMController->>SharedDirectory: (通过 virtiofsd_config.shared_dir 配置)
VMMController->>VirtiofsdProcess: 启动 virtiofsd 进程 (通过 start_virtiofsd 方法)
VirtiofsdProcess->>VirtiofsdProcess: 监听 Unix Socket (/base_dir/virtiofs.sock) 并提供共享目录服务
VMMController->>CloudHypervisorVMM: 添加 virtio-fs 设备,指定 socket_path 和 tag="kuasar"
CloudHypervisorVMM->>MicroVMGuest: 模拟 virtio-fs 设备,连接到宿主机上的 virtiofsd socket
MicroVMGuest->>VMMTaskProcess: Guest 内核启动参数包含 task.sharefs_type=virtiofs
VMMTaskProcess->>MicroVMGuest: 通过 guest 内核中的 virtio-fs 驱动挂载共享目录
MicroVMGuest-->>SharedDirectory: Guest OS 通过 virtio-fs 挂载访问容器 rootfs
VMMTaskProcess->>VMMTaskProcess: 管理容器生命周期,使用共享的 rootfs
容器镜像共享
问:请详细介绍一下kuasar启动 cloud hypervisor 的 microvm 的过程中,容器镜像是如何共享的,最好用 mermaid 画序列图来帮助理解整个过程。
在 Kuasar 启动 Cloud Hypervisor microVM 的过程中,容器镜像的共享主要通过 virtio-fs 设备进行。vmm-sandboxer 作为 containerd 的沙箱插件,负责管理沙箱生命周期。当创建一个新的沙箱时,它会启动 Cloud Hypervisor 虚拟机并在其中运行 vmm-task 作为 PID 1 进程,virtiofsd 作为辅助进程。容器镜像(rootfs)通过 virtio-fs 挂载到 guest VM 内部,供 vmm-task 和 runc 使用。
以下是容器镜像共享的详细过程及序列图:
containerd-shim请求创建沙箱和容器containerd-shim接收到创建容器的请求。containerd-shim通过ttrpc调用vmm-sandboxer的CreateSandbox方法。
vmm-sandboxer准备Cloud Hypervisor虚拟机vmm-sandboxer(vmm/sandbox/src/bin/cloud_hypervisor/main.rs) 根据配置 (vmm/sandbox/config_clh.toml) 初始化CloudHypervisorVM实例。CloudHypervisorVM实例会配置virtiofsd的相关信息,包括socket_path和shared_dir。shared_dir会被设置为base_dir/SHARED_DIR_SUFFIX(vmm/sandbox/src/cloud_hypervisor/mod.rs),这个目录将在宿主机上用于存放共享文件。vmm-sandboxer创建CloudHypervisorVM实例时,会添加virtio-fs设备。这个Fs设备 (vmm/sandbox/src/cloud_hypervisor/devices/fs.rs) 使用virtiofsd_config.socket_path和一个tag("kuasar")。
virtiofsd启动- 在
CloudHypervisorVM::start方法中 (vmm/sandbox/src/cloud_hypervisor/mod.rs),vmm-sandboxer会启动virtiofsd进程。 virtiofsd进程会使用virtiofsd_config.shared_dir作为其共享目录,并通过socket_path对外提供服务。virtiofsd会创建一个 Unix Domain Socket 监听连接,等待Cloud Hypervisor连接。
- 在
Cloud Hypervisor启动并连接virtiofsdvmm-sandboxer构建Cloud Hypervisor命令行参数,并启动Cloud Hypervisor虚拟机。Cloud Hypervisor启动时,会根据配置的virtio-fs设备信息,连接到virtiofsd创建的 Unix Domain Socket。Cloud Hypervisor在 guest VM 内部暴露一个virtio-fs设备。
vmm-task启动并挂载virtio-fsCloud Hypervisor启动 guest VM,并在 guest VM 内部启动vmm-task(vmm/task/src/main.rs) 作为 PID 1 进程。vmm-task会解析Cloud Hypervisor传递的kernel_params。其中包含task.sharefs_type=virtiofs(vmm/sandbox/src/cloud_hypervisor/config.rs),指示使用virtio-fs进行文件共享。vmm-task将virtio-fs设备挂载到KUASAR_GUEST_SHARE_DIR(/run/kuasar/storage/containers/) 路径下,使用的tag为"kuasar"(DEFAULT_KERNEL_PARAMS中包含rootfstype=ext4 task.sharefs_type=virtiofs)。- 挂载成功后,
vmm-task就可以通过KUASAR_GUEST_SHARE_DIR访问宿主机上vmm-sandboxer的base_dir/SHARED_DIR_SUFFIX目录中的文件。
- 容器镜像共享
- 当
containerd-shim请求创建容器时,vmm-sandboxer会处理Mount信息。 - 对于容器的
rootfs和bind mounts,vmm-sandboxer会根据类型进行处理 (vmm/sandbox/src/storage/mod.rs)。 - 如果
rootfs或bind mount关联到Storage并且need_guest_handle为true,vmm-sandboxer会将这些Storage信息序列化,并通过ANNOTATION_KEY_STORAGE注解传递给vmm-task。 vmm-sandboxer会在宿主机的base_dir/SHARED_DIR_SUFFIX目录下创建必要的目录结构或文件,并将宿主机上的mount point绑定到这些目录或文件上 (vmm/sandbox/src/storage/mod.rs)。vmm-task接收到容器创建请求后,会解析ANNOTATION_KEY_STORAGE获取Storage列表 (vmm/task/src/container.rs)。vmm-task会根据Storage信息,将KUASAR_GUEST_SHARE_DIR下对应的目录作为容器的rootfs或bind mount的源路径 (vmm/task/src/sandbox.rs)。
- 当
通过上述步骤,宿主机上的容器镜像(以及其他需要共享的文件)通过 virtio-fs 机制,以文件系统挂载的方式透明地共享给 guest VM 中的容器。
sequenceDiagram
participant Containerd
participant ContainerdShim
participant VmmSandboxer
participant CloudHypervisor
participant Virtiofsd
participant VmmTask
participant Runc
Containerd->>ContainerdShim: CreateContainer Request (OCI Bundle)
ContainerdShim->>VmmSandboxer: CreateSandbox/Container (ttrpc)
VmmSandboxer->>VmmSandboxer: Initialize CloudHypervisorVM
VmmSandboxer->>VmmSandboxer: Configure virtio-fs device (socket_path, shared_dir)
VmmSandboxer->>Virtiofsd: Start virtiofsd (shared_dir=base_dir/shared_suffix, socket_path)
activate Virtiofsd
Virtiofsd->>Virtiofsd: Create Unix Domain Socket
VmmSandboxer->>CloudHypervisor: Start Cloud Hypervisor VM (kernel, image, virtio-fs device, kernel_params="...task.sharefs_type=virtiofs...")
activate CloudHypervisor
CloudHypervisor->>Virtiofsd: Connect to virtiofsd Unix Domain Socket
CloudHypervisor->>VmmTask: Launch Guest VM & VmmTask (PID 1)
activate VmmTask
VmmTask->>VmmTask: Parse kernel_params (task.sharefs_type=virtiofs)
VmmTask->>VmmTask: Mount virtio-fs device to /run/kuasar/storage/containers/
VmmTask-->>CloudHypervisor: Mount successful
VmmSandboxer->>VmmSandboxer: Process container Mounts (rootfs, bind mounts)
VmmSandboxer->>VmmSandboxer: Create host shared directories/files (in base_dir/shared_suffix)
VmmSandboxer->>VmmSandboxer: Bind mount host paths to shared directories
VmmSandboxer->>VmmTask: CreateContainer (ttrpc, with ANNOTATION_KEY_STORAGE)
VmmTask->>VmmTask: Read ANNOTATION_KEY_STORAGE
VmmTask->>VmmTask: Identify container rootfs/mounts from /run/kuasar/storage/containers/
VmmTask->>Runc: CreateContainer (OCI Spec with adjusted rootfs/mount paths)
activate Runc
Runc->>VmmTask: Container created
deactivate Runc
VmmTask->>ContainerdShim: Container created response
ContainerdShim->>Containerd: Container created response
1.3 - cgroup
内容来自 code wiki.
cgroup
问:当我在一个 bms 上创建多个 cloud hypervisor 的 microvm 时,cgroup 是如何设计的?什么时候创建 cgroup?
当在 bare-metal server (BMS) 上创建多个 Cloud Hypervisor microVM 时,Kuasar 会为每个 sandbox 设计和管理 cgroup,以实现资源隔离和控制。
Cgroup 的设计和创建时间
- Sandbox Cgroup 的创建:
- 当
KuasarSandboxer收到创建 sandbox 的请求时,它会首先尝试为该 sandbox 创建 cgroup。 - 在
KuasarSandboxer的create方法中 (kuasar-io/kuasar/vmm/sandbox/src/sandbox.rs),会调用SandboxCgroup::create_sandbox_cgroups函数 (kuasar-io/kuasar/vmm/sandbox/src/cgroup.rs)。 - 这个函数会创建一个主 sandbox cgroup,路径格式为
{cgroup_parent_path}/{sandbox_id}。默认的cgroup_parent_path是"kuasar-vmm"(kuasar-io/kuasar/vmm/sandbox/src/cgroup.rs),但也可以通过PodSandboxConfig中的cgroup_parent字段来指定,例如 Kubernetes 环境下可能为/kubepods/burstable/podxxx(kuasar-io/kuasar/vmm/sandbox/src/cgroup.rscreate_mock_pod_sandbox_config函数)。 - 除了主 sandbox cgroup,还会创建两个子 cgroup:
vcpucgroup (kuasar-io/kuasar/vmm/sandbox/src/cgroup.rs),用于管理 vCPU 相关的 CPU 资源。pod_overheadcgroup (kuasar-io/kuasar/vmm/sandbox/src/cgroup.rs),用于管理 pod overhead 相关的 CPU 资源。
- 创建时机: 这些 cgroup 在
KuasarSandboxer处理CreateSandbox请求时,VM 启动之前被创建。
- 当
- 资源限制的应用:
- cgroup 创建后,
KuasarSandboxer会调用sandbox_cgroups.update_res_for_sandbox_cgroups(kuasar-io/kuasar/vmm/sandbox/src/sandbox.rs) 来应用资源限制。 - 这个函数 (
kuasar-io/kuasar/vmm/sandbox/src/cgroup.rs) 会根据SandboxData中定义的资源 (LinuxContainerResources) 对主 sandbox cgroup 应用 CPU、内存、cpuset 和 hugetlb 资源限制。 vcpu和pod_overheadcgroup 仅应用 CPU 资源限制 (kuasar-io/kuasar/vmm/sandbox/src/cgroup.rs)。- 应用时机: 资源限制在 sandbox cgroup 创建后立即应用,同样在 VM 启动之前。
- cgroup 创建后,
- VMM 进程和 VCPU 线程加入 Cgroup:
- 当 MicroVM 启动后,
vmm-sandboxer会获取 VMM 进程的 PID 和其 vCPU 线程的 PID (kuasar-io/kuasar/vmm/sandbox/src/sandbox.rs)。 - 它会调用
sandbox_cgroups.add_process_into_sandbox_cgroups(kuasar-io/kuasar/vmm/sandbox/src/cgroup.rs):- 将 VMM 主进程加入到主 sandbox cgroup 和
pod_overheadcgroup 中。 - 将所有 vCPU 线程加入到
vcpucgroup 中。
- 将 VMM 主进程加入到主 sandbox cgroup 和
- 加入时机: VMM 进程和 VCPU 线程在 MicroVM 启动并获取到其 PID 后,通过
add_to_cgroup方法 (kuasar-io/kuasar/vmm/sandbox/src/sandbox.rs) 加入到相应的 cgroup 中。
- 当 MicroVM 启动后,
- Cgroup V1 和 V2 的支持:
- 目前 Kuasar 主要支持 cgroup V1 (
kuasar-io/kuasar/vmm/sandbox/src/cgroup.rs)。 vmm-task(PID 1在 Guest VM 内) 也会处理 cgroup 的挂载配置,get_cgroup_mounts函数 (kuasar-io/kuasar/vmm/task/src/mount.rs) 会根据unified_cgroup_hierarchy参数生成 cgroup 挂载点,支持 cgroup v1 和 v2 两种模式,确保 Guest VM 内的 cgroup 视图正确。
- 目前 Kuasar 主要支持 cgroup V1 (
- Cgroup 的清理:
- 当 sandbox 被删除时,
KuasarSandboxer会调用sandbox_cgroups.remove_sandbox_cgroups(kuasar-io/kuasar/vmm/sandbox/src/cgroup.rs) 来清理所有相关的 cgroup。 - 清理过程包括将 cgroup 中的任务移动到父 cgroup,然后删除子 cgroup 和主 cgroup。
- 清理时机: 在
KuasarSandboxer的delete方法中 (kuasar-io/kuasar/vmm/sandbox/src/sandbox.rs)。
- 当 sandbox 被删除时,
总结:
Kuasar 在创建每个 MicroVM sandbox 时,会在宿主机上动态创建一套分层的 cgroup 结构 (sandbox -> vcpu/pod_overhead)。这些 cgroup 在 VM 启动前创建并配置资源限制,在 VM 启动后将 VMM 进程和 vCPU 线程加入相应的 cgroup 进行管理,以确保每个 MicroVM 及其内部组件都运行在预定义的资源限制内,从而实现多个 MicroVM 在 BMS 上的隔离和高效运行。
sequenceDiagram
participant KuasarSandboxer
participant SandboxCgroup
participant MicroVM
participant VMMProcess
participant VCPUThreads
KuasarSandboxer->>SandboxCgroup: create_sandbox_cgroups(cgroup_parent_path, sandbox_id)
activate SandboxCgroup
SandboxCgroup->>SandboxCgroup: Create main sandbox cgroup ({parent_path}/{sandbox_id})
SandboxCgroup->>SandboxCgroup: Create vcpu cgroup ({sandbox_id}/vcpu) (CPU subsystem only)
SandboxCgroup->>SandboxCgroup: Create pod_overhead cgroup ({sandbox_id}/pod_overhead) (CPU subsystem only)
deactivate SandboxCgroup
KuasarSandboxer->>SandboxCgroup: update_res_for_sandbox_cgroups(sandbox_data)
activate SandboxCgroup
SandboxCgroup->>SandboxCgroup: Apply CPU, Memory, CPUSet, HugeTlb limits to main sandbox cgroup
SandboxCgroup->>SandboxCgroup: Apply CPU limits to vcpu cgroup
SandboxCgroup->>SandboxCgroup: Apply CPU limits to pod_overhead cgroup
deactivate SandboxCgroup
KuasarSandboxer->>MicroVM: Start MicroVM (e.g., Cloud Hypervisor)
activate MicroVM
MicroVM->>VMMProcess: Launch VMM process
VMMProcess->>VCPUThreads: Spawn VCPU threads
MicroVM-->>KuasarSandboxer: VMM Process PID, VCPU Thread PIDs
KuasarSandboxer->>SandboxCgroup: add_process_into_sandbox_cgroups(VMM_PID, VCPU_TIDs)
activate SandboxCgroup
SandboxCgroup->>SandboxCgroup: Add VMM_PID to main sandbox cgroup
SandboxCgroup->>SandboxCgroup: Add VMM_PID to pod_overhead cgroup
SandboxCgroup->>SandboxCgroup: Add VCPU_TIDs to vcpu cgroup
deactivate SandboxCgroup
KuasarSandboxer->>KuasarSandboxer: MicroVM and containers run with cgroup isolation
alt Sandbox Deletion
KuasarSandboxer->>SandboxCgroup: remove_sandbox_cgroups()
activate SandboxCgroup
SandboxCgroup->>SandboxCgroup: Move all tasks from cgroups to parent
SandboxCgroup->>SandboxCgroup: Delete vcpu cgroup
SandboxCgroup->>SandboxCgroup: Delete pod_overhead cgroup
SandboxCgroup->>SandboxCgroup: Delete main sandbox cgroup
deactivate SandboxCgroup
end
1.4 - vmm task
vmm task 启动
问:cloud hypervisor 的 microvm 里面的 kuasar agent,是通过什么方式启动的?systemd,还是 init 脚本?
在 Cloud Hypervisor MicroVM 内部,Kuasar agent (即 vmm-task) 是作为 PID 1 进程启动的,它会处理操作系统的初始化和信号,而不是通过传统的 systemd 或 init 脚本启动。
以下是 vmm-task 启动方式的详细说明:
- 作为 PID 1 启动:根据 [MicroVM Sandbox (
VMM)](#microvm-sandbox-vmm) 和 VMM Task Lifecycle and Runtime Management 描述,vmm-task在 Guest VM 内部作为 PID 1 进程运行。这意味着它是 Guest OS 启动后执行的第一个用户空间进程,负责管理容器和进程的生命周期,以及处理资源分配、网络配置和异步 I/O 操作。 - 初始化 VM 环境:
vmm-task的主要入口点在kuasar-io/kuasar/vmm/task/src/main.rs。这个组件会初始化 VM 的环境,设置必要的文件系统和内核参数。它还配置了一个ttrpc服务器,用于暴露服务以管理任务、沙箱和流,允许外部组件与 VM 的内部进程进行交互。 - 处理操作系统信号:作为 PID 1 进程,
vmm-task还负责处理操作系统信号,包括回收子进程和管理优雅关机,这在kuasar-io/kuasar/vmm/task/src/main.rs的handle_signals函数中有体现。 - 配置通过内核命令行参数:
vmm-task的配置参数由TaskConfig结构管理,该结构在kuasar-io/kuasar/vmm/task/src/config.rs中定义。这些配置是通过解析来自/proc/cmdline的内核命令行参数动态确定的,允许在运行时灵活调整,而无需依赖外部配置文件。例如,通过kernel_params = "task.debug"可以在vmm-task中启用调试控制台功能。如docs/vmm/README.md中“Developer Guide”部分的“Set up a debug console”所述,task.debug会作为kernel_params传递。
因此,Kuasar agent (vmm-task) 不依赖 systemd 或传统的 init 脚本在 Guest VM 中启动,而是直接作为 MicroVM 的 PID 1 进程运行,并根据内核命令行参数进行配置和初始化。
sequenceDiagram
participant CloudHypervisor
participant GuestKernel
participant VmmTask
CloudHypervisor->>GuestKernel: Boot Guest VM (kernel, initrd, kernel_params="...task.sharefs_type=virtiofs task.debug=true...")
activate GuestKernel
GuestKernel->>VmmTask: Execute vmm-task as PID 1
activate VmmTask
VmmTask->>VmmTask: `initialize()` function in `main.rs`
VmmTask->>VmmTask: Call `TaskConfig::new()`
VmmTask->>VmmTask: Parse kernel command-line arguments from `/proc/cmdline` to create `TaskConfig`
VmmTask->>VmmTask: Set tracing (if `config.enable_tracing` is true)
VmmTask->>VmmTask: Initialize logger (`init_logger`)
VmmTask->>VmmTask: Call `early_init_call()`
VmmTask->>VmmTask: Setup environment variables from `ENVS`
VmmTask->>VmmTask: Initialize VM rootfs (`init_vm_rootfs()`)
VmmTask->>VmmTask: Mount static mounts (e.g., `/proc`, `/sys`, `/dev`, `/run`, `/dev/shm`, `/dev/pts`)
VmmTask->>VmmTask: Mount cgroup filesystems based on `get_cgroup_mounts`
VmmTask->>VmmTask: Set default `sysctl` parameters (e.g., `vm.overcommit_memory`)
alt Sharefs Type is "virtiofs"
VmmTask->>VmmTask: Mount `SHAREFS_VIRTIOFS_MOUNTS` (e.g., `virtiofs` for `KUASAR_STATE_DIR`)
else Sharefs Type is "9p"
VmmTask->>VmmTask: Mount `SHAREFS_9P_MOUNTS` (e.g., `9p` for `KUASAR_STATE_DIR`)
end
alt Debug Mode Enabled (`config.debug` is true)
VmmTask->>VmmTask: Call `listen_debug_console("vsock://-1:1025", &config.debug_shell)`
end
VmmTask->>VmmTask: Call `late_init_call()`
VmmTask->>VmmTask: Setup DNS by bind mounting `KUASAR_STATE_DIR/RESOLV_FILENAME` to `/etc/resolv.conf` (if exists)
VmmTask->>VmmTask: Create TTRPC Server (`create_ttrpc_server()`)
VmmTask->>VmmTask: Create `TaskService`
VmmTask->>VmmTask: Create `SandboxService`
VmmTask->>VmmTask: Create `StreamingService`
VmmTask->>VmmTask: Register services and bind to `vsock://-1:1024`
VmmTask->>VmmTask: Start TTRPC Server
VmmTask->>VmmTask: Listen for OS signals (`handle_signals()`)
loop Signal Handling
VmmTask->>VmmTask: Handle `SIGTERM`, `SIGINT`, `SIGCHLD`, `SIGUSR1`, etc.
alt SIGCHLD
VmmTask->>VmmTask: `waitpid()` for child processes
VmmTask->>VmmTask: Notify monitor about child exit/termination
end
alt SIGUSR1
VmmTask->>VmmTask: Toggle tracing and re-initialize logger
end
end