本文根据一次 Libvirt CPU 模型讨论和官方文档整理。示例用于说明设计方法,尚未在客户环境完成跨 CPU 热迁移实测。
我之前认为,只要把虚拟机 CPU 配置成 host-model,Libvirt 就能屏蔽宿主机 CPU 差异,从而解决 CPU 异构问题。
这个理解只对了一半:host-model 确实比 host-passthrough 更容易迁移,但它优化的是虚拟机在当前宿主机启动时所看到的 CPU,并不会自动计算整个集群都支持的公共 CPU 基线。
如果客户真正需要的是在不同代 CPU 之间进行双向热迁移,核心问题不是“如何尽量接近当前宿主机”,而是“如何让虚拟机从启动开始就只使用所有目标宿主机都能提供的 CPU ABI”。
先区分三种需求
“支持 CPU 异构”可能指三件不同的事:
| 需求 | 典型场景 | host-model 是否合适 |
|---|---|---|
| 异构节点都能创建虚拟机 | 虚拟机关机后可以调度到另一代 CPU | 通常可以,但重启后 Guest 看到的 CPU 可能变化 |
| 单向热迁移 | 从旧 CPU 迁移到能力更强的新 CPU | 可能可行,必须预检查和实测 |
| 双向跨代热迁移 | 新旧 CPU 节点之间可以来回迁移 | 不能只依赖 host-model,应设计公共 CPU 基线 |
客户说“跨 CPU”,还需要继续明确边界:
- 是同厂商不同代,例如 Intel Broadwell 与 Skylake;
- 还是 Intel 与 AMD 跨厂商;
- 只要求旧节点迁往新节点,还是必须双向迁移;
- 是热迁移,还是允许关机后冷迁移;
- 迁移池中具体包含哪些宿主机,而不是泛指整个集群。
这些答案会直接改变 CPU 模型设计。
Libvirt 的四种 CPU 模式
| CPU 模式 | Guest 看到的 CPU | 性能与特性 | 迁移边界 |
|---|---|---|---|
host-passthrough |
尽可能等同宿主机 | 最接近物理 CPU | 依赖硬件、QEMU、微码和配置高度一致 |
host-model |
启动时选择最接近当前宿主机的命名模型,再补充特性 | 接近宿主机 | 适合 CPU 基本同构的迁移池,不保证跨代双向迁移 |
custom |
显式指定命名模型和 CPU Features | 会舍弃部分新 CPU 特性 | 可固定 Guest CPU ABI,适合跨代迁移池 |
maximum |
暴露 Hypervisor 能提供的最大特性集 | 特性最多 | 不适合追求迁移兼容性的 KVM Guest |
host-passthrough 的目标是“像宿主机”,host-model 的目标是“尽量接近宿主机且更可迁移”,而 custom 的目标是“无论落在哪台宿主机,都向 Guest 提供相同的 CPU 契约”。
这三者解决的不是同一个问题。
host-model 实际做了什么
下面的 XML 看起来没有指定具体 CPU:
1 | <cpu mode='host-model'/> |
虚拟机启动前,Libvirt 会根据当前宿主机的 Domain Capabilities,选择一个最接近宿主机的 QEMU 命名 CPU 模型,并补充必要的 CPU Features。
假设两台宿主机的能力如下:
1 | Host A:支持 f1、f2、f3、f4 |
虚拟机在 Host A 启动后,可能看到:
1 | Guest CPU:f1、f2、f3、f4 |
热迁移时,Libvirt 会把这台运行中虚拟机的完整 CPU 定义传给 Host B。为了保证 Guest 在迁移前后看到相同硬件,Host B 也必须提供 f4。如果它做不到,迁移预检查或恢复阶段就会失败。
反过来,虚拟机如果最初在 Host B 启动,只暴露 f1、f2、f3,迁移到能力更强的 Host A 往往更容易成功。因此 host-model 的迁移兼容性可能具有方向性:
1 | 旧 CPU → 新 CPU:可能成功 |
这里的“可能”很重要。CPU 不是热迁移的唯一约束,QEMU 版本、Machine Type、内核、微码、设备模型、存储和网络也要兼容。
最容易混淆的地方
同一份 host-model XML,不等于同一个 Guest CPU
相同的持久化 XML:
1 | <cpu mode='host-model'/> |
在不同宿主机冷启动时,可能展开成不同的有效 CPU。也就是说,它保证的是“在当前节点尽可能合理”,而不是“整个迁移池的 CPU ABI 永远一致”。
运行中的虚拟机热迁移后,Guest 仍会保持迁移前的 CPU 定义;但关机并在目标宿主机重新启动后,Libvirt 会重新计算 host-model,Guest 可能看到更多或更少的 CPU Features。
migratable=’on’ 不是异构迁移开关
例如:
1 | <cpu mode='host-passthrough' migratable='on'/> |
migratable='on' 只会尝试移除已知会阻塞迁移的 CPU Features,并不会自动求出两台异构宿主机的公共特性集。Libvirt 官方文档明确提醒:即使开启它,host-passthrough 在宿主机不一致时仍然危险。
CPU 兼容不等于整机可热迁移
CPU 基线通过,只能说明 CPU 这一层满足条件。完整热迁移还依赖:
- 源、目标使用兼容的 QEMU 版本和相同的版本化 Machine Type;
- Guest 设备模型及其 Features 一致;
- 磁盘、网络和迁移通道满足迁移方式要求;
- NUMA、CPU Pinning、Huge Page、VFIO 等资源能在目标节点重建;
- 目标宿主机内核、KVM、微码和安全缓解特性满足 Guest CPU 定义。
因此,不能把“CPU Compare 通过”写成“热迁移一定成功”。
跨代双向热迁移:固定公共 CPU 基线
如果迁移池中包含多代 CPU,并要求双向热迁移,更稳妥的设计是:
1 | Host A CPU Features ─┐ |
QEMU 的建议也是:在混合 CPU 型号的迁移池中,选择所有目标宿主机都支持的最新命名 CPU 模型。
1. 固定迁移域
先列出真正需要互相热迁移的宿主机,不要把所有计算节点默认放进同一个兼容域。跨厂商 CPU 的边界更复杂,不能只看少量相同 Flags 就假设兼容;更稳妥的做法是拆分迁移域,再分别验证。
2. 收集能力并计算 Baseline
可以先在各宿主机收集能力:
1 | virsh capabilities > host-a.xml |
将多个宿主机的 Capabilities 汇总到一个输入文件后,计算可迁移基线:
1 | virsh cpu-baseline hosts.xml --migratable |
较新的 Libvirt 还提供 hypervisor-cpu-baseline。它会结合指定 QEMU、架构和 Machine Type 的实际能力计算基线,比只比较物理 CPU 更贴近虚拟机最终能获得的 CPU。生产设计应优先使用 Domain Capabilities 和该命令,并确保所有节点使用相同参数。
3. 把结果固化为 custom CPU
不要为异构迁移池保留动态 host-model,而是将计算和验证后的基线固化到虚拟机模板。例如:
1 | <cpu mode='custom' match='exact'> |
这只是结构示例,Broadwell-v4 和 pcid 不能直接套用。真实模型与 Features 必须来自迁移池的能力交集,并结合 Guest 安全、性能和业务需求评审。
fallback='forbid' 可以避免 Hypervisor 静默退回到另一个模型。代价是目标宿主机无法严格提供该 CPU 时,虚拟机会直接启动失败;这正是生产环境需要的失败方式。
4. 在每台目标宿主机做预检查
至少验证:
1 | virsh hypervisor-cpu-compare baseline.xml \ |
QEMU 路径和 Machine Type 以实际 Domain XML 为准。还应比较各节点的:
1 | virsh version |
版本号相同也不是充分条件,还要保证发行版补丁、微码和关键配置的兼容策略一致。
验收矩阵
不要只做一次“迁移命令返回成功”。最小验收应覆盖:
| 场景 | 预期结果 |
|---|---|
| Host A → Host B 热迁移 | 迁移成功,业务连续 |
| Host B → Host A 热迁移 | 迁移成功,业务连续 |
| 迁移前后检查 Guest CPU | Model、Flags 和 ABI 保持一致 |
| CPU 压力下迁移 | 业务计算结果正确,无 Guest Crash 或 Hang |
| 目标宿主机冷启动 | Guest CPU 仍与模板定义一致 |
| 迁移失败回滚 | 源端虚拟机可继续运行,状态明确 |
| 宿主机升级后回归 | 双向迁移矩阵重新通过 |
迁移前后可在 Guest 中记录:
1 | lscpu |
如果使用了特定指令集,还应运行能实际执行这些指令的业务或测试程序。只比较 Flags,不能证明应用状态和设备状态都正确迁移。
最终结论
我原来的判断是:
host-model可以屏蔽 CPU 差异,所以能解决 CPU 异构。
更准确的结论应该是:
host-model能在当前宿主机上生成接近物理 CPU、相对易迁移的 Guest CPU,适合 CPU 基本同构的资源池;它不是集群级公共 CPU 基线,也不保证不同代 CPU 之间可以双向热迁移。
如果客户只是希望异构节点都能运行虚拟机,host-model 可能已经足够;如果客户要求跨代双向热迁移,应为明确的迁移域计算公共 CPU Baseline,固化为 custom CPU,并连同 Machine Type、QEMU、内核、微码和设备模型一起完成双向实测。