ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

在 Mac 上运行云原生虚拟机:CloudHypervisor 移植实践

在 Mac 上运行云原生虚拟机:CloudHypervisor 移植实践 在 Mac 上做 Linux 虚拟化长期以来是一件“能用但别扭”的事。UTM 和 QEMU 方案功能齐全但命令行参数复杂、镜像管理颗粒度不够云原生Docker 只能覆盖容器场景无法模拟内核和完整虚拟机。更麻烦的是很多在 Linux 服务器上成熟的虚拟化项目比如 Cloud Hypervisor默认只面向 KVM本地 Mac 想复现一套云环境往往要绕很远。CloudHypervisor 被移植到 macOS Hypervisor.framework、并且通过一个自定义 VMMCustomVMM来承载的消息正好踩在这个痛点上。这篇文章要讲清楚的就是这个移植为什么有意义、Hypervisor.framework 和 CloudHypervisor 之间到底差了什么、以及如果你想在 Mac 上把它跑起来应该怎么准备环境、构建、启动和排查问题。先说结论这次移植的关键价值不是“让 Mac 用户多一个虚拟机软件”而是让开发者的本地研发环境第一次有机会跟云端的 Cloud Hypervisor / Kata Containers 运行时完全对齐同时保留 macOS 的图形开发体验。读完这篇文章你能搞清楚 CloudHypervisor 的启动链路、CustomVMM 在整个体系中的位置并且照着完成一次从源码构建到启动 Linux guest 的最小实践。1. 为什么要在 Mac 上跑 CloudHypervisor1.1 本地开发环境与云环境割裂的问题很多后端团队现在会遇到一个典型矛盾生产环境用 Kata Containers、Firecracker 这类轻量虚拟化技术来隔离容器但开发者本地的 Mac 上只有 Docker Desktop两者行为并不一致。Docker 在 Mac 上本身是跑在一个 Linux 虚拟机里的这个虚拟机由 Apple 的 Virtualization.framework 或 QEMU 驱动和你在云上用的 Kata 运行时根本不是同一套 VMM。这意味着什么意味着你在本地能通过的配置到了生产环境可能因为内核参数、设备模型、固件行为不同而出现问题。如果你负责维护 Kata Containers 或 Cloud Hypervisor 的运行环境这个问题会非常痛苦你没办法在 Mac 上直接调试 guest 内核也没办法复现生产环境的启动参数。1.2 CloudHypervisor 的定位Cloud Hypervisor 是一个用 Rust 编写的开源 VMMVirtual Machine Monitor是 Linux Foundation 旗下的项目。它的定位非常明确为云工作负载而生而不是像 QEMU 那样什么都支持。它只保留必要设备启动极快、内存占用低特别适合 Kata Containers、Intel 的 Cloud Service 等场景。但 CloudHypervisor 长期以来依赖 KVM也就是 Linux 内核的虚拟化模块。在 Mac 上跑不了 KVM所以移植到 macOS 必须换一个 hypervisor 后端。这就是标题里“Ported to Mac HypervisorFramework”的由来用苹果的 Hypervisor.framework 作为底层加速接口让 CloudHypervisor 的 vCPU 和内存管理逻辑在 macOS 上跑起来。1.3 什么样的读者最需要关注这次移植维护 Kata Containers 或 Cloud Hypervisor 集群的工程师可以在一台 MacBook 上完整复现生产环境的虚拟机启动链路。做虚拟化开发、Rust 系统编程的开发者CustomVMM 的实现是很好的学习样本。在 Mac 上折腾 Linux VM 的进阶用户如果你想绕过 QEMU 的重型设备模拟用云原生的方式跑 guest这篇能帮你少踩坑。反过来说如果你只是想偶尔开一个 Linux 虚拟机跑跑软件那 CloudHypervisor on Mac 对你帮助不大UTM 或 Lima 更合适。这个项目适合的是“把虚拟机当基础设施”的人而不是“把虚拟机当工具”的人。2. 核心概念CloudHypervisor、Hypervisor.framework、CustomVMM2.1 CloudHypervisor 到底是什么CloudHypervisor 的定位可以理解为“云原生版 QEMU 替代者”。它的核心特点有三个仅面向云工作负载去掉了声卡、USB、图形桌面等不需要的设备。启动速度快内存开销小只提供必要的虚拟化设备。用 Rust 实现内存安全减少 VMM 自身被攻破的概率。它和 QEMU 最大的区别是“少而专”。QEMU 支持几十种架构、几百种设备而 CloudHypervisor 只专注 x86_64 和 AArch64 下最小化的设备模型。在云原生产品组合里CloudHypervisor 和 Kata Containers 的关系是Kata 负责把容器变成一个轻量虚拟机而 CloudHypervisor 就是那个真正创建并运行虚拟机的引擎。所以 CloudHypervisor 的稳定性直接影响 Kata 的安全性。2.2 Hypervisor.framework 是什么Hypervisor.framework 是苹果从 macOS 10.10 开始提供的原生虚拟化框架。它和 Virtualization.framework 是两层东西Virtualization.framework面向上层应用提供完整的虚拟机能力你配置好内存、CPU、磁盘就能跑苹果帮你实现了设备模型。Hypervisor.framework只提供最底层的 vCPU 创建、内存映射、寄存器读写能力不提供任何设备模拟、固件、中断控制器、时钟源。对 CloudHypervisor 来说它恰恰需要 Hypervisor.framework 这种底层能力因为 CloudHypervisor 自己要实现完整的设备模型。如果使用 Virtualization.framework反而被苹果的设备模型限制住了没办法按云工作负载的方式定制。Hypervisor.framework 的工作机制和 KVM 类似VMM 在用户态创建虚拟 CPU通过系统调用把 vCPU 交给硬件执行再通过事件循环处理虚拟机的 exits比如内存访问缺页、I/O 指令等。CloudHypervisor 的 VMM 核心逻辑是可以跨平台复用的真正需要替换的是跟 KVM 交互的那层代码。2.3 CustomVMM 在这里指什么“CustomVMM”并不是一个官方产品而是这次移植方案的核心思路因为 Hypervisor.framework 不提供设备模型所以必须有一个自定义 VMM 来承担 CloudHypervisor 的设备模拟和 vCPU 管理职责。通俗地讲Hypervisor.framework 只是给了你一块可以跑 guest 代码的“裸 CPU”而 CustomVMM 负责为这块裸 CPU 搭好周围的环境内存布局、中断控制器、PCI 总线、串口、磁盘控制器、网络设备。这些在 KVM 场景下部分由内核完成部分由 QEMU 完成而在 Mac 上全部要由用户态 VMM 自己搞定。CloudHypervisor 本身的架构设计在这里就体现出了优势。它内部有 hypervisor 抽象层可以在不同后端之间切换。移植到 macOS本质上是新增一个基于 Hypervisor.framework 的后端同时把设备模型的差异处理掉。2.4 三者的关系层次角色类比CloudHypervisor云原生 VMM负责整体虚拟机生命周期项目经理Hypervisor.framework提供裸硬件加速能力施工队CustomVMM基于 Hypervisor.framework 实现 vCPU、内存、设备管理现场负责人没有 CustomVMMHypervisor.framework 只是一组 APICloudHypervisor 无法直接驱动它。没有 CloudHypervisorCustomVMM 也只是一个底层控制器没法把 Linux guest 跑成云工作负载。三者组合起来才构成了一个可用的轻量虚拟机方案。3. 环境准备与前置条件在开始动手之前先确认你的机器条件。这个实践对硬件和系统有明确要求提前确认能省掉很多排查时间。3.1 硬件与操作系统要求CloudHypervisor 原本是面向 x86_64 和 AArch64 的 VMM。在 Mac 上跑你需要重点确认Intel Mac 或 Apple Silicon Mac 都可以尝试但 guest 架构需要和 host 架构对齐。系统版本建议使用较新的 macOS因为 Hypervisor.framework 的部分 API 在不同版本有差异。内存建议至少 16GB因为 CloudHypervisor 本身要占用内存guest 还要分配内存。从现有信息看这个移植主要面向新的 macOS 版本所以如果你的 Mac 系统太老可能无法获得完整的 API 支持。如果你在系统升级上遇到类似“macOS 恢复启动”“安全策略更改”这类问题建议先处理好系统基础环境再继续。3.2 安装 Xcode Command Line ToolsHypervisor.framework 是系统框架一般不需要额外安装但编译 CloudHypervisor 需要 clang、链接器等基础工具Xcode Command Line Tools 是必须的。xcode-select --install安装完成后验证clang --version如果之前已经安装过 Xcode但提示找不到工具链可以指定开发目录sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer3.3 安装 Rust 工具链CloudHypervisor 是 Rust 项目构建方式基于 Cargo。推荐使用 rustup 安装 Rust。curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh安装完成后需要让当前 shell 加载环境变量source $HOME/.cargo/env rustc --version cargo --version这里要注意CloudHypervisor 这类大型 Rust 项目对工具链版本有要求。如果构建时报错说某些 feature 不稳定不要急着改代码先确认是否使用了项目 README 中指定的 Rust 版本。3.4 准备必要的系统工具构建过程还需要 git、make、pkg-config 等基础工具。如果你在 Mac 上已经装过 Homebrew可以直接安装缺失的依赖brew install git make pkg-config如果还没有 Homebrew先去官网安装或者用系统自带的 git。实际开发中我建议把 Homebrew 和 git 都配好这样后续获取源码、解决依赖都方便。3.5 安全策略与权限注意Hypervisor.framework 在 macOS 上属于系统级能力正常开发不需要关闭 SIP 或修改安全策略。但如果你的机器是企业统一管控的或者曾经改过安全设置可能遇到无法创建 VM 的错误。另外从网上下载构建好的固件、内核镜像时macOS 的 Gatekeeper 可能会拦截未签名的文件。如果你看到类似“未打开……因其包含恶意软件”的提示先确认文件来源是否可信不要盲目绕过安全机制。在测试环境中如果确实需要运行自己构建的二进制可以去“系统设置 → 隐私与安全性”中手动允许但不要关闭整个安全体系。4. 核心流程拆解从 KVM 到 Hypervisor.framework要理解这次移植关键是理解 CloudHypervisor 从 Linux 到 macOS 的迁移过程中哪些逻辑被保留哪些逻辑被替换。4.1 CloudHypervisor 的架构分层CloudHypervisor 的代码结构大致可以分为几个层次顶层 VMM 逻辑负责解析命令行参数、创建虚拟机、管理生命周期。hypervisor 抽象层定义 vCPU、内存、中断等操作的接口屏蔽底层 hypervisor 差异。设备模型实现串口、PCI、virtio-net、virtio-blk 等设备。后端实现针对不同 hypervisor 的具体实现在 Linux 上是 KVM在 macOS 上是 Hypervisor.framework。移植到 macOS主要动的是最后一块。顶层逻辑、设备模型大部分可以复用因为 virtio 设备是在用户态实现的不依赖底层 hypervisor。4.2 KVM 与 Hypervisor.framework 的差异KVM 和 Hypervisor.framework 虽然都是底层虚拟化接口但存在几个明显差异对比项KVMHypervisor.framework运行平台LinuxmacOSvCPU 创建通过 /dev/kvm ioctl通过 hv_vcpu_create 系统调用内存管理KVM slots通过 mmap 映射hv_vm_map需要管理物理内存映射中断注入irqfd、ioeventfd 支持好能力相对有限需要 VMM 自己处理设备直通支持 PCI passthrough不面向普通开发者开放生态成熟度极高相对有限这些差异意味着不能简单地把 KVM 相关的代码换一个库路径就完事。vCPU 的运行循环、中断事件的处理方式、内存映射的生命周期都需要重新实现。4.3 为什么需要 CustomVMM 这一层Hypervisor.framework 就像一块“裸金属”它只提供了创建 vCPU 和映射内存的能力。如果你创建了一个 vCPU但没有给它配好固件、中断控制器、PCI 总线guest 的 Linux 内核根本无法启动。CustomVMM 在这里的作用就是补全这些基础设施。它要做的事情包括创建一个虚拟机和 vCPU。分配 guest 物理内存并映射到 host 地址空间。加载固件通常使用 EDK2 / Cloud Hypervisor 定制的 UEFI 固件。配置中断控制器处理虚拟中断的注入。实现串口和必要的启动设备让内核能输出日志。挂载 virtio 设备提供网络和存储能力。这些逻辑在 QEMU 中是用 C 语言实现的在 CloudHypervisor 中是用 Rust 实现的。移植到 macOS 后设备模型的部分可以保留但底层的“如何创建 vCPU”“如何注入中断”必须重写。4.4 移植路径判断从仓库结构和社区讨论来看这个移植更合理的路径是保留 CloudHypervisor 的 VMM 主体新增一个基于 Hypervisor.framework 的 hypervisor 后端。这意味着 CustomVMM 并不是一个完全独立的项目而是 CloudHypervisor 内部的一个编译开关或 feature。实际构建时你需要确认当前代码分支是否默认启用了 macOS 支持。如果默认没有启用可能需要在 Cargo 配置或编译参数里手动开启对应 feature。具体名称以项目 README 为准不要照抄网上过时的命令。5. 完整实践在 Mac 上构建并启动 CloudHypervisor下面我们走一遍完整的最小实践流程。先说明由于 CloudHypervisor 版本迭代较快具体命令参数以官方仓库为准本文演示的是核心思路和常见参数。5.1 步骤 1验证 Hypervisor.framework 可用性在开始复杂构建前先用一个最小的 Swift 程序验证系统支持 Hypervisor.framework。把下面的代码保存为hvf_check.swiftimport Hypervisor let ret hv_vm_create(HV_VM_DEFAULT) if ret HV_SUCCESS { print(Hypervisor.framework is available) hv_vm_destroy() } else { print(hv_vm_create failed with error: \(ret)) }运行swift hvf_check.swift如果输出Hypervisor.framework is available说明系统层面没有问题。如果报错需要先检查系统版本或安全策略。5.2 步骤 2获取 CloudHypervisor 源码git clone https://github.com/cloud-hypervisor/cloud-hypervisor.git cd cloud-hypervisor如果你所在网络环境访问 GitHub 较慢可以考虑使用镜像或代理但一定要确保源码完整。下载后先看 README确认当前分支是否支持 macOS。这一步看起来简单但非常重要因为主分支可能在某个时间点才合并了 Hypervisor.framework 支持。5.3 步骤 3构建 CloudHypervisorcargo build --release构建时间取决于机器性能第一次构建可能需要十几分钟到半小时。CloudHypervisor 依赖较多Cargo 会编译很多 crate这是正常现象。如果构建提示缺少某些系统库可以用 Homebrew 安装后再重试。如果提示 Rust 版本需要 nightlyrustup override set nightly但一般情况下CloudHypervisor 会尽量保持 stable Rust 可构建。遇到这类问题优先看官方文档而不是急于切换工具链。构建完成后二进制位于target/release/cloud-hypervisor。可以验证版本./target/release/cloud-hypervisor --version5.4 步骤 4准备固件和 guest 内核CloudHypervisor 需要一个 UEFI 固件来引导 guest。常见做法是把 EDK2 编译成 CloudHypervisor 专用的固件文件。你需要从 CloudHypervisor 官方发布渠道或相关仓库下载预编译固件文件名通常会带有版本号。mkdir -p ~/cloudhv cd ~/cloudhv # 从官方渠道下载 cloud-hypervisor 对应的 EDK2 固件 # 示例文件名CLOUDHV.fd另外准备一个 Linux 内核。CloudHypervisor 的测试通常使用标准 Linux 内核你需要一个包含 virtio 驱动的内核镜像。实际操作中建议从 CloudHypervisor 官方测试脚本里找到他们使用的内核和 rootfs 下载地址。5.5 步骤 5启动一个 Linux guest这是核心步骤。CloudHypervisor 的启动参数比 QEMU 简单很多典型的启动命令如下./target/release/cloud-hypervisor \ --kernel ~/cloudhv/vmlinux \ --disk path~/cloudhv/ubuntu-cloud.img \ --cpus boot4 \ --memory size4G \ --net tap,mac \ --rng参数说明--kernel指定 guest 内核镜像。--disk指定磁盘镜像目前通常使用 raw 或 qcow2 格式。--cpus boot4虚拟机的 CPU 数量。--memory size4G虚拟机内存大小。--net tap网络设备配置tap 参数在当前移植版本中可能还不完整。--rng提供虚拟随机数发生器。在 macOS 上网络后端是最容易出问题的部分。因为 macOS 没有 Linux 的 TAP/TUN 设备语义需要走 vmnet.framework 或改造网络后端。如果网络暂时不可用可以先把--net去掉用仅串口的方式验证基本启动流程。5.6 步骤 6观察 guest 启动日志启动后CloudHypervisor 会把 guest 的串口输出重定向到终端。你应该能看到 Linux 内核的启动日志类似Booting Linux on physical CPU 0x0000000000 Linux version 6.x.x ... Freeing unused kernel memory: ... Welcome to Ubuntu ...看到 welcome 或 login 提示说明虚拟机已经正常启动了。6. 运行结果与效果验证6.1 如何判断运行成功判断标准其实很朴素guest 内核能不能完整启动能不能出现登录提示符。具体来说串口输出是否为完整的内核启动日志。日志最后是否出现 init 进程或 shell 提示符。如果配置了 virtio-blkguest 内能否挂载磁盘。如果配置了 virtio-netguest 内能否获取 IP 并通信。如果只是看到部分内核日志然后立即重启通常是固件或内核参数问题。如果连固件都没出来优先检查固件文件路径和权限。6.2 推荐的验证工具链在 guest 内可以使用一系列命令验证虚拟化环境是否正常uname -a cat /proc/cpuinfo free -h lsblk ip addr这些命令能帮你确认 CPU、内存、磁盘、网络是否符合预期。如果这些命令的结果和你在云上看到的类似说明 CloudHypervisor 的设备模型工作正常。6.3 失败时的第一排查入口如果启动失败不要急着搜索所有参数。按照这个顺序排查看 CloudHypervisor 进程的输出尤其是 panic 或 error 日志。确认固件路径是否正确固件是否匹配当前架构。确认磁盘镜像格式是否被支持raw 格式最稳妥。确认 macOS 系统版本是否满足 Hypervisor.framework API 要求。确认是否因为 Gatekeeper 或安全策略拦截了二进制执行。这些步骤能覆盖大多数启动失败场景。7. 常见问题与排查思路以下是 Mac 上运行 CloudHypervisor 的常见问题我把排查思路整理成表格方便实际遇到问题时快速定位。问题现象可能原因排查方式解决方案启动直接退出无任何输出固件路径错误或固件缺失检查命令行参数和固件文件是否存在重新下载匹配架构的固件guest 启动后立即重启内核参数与固件不匹配查看串口输出的最后几行调整内核启动参数或换用官方推荐内核编译失败提示缺少系统库缺少 pkg-config 或相关依赖查看 Cargo 报错信息安装对应依赖后重试编译失败提示 Rust 版本不兼容工具链版本与项目 MSRV 不符检查项目 README 中的工具链要求切换到指定版本或使用 rustupmacOS 提示应用无法打开Gatekeeper 拦截未签名二进制检查“系统设置 → 隐私与安全性”对可信构建产物手动允许打开无法创建 VMhv_vm_create 返回错误系统版本过旧或安全策略限制运行前面的 hvf_check.swift升级 macOS 或确认安全策略网络不通macOS 上 tap 后端未完成查看网络后端支持状态先移除 --net 参数聚焦启动链路内存分配失败host 内存不足或 vm_map 失败查看 CloudHypervisor 错误日志降低 guest 内存大小磁盘无法识别virtio-blk 驱动未编译进 guest 内核lsblk 检查设备使用包含 virtio 驱动的发行版内核系统资源占用过高guest CPU 数量设置过多检查 host 负载减少 --cpus 数量这里要特别提醒遇到“应用无法打开”“来自不明开发者”这类提示时不要因为图省事就关闭 SIP 或修改安全策略。如果你运行的是自己从源码构建的二进制并且确认源码可信可以在系统设置里手动允许。但如果文件来源不明直接拒绝执行才是正确做法。8. 最佳实践与工程建议8.1 日常开发场景不要把 CloudHypervisor 当万能工具CloudHypervisor on Mac 的适用场景是开发和调试不是替代 Docker Desktop 或 UTM。我建议的用法是用脚本封装启动命令避免每次输入一长串参数。使用固定的 guest 镜像模板确保测试可重复。把固件、内核、磁盘镜像放到独立目录不要散落在项目中。反过来如果你只是需要跑一个 GUI 版本的 Linux那 UTM 这类基于 Virtualization.framework 的工具更合适。CloudHypervisor 的设计哲学是“最小化、云原生”它并不希望你把桌面环境跑进去。8.2 配置管理启动参数版本化CloudHypervisor 的启动参数会随版本变化。不要在生产环境或团队协作中口头传递参数建议把所有参数写入版本控制的脚本中。#!/bin/bash # 文件路径scripts/run-cloudhv.sh CLOUDHV_BIN$HOME/cloud-hypervisor/target/release/cloud-hypervisor KERNEL$HOME/cloudhv/vmlinux DISK$HOME/cloudhv/ubuntu-cloud.img $CLOUDHV_BIN \ --kernel $KERNEL \ --disk path$DISK \ --cpus boot4 \ --memory size4G \ --rng这样每次启动的配置都可审计、可回溯团队成员之间也不会因为命令参数不一致导致行为差异。8.3 日志与排错习惯CloudHypervisor 的日志是理解虚拟机行为的最大信息来源。建议在启动命令中加上--log-file参数把 VMM 自身的日志单独保存避免和 guest 串口输出混在一起。--log-file /tmp/cloudhv/vmm.log排查问题时先看 VMM 日志再看 guest 串口日志最后看系统日志。这个顺序能帮你快速判断问题出在 VMM 层还是 guest 层。8.4 安全边界虚拟化软件属于安全敏感领域使用时要特别注意不要把 Mac 上的 Hypervisor.framework 测试环境等同于生产集群的安全等级。构建 VMM 时尽量使用官方发布的固件和内核避免使用来源不明的二进制。在 guest 内不要运行不受信任的代码尤其当你的 VMM 还处于移植早期阶段。虚拟机配置里要设置合理的资源限制避免单个 guest 耗尽 host 内存。8.5 何时继续使用 QEMU/UTM何时切换到 CloudHypervisor场景工具选择理由需要图形桌面UTM / Virtualization.framework设备模型更完整调试云原生运行时CloudHypervisor与生产环境对齐学习虚拟化原理CloudHypervisor代码简洁、可控性好快速跑一个 Linux 容器Docker Desktop / Lima启动更快、工程化更好做 Kata Containers 开发CloudHypervisor 是首选它是 Kata 的默认 VMM 之一9. 总结与后续学习方向CloudHypervisor 移植到 macOS Hypervisor.framework本质上解决的是“Mac 本地无法贴近云原生虚拟化环境”的问题。它的实现路径是用 CustomVMM 补全 Hypervisor.framework 缺失的设备模型让 CloudHypervisor 的 Rust 代码能够在 macOS 上创建和管理虚拟机。如果你想动手实践我建议按这个顺序来先跑通一个最小的 Linux guest再逐步加入磁盘、网络、多 vCPU 配置最后再去看 CloudHypervisor 的源码理解 hypervisor 抽象层的设计。不要一开始就去研究中断注入或内存映射细节那样容易劝退自己。下一步值得深入的方向有几个一是 CloudHypervisor 的 hypervisor 抽象层代码这是理解 KVM 和 Hypervisor.framework 差异的最佳入口二是 virtio 设备模型的实现方式因为它决定了 guest 的 I/O 性能三是 macOS 上 vmnet 网络后端的实现这是把虚拟机真正纳入开发网络的关键。如果你的目标是做 Kata Containers 相关开发建议把这次实践当作起点继续在 Linux 服务器上深入学习 KVM 和 CloudHypervisor 的原生用法。Mac 上的移植版本适合作为日常调试工具但生产环境的性能和行为验证仍然要回到 Linux 平台上去做。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进