仮想化の「マトリョーシカ」を解剖する:Nested Virtualizationが抱えるCPUの苦悩と真実
こんにちは。クラウドの深淵を覗き込み、夜な夜なパケットの挙動を夢に見るSREです。
最近、クラウドネイティブな環境でも「CI/CDパイプラインの中で軽量なVMを動かしたい」「コンテナ実行環境の中でさらに別のランタイムを試したい」という要件から、Nested Virtualization(ネステッド仮想化)を避けては通れない場面が増えてきました。
一見すると「VMの中にVMを作る」だけに見えますが、これがネットワークやI/Oのレイテンシにどう響くのか、そしてCPUが裏でどれほど必死に汗をかいているのか、現場でトラブルシューティングする視点で解説します。
—
1. なぜ「仮想化の二重化」は泥沼なのか
通常、ハイパーバイザー(L0)は、物理CPUの持つ「仮想化支援機能(Intel VT-x / AMD-V)」を使い、ゲストOS(L1)を特権モードに近い状態で走らせます。ここまでは教科書通りです。
しかし、L1の中でさらにハイパーバイザー(L2)を動かそうとすると、「誰が物理CPUの制御権を握るのか」という問題が発生します。
L2が「特権命令」を発行したとき、L1はそれを「本物のCPUへの命令」だと勘違いして処理しようとしますが、実際にはL0がその命令を横取りして解釈する必要があります。この「命令の横取り」こそが、パフォーマンスを削り取る最大の要因です。
VM-Exitの連鎖(Nested VM-Exit)
L2の命令がL1を突き抜け、L0にまで届く現象をNested VM-Exitと呼びます。
1. L2が特権命令実行(ここでVM-Exit発生)
2. L1のハイパーバイザーが「おっと、私の仕事じゃない」と判断
3. L0にトラップが転送される
4. L0がL2の挙動をエミュレートし、L1に戻す
この往復ビンタのようなコンテキストスイッチが、ネットワークパケットの処理性能をガタガタにする主犯です。
—
2. 現場で使える設定:AWS EC2(Nitro)でのNested VT-x有効化
例えば、AWSの c5 や m5 インスタンス(Nitro世代)でネステッド仮想化を有効にする場合、単純にインスタンスを立てるだけでは不十分です。CPUフラグをゲストOSにパススルーする必要があります。
以下は、cloud-init やシェルスクリプトでKVMモジュールのパラメータを注入する典型的な例です。
# 1. まず、L1のゲストOS上でkvm_intelモジュールがネステッドを許可しているか確認
cat /sys/module/kvm_intel/parameters/nested
# 2. もし 'N' なら、一時的に有効化(再起動で消えるため恒久化が必要)
sudo modprobe -r kvm_intel
sudo modprobe kvm_intel nested=1
# 3. 恒久化設定(/etc/modprobe.d/kvm-nested.conf を作成)
echo "options kvm_intel nested=1" | sudo tee /etc/modprobe.d/kvm-nested.conf
これだけでL1上の kvm-ok コマンドが「KVM acceleration can be used」と返してくれるはずです。
—
3. 実践:Pythonでハイパーバイザーの応答速度を計測する
Nested環境において、Web APIをL2で動かす場合、遅延(Latency)は致命的になります。簡単なPythonスクリプトで、L1とL2の境界を跨ぐ際のオーバーヘッドを測定してみましょう。
import time
import subprocess
def measure_latency():
# L2からホスト(L0)へ ping を打つのではなく、
# L2からL1へのシステムコール発行時間を計測するイメージ
start = time.perf_counter()
# 簡易的なコマンド実行によるコンテキストスイッチのオーバーヘッド計測
subprocess.run(["/bin/true"], capture_output=True)
end = time.perf_counter()
return (end - start) * 1000
print(f"L2での命令実行コスト: {measure_latency():.4f} ms")
この数値がベアメタル環境と比較して数倍に跳ね上がるようであれば、I/O処理にNestedのオーバーヘッドが直撃している証拠です。
—
4. SRE視点のトラブルシューティングTips
現場で「なぜかNested環境でパケットロスする」「Web APIの応答が不安定だ」という事態に陥ったとき、私が必ずチェックするポイントを共有します。
- CPU Pinningの確認: L1の仮想CPU(vCPU)が物理コア上で頻繁に移動していると、Nested VM-Exitのオーバーヘッドがさらに増幅します。
tasksetやハイパーバイザー側の設定でvCPUを物理コアに固定(Pinning)してください。 - EPT/NPTの設定: ゲスト物理アドレスからホスト物理アドレスへの変換(Nested Page Tables)は、二重化されるとメモリ参照コストが跳ね上がります。CPUのフラグで
vmxだけでなくeptも確実にパススルーされているか確認しましょう。 - MTUの不一致: Nested環境では、カプセル化(VXLAN等)を多用しがちです。オーバーレイネットワークのヘッダー分を考慮し、MTUを
1450程度まで下げておかないと、フラグメントが多発してスループットが壊滅します。
最後に
ネステッド仮想化は強力な武器ですが、「便利さと引き換えにハードウェアへの直通ルートを捨てる行為」です。
アーキテクチャ設計時には、「本当にNestedが必要か? コンテナで代替できないか?」と自問自答してください。それでも必要なら、このCPUの二重苦とパケットの旅路を理解した上で、チューニングという名の「泥臭い追い込み」を楽しんでください。
皆さんのインフラに、平穏なCPUサイクルがあらんことを。
コメント