使用 KVM 运行嵌套客户机¶
嵌套客户机是指在一个客户机内部运行另一个客户机(可以是基于 KVM 的,也可以是不同的虚拟机管理程序)。最简单的例子是一个 KVM 客户机,它反过来运行在另一个 KVM 客户机上(本文档的其余部分基于此示例构建)
.----------------. .----------------.
| | | |
| L2 | | L2 |
| (Nested Guest) | | (Nested Guest) |
| | | |
|----------------'--'----------------|
| |
| L1 (Guest Hypervisor) |
| KVM (/dev/kvm) |
| |
.------------------------------------------------------.
| L0 (Host Hypervisor) |
| KVM (/dev/kvm) |
|------------------------------------------------------|
| Hardware (with virtualization extensions) |
'------------------------------------------------------'
术语
L0 – 0 级;裸机主机,运行 KVM
L1 – 1 级客户机;在 L0 上运行的 VM;也称为“客户机虚拟机管理程序”,因为它本身能够运行 KVM。
L2 – 2 级客户机;在 L1 上运行的 VM,这是“嵌套客户机”
注意
上面的图表是按照 x86 架构建模的;s390x、ppc64 和其他架构的嵌套设计可能有所不同。
例如,s390x 总是在裸机上运行 LPAR(逻辑分区)虚拟机管理程序,从而添加了另一层,并在嵌套设置中产生至少四个级别 — L0(裸机,运行 LPAR 虚拟机管理程序)、L1(主机虚拟机管理程序)、L2(客户机虚拟机管理程序)、L3(嵌套客户机)。
本文档将对所有架构都坚持使用三级术语(L0、L1 和 L2);并且主要关注 x86。
用例¶
在以下几种情况下,嵌套 KVM 可能会很有用,仅举几例
作为开发人员,您想在不同的操作系统 (OS) 上测试您的软件。与其从云提供商租用多个 VM,不如使用嵌套 KVM 租用足够大的“客户机虚拟机管理程序”(1 级客户机)。这反过来允许您创建多个嵌套客户机(2 级客户机),运行不同的操作系统,您可以在其上开发和测试您的软件。
“客户机虚拟机管理程序”及其嵌套客户机的实时迁移,用于负载平衡、灾难恢复等。
VM 镜像创建工具(例如,
virt-install
等)通常运行它们自己的 VM,用户希望这些工具在 VM 内部工作。一些操作系统在内部使用虚拟化来提高安全性(例如,让应用程序在隔离的环境中安全运行)。
启用“嵌套”(x86)¶
从 Linux 内核 v4.20 开始,默认情况下为 Intel 和 AMD 启用 nested
KVM 参数。(尽管您的 Linux 发行版可能会覆盖此默认值。)
如果您运行的 Linux 内核版本早于 v4.19,要启用嵌套,请将 nested
KVM 模块参数设置为 Y
或 1
。要在重启后保留此设置,您可以将其添加到配置文件中,如下所示
在裸机主机 (L0) 上,列出内核模块并确保 KVM 模块
$ lsmod | grep -i kvm kvm_intel 133627 0 kvm 435079 1 kvm_intel
显示
kvm_intel
模块的信息$ modinfo kvm_intel | grep -i nested parm: nested:bool
为了使嵌套 KVM 配置在重启后保持不变,请将以下内容放入
/etc/modprobed/kvm_intel.conf
中(如果该文件不存在,请创建它)$ cat /etc/modprobe.d/kvm_intel.conf options kvm-intel nested=y
卸载并重新加载 KVM Intel 模块
$ sudo rmmod kvm-intel $ sudo modprobe kvm-intel
验证是否启用了 KVM 的
nested
参数$ cat /sys/module/kvm_intel/parameters/nested Y
对于 AMD 主机,该过程与上述相同,只是模块名称为 kvm-amd
。
启动嵌套客户机 (x86)¶
一旦您的裸机主机 (L0) 配置为嵌套,您应该能够启动 L1 客户机,如下所示
$ qemu-kvm -cpu host [...]
上述操作将按原样将主机 CPU 的功能传递给客户机,或者为了更好地实现实时迁移兼容性,请使用 QEMU 支持的命名 CPU 型号。例如
$ qemu-kvm -cpu Haswell-noTSX-IBRS,vmx=on
然后,客户机虚拟机管理程序将随后能够运行具有加速 KVM 的嵌套客户机。
启用“嵌套”(s390x)¶
在主机虚拟机管理程序 (L0) 上,在 s390x 上启用
nested
参数$ rmmod kvm $ modprobe kvm nested=1
注意
在 s390x 上,内核参数 hpage
与 nested
参数互斥 — 即,为了能够启用 nested
,必须禁用 hpage
参数。
必须向客户机虚拟机管理程序 (L1) 提供
sie
CPU 功能 — 使用 QEMU,可以通过使用“主机直通”来完成此操作(通过命令行-cpu host
)。现在可以在 L1(客户机虚拟机管理程序)中加载 KVM 模块
$ modprobe kvm
使用嵌套 KVM 进行实时迁移¶
将其中包含实时嵌套客户机的 L1 客户机迁移到另一个裸机主机,在 Linux 内核 5.3 和 QEMU 4.2.0 及更高版本中(适用于 Intel x86 系统)以及 s390x 的旧版本上均可正常运行。
在 AMD 系统上,一旦 L1 客户机启动了 L2 客户机,在 L2 客户机关闭之前,不应再迁移或保存 L1 客户机(请参阅 QEMU 关于“savevm”/“loadvm”的文档)。在 L2 客户机运行时尝试迁移或保存和加载 L1 客户机将导致未定义的行为。您可能会在 dmesg
中看到 kernel BUG!
条目,内核“oops”或完全内核崩溃。此类迁移或加载的 L1 客户机不再被认为是稳定或安全的,必须重新启动。即使在 AMD 系统上,仅配置为支持嵌套而实际上未运行 L2 客户机的迁移 L1 客户机也应正常工作,但一旦启动客户机,可能会失败。
迁移 L2 客户机始终有望成功,因此即使在 AMD 系统上,所有以下方案都应正常工作
将嵌套客户机 (L2) 迁移到同一裸机主机上的另一个 L1 客户机。
将嵌套客户机 (L2) 迁移到不同裸机主机上的另一个 L1 客户机。
将嵌套客户机 (L2) 迁移到裸机主机。
从嵌套设置报告错误¶
调试“嵌套”问题可能需要筛选 L0、L1 和 L2 中的日志文件;这可能导致错误报告者和错误修复者之间来回繁琐的沟通。
请提及您正在使用“嵌套”设置。如果您正在运行任何类型的“嵌套”,请说明。不幸的是,需要指出这一点,因为在报告错误时,人们往往会忘记提及他们正在使用嵌套虚拟化。
确保您实际上正在 KVM 上运行 KVM。有时,人们没有为他们的客户机虚拟机管理程序 (L1) 启用 KVM,这会导致他们使用纯仿真或 QEMU 称之为“TCG”运行,但他们认为他们正在运行嵌套 KVM。因此,将“嵌套 Virt”(也可能意味着,KVM 上的 QEMU)与“嵌套 KVM”(KVM 上的 KVM)混淆。
要收集的信息(通用)¶
以下不是详尽的列表,而是一个很好的起点
L0 的内核、libvirt 和 QEMU 版本
L1 的内核、libvirt 和 QEMU 版本
L1 的 QEMU 命令行 — 当使用 libvirt 时,您可以在此处找到它:
/var/log/libvirt/qemu/instance.log
L2 的 QEMU 命令行 — 如上所述,当使用 libvirt 时,获取完整的 libvirt 生成的 QEMU 命令行
L0 中的
cat /sys/cpuinfo
L1 中的
cat /sys/cpuinfo
L0 中的
lscpu
L1 中的
lscpu
L0 的完整
dmesg
输出L1 的完整
dmesg
输出
要收集的 x86 特定信息¶
以下两个命令,x86info
和 dmidecode
,在大多数 Linux 发行版上应该都以相同的名称可用。
来自 L0 的
x86info -a
的输出来自 L1 的
x86info -a
的输出来自 L0 的
dmidecode
的输出来自 L1 的
dmidecode
的输出
需要收集的 s390x 特定信息¶
除了前面提到的通用细节外,还建议收集以下信息
来自 L1 的
/proc/sysinfo
; 这也将包括来自 L0 的信息。