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) |
'------------------------------------------------------'
Terminology: - L0 – レベル 0。ベアメタルのホストで、KVM を動かす。
L1 – レベル 1 のゲスト。L0 上で動く VM で、「ゲストハイパーバイザ」とも呼ぶ。それ自体が KVM を動かせるためである。
L2 – レベル 2 のゲスト。L1 上で動く VM で、これが「ネストゲスト」である。
注釈
上の図は x86 アーキテクチャを基にしている。s390x や ppc64 など他のアーキテクチャでは、ネストの設計が異なる可能性が高い。
たとえば s390x では、常にベアメタル上で LPAR(LogicalPARtition)ハイパーバイザが動いており、層がもう 1 つ増えるため、ネスト構成は少なくとも 4 つのレベルになる — L0(ベアメタル、LPAR ハイパーバイザを実行)、L1(ホストハイパーバイザ)、L2(ゲストハイパーバイザ)、L3(ネストゲスト)。
本書ではすべてのアーキテクチャについて 3 レベルの用語(L0、L1、L2)で統一し、主に x86 を扱う。
利用例
ネスト KVM が役立つ場面はいくつかある。いくつか挙げると:
開発者として、自分のソフトウェアをさまざまなオペレーティングシステム(OS)でテストしたい場合。クラウドプロバイダから複数の VM を借りる代わりに、ネスト KVM を使えば、十分な大きさの「ゲストハイパーバイザ」(レベル 1 のゲスト)を 1 つ借りられる。その上で、異なる OS を動かす複数のネストゲスト(レベル 2 のゲスト)を作り、そこでソフトウェアを開発・テストできる。
負荷分散や災害復旧などのための、「ゲストハイパーバイザ」とそのネストゲストのライブマイグレーション。
VM イメージ作成ツール(
virt-installなど)はしばしば自前の VM を動かすが、利用者はそれが VM の中で動くことを期待する。一部の OS は、セキュリティのために内部で仮想化を使う(たとえば、アプリケーションを安全に分離して実行させるため)。
「nested」を有効にする(x86)
Linux カーネル v4.20 以降では、Intel と AMD の ``nested``KVM パラメータ はデフォルトで有効である(ただし、使用している Linux ディストリビューシ ョンがこのデフォルトを上書きしているかもしれない)。
v4.19 より古い Linux カーネルを使っている場合は、ネストを有効にするには ``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)
ハードウェアが十分に新しい場合(新しいハードウェア仮想化拡張を持つ Intel Haswell プロセッサ以降)、次の追加機能もデフォルトで有効になる。「Shadow VMCS(Virtual Machine Control Structure)」と、ベアメタルのホスト(L0) での APIC 仮想化である。Intel ホストのパラメータ:
$ cat /sys/module/kvm_intel/parameters/enable_shadow_vmcs
Y
$ cat /sys/module/kvm_intel/parameters/enable_apicv
Y
$ cat /sys/module/kvm_intel/parameters/ept
Y
注釈
L2(すなわちネストゲスト)の動作が遅いと思われる場合は、上記が有効になっていることを確認する(特に enable_shadow_vmcs と ept)。
ネストゲストを起動する(x86)
ベアメタルのホスト(L0)でネストの設定が済めば、次のようにして L1 ゲスト を起動できるはずである:
$ qemu-kvm -cpu host [...]
上記はホスト CPU のケイパビリティをそのままゲストにパススルーする。ライ ブマイグレーションの互換性を高めたい場合は、QEMU が対応する名前付き CPU モデルを使う。例:
$ qemu-kvm -cpu Haswell-noTSX-IBRS,vmx=on
そうすれば、ゲストハイパーバイザは以降、アクセラレーションされた KVM で ネストゲストを動かせるようになる。
「nested」を有効にする(s390x)
ホストハイパーバイザ(L0)で、s390x の
nestedパラメータを有効にする:$ rmmod kvm $ modprobe kvm nested=1
注釈
s390x では、カーネルパラメータ hpage と nested パラメータは同時に使えない — つまり nested を有効にするには、hpage パラメータを無効に*しなければならない*。
ゲストハイパーバイザ(L1)には
sieCPU 機能を渡す必要がある — QEMU では「host passthrough」(コマンドラインの-cpu host)を使えばよい。これで L1(ゲストハイパーバイザ)で KVM モジュールをロードできる:
$ modprobe kvm
ネスト KVM でのライブマイグレーション
*実行中*のネストゲストを内包する L1 ゲストを別のベアメタルホストへマイグ レーションする機能は、Intel x86 システムでは Linux カーネル 5.3 と QEMU 4.2.0 以降で動作し、s390x ではそれより古いバージョンでも動作する。
AMD システムでは、L1 ゲストが L2 ゲストを起動したあとは、L2 ゲストが停止 するまで L1 ゲストをマイグレーションしたり保存したりすべきではない(QEMU ドキュメントの「savevm」/「loadvm」を参照)。L2 ゲストの実行中に L1 ゲス トをマイグレーションまたは保存・読み込みしようとすると、動作は未定義にな る。``dmesg``に ``kernelBUG!``が出たり、カーネルの oops や、最悪の場合は カーネルパニックが起きたりするかもしれない。そのようにマイグレーションま たは読み込みされた L1 ゲストは、もはや安定とも安全ともみなせないので、再 起動しなければならない。ネストに対応するよう設定しただけで実際には L2 ゲ ストを動かしていない L1 ゲストのマイグレーションは、AMD システムでも通常 どおり動作するはずだが、ゲストを起動すると失敗するかもしれない。
L2 ゲストのマイグレーションは常に成功するはずなので、次のどの場面も AMD システムで動作するはずである:
ネストゲスト(L2)を*同じ*ベアメタルホスト上の別の L1 ゲストへマイグレーションする。
ネストゲスト(L2)を*別の*ベアメタルホスト上の別の L1 ゲストへマイグレーションする。
ネストゲスト(L2)をベアメタルホストへマイグレーションする。
ネスト構成でのバグ報告
「ネスト」の問題をデバッグするには、L0、L1、L2 にまたがるログファイルを 調べる必要がある。そのため、バグの報告者と修正者の間で面倒なやり取りが何 度も起きがちである。
「ネスト」構成で動かしていることを伝える。何らかの「ネスト」を動かしているなら、そう書く。残念ながらこれは明示する必要がある。バグを報告するとき、人はネスト仮想化を使っていることさえ*書くのを*忘れがちだからである。
実際に KVM on KVM で動かしていることを確認する。ゲストハイパーバイザ(L1)で KVM を有効にしておらず、純粋なエミュレーション、いわゆる QEMU でいう「TCG」で動かしているのに、ネスト KVM を動かしていると思い込んでいる人がいる。こうして「nested Virt」(KVM 上の QEMU を指すこともある)と「nested KVM」(KVM on KVM)を混同してしまう。
収集する情報(共通)
以下は完全な一覧ではないが、非常に良い出発点になる:
L0 のカーネル、libvirt、QEMU のバージョン
L1 のカーネル、libvirt、QEMU のバージョン
L1 の QEMU コマンドライン — libvirt を使っている場合はここにある:
/var/log/libvirt/qemu/instance.logL2 の QEMU コマンドライン — 上と同様に、libvirt を使っている場合は libvirt が生成した完全な QEMU コマンドラインを取得する
L0 の
cat /sys/cpuinfoL1 の
cat /sys/cpuinfoL0 の
lscpuL1 の
lscpuL0 の
dmesgの全出力L1 の
dmesgの全出力
x86 固有の収集情報
以下に挙げる ``x86info``と ``dmidecode``のコマンドは、ほとんどの Linux ディストリビューションで同じ名前で利用できるはずである:
L0 での
x86info -aの出力L1 での
x86info -aの出力L0 での
dmidecodeの出力L1 での
dmidecodeの出力
s390x 固有の収集情報
前述の共通の情報に加えて、以下も推奨する:
L1 の
/proc/sysinfo。これには L0 の情報も含まれる