使用 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) 上測試您的軟體。使用巢狀 KVM 可以讓您租用足夠大的“訪客管理程式”(1 級訪客),而不是從雲提供商那裡租用多個 VM。這反過來又允許您建立多個執行不同作業系統的巢狀訪客(2 級訪客),您可以在這些訪客上開發和測試您的軟體。

  • “訪客管理程式”及其巢狀訪客的即時遷移,用於負載均衡、災難恢復等。

  • VM 映象建立工具(例如 virt-install 等)通常執行自己的 VM,使用者希望這些工具能在 VM 內部工作。

  • 某些作業系統在內部使用虛擬化來實現安全性(例如,讓應用程式安全地隔離執行)。

啟用“巢狀” (x86)

從 Linux 核心 v4.20 起,Intel 和 AMD 的 nested KVM 引數預設啟用。(儘管您的 Linux 發行版可能會覆蓋此預設設定。)

如果您執行的是 v4.19 之前的 Linux 核心,要啟用巢狀,請將 nested KVM 模組引數設定為 Y1。要使此設定在重啟後仍然有效,您可以將其新增到配置檔案中,如下所示:

  1. 在裸機主機 (L0) 上,列出核心模組並確保 KVM 模組

    $ lsmod | grep -i kvm
    kvm_intel             133627  0
    kvm                   435079  1 kvm_intel
    
  2. 顯示 kvm_intel 模組的資訊

    $ modinfo kvm_intel | grep -i nested
    parm:           nested:bool
    
  3. 為了使巢狀 KVM 配置在重啟後仍然有效,請將以下內容放置在 /etc/modprobed/kvm_intel.conf 中(如果檔案不存在則建立)

    $ cat /etc/modprobe.d/kvm_intel.conf
    options kvm-intel nested=y
    
  4. 解除安裝並重新載入 KVM Intel 模組

    $ sudo rmmod kvm-intel
    $ sudo modprobe kvm-intel
    
  5. 驗證 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)

  1. 在主機管理程式 (L0) 上,在 s390x 上啟用 nested 引數

    $ rmmod kvm
    $ modprobe kvm nested=1
    

注意

在 s390x 上,核心引數 hpagenested 引數互斥——即,要啟用 nestedhpage 引數必須停用。

  1. 訪客管理程式 (L1) 必須提供 sie CPU 功能——使用 QEMU,這可以透過使用“主機直通”(透過命令列 -cpu host)來完成。

  2. 現在 KVM 模組可以載入到 L1(訪客管理程式)中

    $ modprobe kvm
    

使用巢狀 KVM 進行即時遷移

在 Linux 核心 5.3 和 QEMU 4.2.0 for Intel x86 系統中,以及更早版本的 s390x 系統中,將 L1 訪客(其中包含一個即時巢狀訪客)遷移到另一臺裸機主機是可行的。

在 AMD 系統上,一旦 L1 訪客啟動了 L2 訪客,L1 訪客在 L2 訪客關閉之前不應再進行遷移或儲存(請參閱 QEMU 文件中的“savevm”/“loadvm”)。嘗試在 L2 訪客執行時遷移或儲存-載入 L1 訪客將導致未定義行為。您可能會在 dmesg 中看到 kernel BUG! 條目、核心“oops”或直接的核心崩潰。此類已遷移或載入的 L1 訪客不再被視為穩定或安全,必須重新啟動。僅配置為支援巢狀但未實際執行 L2 訪客的 L1 訪客遷移,即使在 AMD 系統上也應正常執行,但一旦訪客啟動,可能會失敗。

遷移 L2 訪客始終有望成功,因此以下所有場景即使在 AMD 系統上也應該有效:

  • 將巢狀訪客 (L2) 遷移到同一裸機主機上的另一個 L1 訪客。

  • 將巢狀訪客 (L2) 遷移到不同裸機主機上的另一個 L1 訪客。

  • 將巢狀訪客 (L2) 遷移到裸機主機。

報告巢狀設定中的錯誤

除錯“巢狀”問題可能涉及篩選 L0、L1 和 L2 中的日誌檔案;這可能導致錯誤報告者和錯誤修復者之間繁瑣的來回溝通。

  • 說明您處於“巢狀”設定中。如果您正在執行任何型別的“巢狀”,請務必提及。不幸的是,這一點需要強調,因為在報告錯誤時,人們往往會忘記甚至提及他們正在使用巢狀虛擬化。

  • 確保您實際正在 KVM 上執行 KVM。有時人們沒有為他們的訪客管理程式 (L1) 啟用 KVM,這導致他們使用純模擬或 QEMU 稱之為“TCG”的方式執行,但他們認為他們正在執行巢狀 KVM。因此,這混淆了“巢狀虛擬化”(也可能意味著 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 特定資訊收集

以下兩個命令 x86infodmidecode 在大多數 Linux 發行版中都應該以相同的名稱可用

  • L0 中 x86info -a 的輸出

  • L1 中 x86info -a 的輸出

  • L0 中 dmidecode 的輸出

  • L1 中 dmidecode 的輸出

s390x 特定資訊收集

除了前面提到的通用詳情,還建議收集以下資訊

  • L1 中的 /proc/sysinfo;這還將包括 L0 中的資訊