直面した課題:仮想マシンがハイパーバイザーの提供する以上のものを必要とするとき
私は長らくProxmox VEを使って12以上の仮想マシン(VM)とコンテナを管理するホームラボを運用してきました。ここは本番環境にデプロイする前にあらゆることを試すのに理想的な環境です。
その中には、単に「まあまあ」ではなく、実際に高いパフォーマンスを要求するVMがいくつかありました。例えば、軽いゲームや基本的なグラフィック作業に使うWindows VMや、pfSense/OPNsenseを実行し、非常に低いレイテンシでネットワークを処理させたいVMなどです。
当初、VMに仮想GPUや仮想ネットワークカード(virtio)を割り当てれば十分だと考えていました。しかし、すぐにこの方法の限界に気づきました。Windows VMでは、仮想化されたグラフィックカード(SPICE/QXLなど)は、ゲーミングや高いDirectX/OpenGL性能を必要とするアプリケーションの要件を満たせないことがよくあります。ゲームはカクついたり、互換性のあるドライバーがないために起動すらできないこともありました。ビデオレンダリング作業も非常に遅かったです。
ルーターVMに関しては、virtio-netがかなり良いパフォーマンスを提供していましたが、それでも物理ハードウェアと同等の性能を達成したいと考えていました。これは、大量のネットワークトラフィックを処理する場合や、ネットワークカード(NIC)の特定の機能が必要な場合に特に重要です。
要するに、ハイパーバイザーが仮想化メカニズムを通じて提供するものは、通常のサーバータスクには十分なことが多いです。しかし、専門的なグラフィック性能、ゲーミング、またはネットワークに対する低レイテンシと高帯域幅の要件に直面すると、このメカニズムは大きな障壁となります。
原因分析:なぜ仮想化はハードウェアの真の性能を制限するのか?
性能制限の主な原因は、ハイパーバイザーがハードウェアとどのように相互作用するかという点にあります。VMが起動すると、ハイパーバイザー(KVM/Proxmoxなど)はVMと物理ハードウェアの間に抽象化レイヤーを作成します。VMはGPUやネットワークカードに直接アクセスする代わりに、ハイパーバイザーがエミュレートする仮想デバイスにのみアクセスできます。例えば、GPUの場合、それは仮想VGAデバイス(QXL、SPICE)や準仮想化グラフィックカードである可能性があります。ネットワークカードの場合、それはvirtio-netです。
このエミュレーションプロセスはいくつかの問題を引き起こします:
- オーバーヘッド: VMがハードウェアと通信する必要があるたびに、その要求はハイパーバイザーを経由しなければなりません。ハイパーバイザーはVMからのコマンドを物理ハードウェアに、またその逆も翻訳する役割を担っており、これが遅延を引き起こし、物理ホストのCPUリソースを消費します。
- 機能の不足: 仮想化されたデバイスは通常、基本的な機能しかサポートしていません。例えば、仮想GPUは物理GPUのような完全なグラフィック命令セット、専用メモリ、並列処理能力を持っていません。このため、グラフィックを多用するアプリケーションやゲームは効率的に動作せず、場合によっては実行すらできません。
- ドライバーの互換性: VMは仮想化されたハードウェア用のドライバーを使用する必要があります。このドライバーは通常、物理ハードウェア用のメーカー純正ドライバーほど最適化されていません。さらに、仮想化環境を検出するアンチチートシステムを持つ一部のゲームなど、特定のドライバーを要求するソフトウェアと互換性がない場合があります。
- 高レイテンシ: これはネットワークカードにとって特に重要な要素です。仮想化レイヤーを介したデータ転送はレイテンシを増加させ、VoIP、オンラインゲーム、高性能なネットワークシステムなど、時間的制約のあるアプリケーションに深刻な影響を与えます。
まとめると、ハイパーバイザーは柔軟性とリソース管理機能を提供しますが、その代償として性能とハードウェアへの直接アクセス能力が犠牲になります。
解決策:許容から最適化まで
これらの制限に直面し、私は改善のための解決策を探しました:
1. 仮想化ハードウェアの許容と最適化
これはデフォルトで最もシンプルな解決策です。ユーザーはKVM/Proxmoxが提供する仮想化デバイス(virtio-net、virtio-blk、グラフィック用のSPICE/QXLなど)を使用するだけです。最適化のためには、以下が考えられます:
- VMにより多くのRAMとCPUを割り当てる。
- VM内で最新のVirtIOドライバーを使用する。
- 最も効率的な表示モードを選択する(例:SPICEは通常VNCよりも優れています)。
長所: 設定が簡単で、特別なハードウェアは不要、柔軟性がある(VMのライブマイグレーションが可能)。
短所: 解析した通り、グラフィック性能とネットワークレイテンシの制限は依然として克服できません。重いグラフィックゲーム、プロフェッショナルなグラフィックアプリケーション、または高いネットワーク要件を持つアプリケーションには適していません。
2. SR-IOV (Single Root I/O Virtualization) の使用
SR-IOVは、物理PCIデバイス(例:ネットワークカード)が複数の独立した仮想機能(Virtual Functions – VFs)をVM用に作成できるようにする技術です。各VFはVMに直接割り当てることができ、VMがハードウェアと直接通信し、I/Oタスクのためにハイパーバイザーをバイパスすることを可能にします。
長所: ネットワークデバイスではネイティブとほぼ同等の性能を発揮し、物理ホストのCPU負荷も軽減されます。
短所: 対応ハードウェア(ネットワークカード、マザーボード、CPU)が必要です。設定がより複雑です。この技術は、ネットワークカードのように直接的かつ一般的にGPUには適用できません。私の経験では、SR-IOVはPCIパススルー全体に比べてかなり選り好みがあり、柔軟性に欠けます。
3. PCIパススルー (VFIO) – 最適な解決策
これこそ私が適用し、完全に満足している解決策です。PCIパススルー(VFIO – Virtual Function I/Oとも呼ばれます)は、物理PCIデバイス(GPU、ネットワークカード、USBコントローラー、NVMe SSDなど)をVMに直接割り当てることを可能にします。これにより、VMはそのデバイスが自分自身に直接接続されているかのように認識し、ハイパーバイザーを完全にバイパスして直接通信します。
長所:
- ネイティブ性能: VMは、メーカー純正ドライバーを含め、物理デバイスの全能力と機能を使用できます。
- 低レイテンシ: I/Oタスクのレイテンシを大幅に削減します。
- 高い互換性: 仮想化では提供できないドライバー互換性の問題や特定の機能を解決します。
短所:
- パススルーされたデバイスは、物理ホストまたは他のVMでは使用できなくなります。
- ホストハードウェアがIOMMU(Intel VT-dまたはAMD-Vi)をサポートしている必要があります。
- 設定がやや複雑です。
- パススルーデバイスを持つVMはライブマイグレーションできません。
私のホームラボにおけるグラフィックとネットワークの性能要件を考えると、PCIパススルーは他に代わることのできない選択肢でした。
最良の方法:KVM/ProxmoxでのPCIパススルー設定ガイド
数多くの試行錯誤と調整を経て、以下にProxmox(KVMベース)でPCIパススルーを成功させるために私が行った手順を説明します。
ステップ1:ハードウェア互換性(IOMMU)の確認
まず、物理ホストのCPUとマザーボードがIOMMU(Intel VT-dまたはAMD-Vi)をサポートしていることを確認する必要があります。これは、ハイパーバイザーがPCIデバイスをVMに直接割り当てることを可能にする技術です。
CPUの確認:
- Intel:CPUの仕様を調べて「Intel VT-d」のサポートを確認します。
- AMD:同様に、CPUが「AMD-Vi」(AMD IOMMUとも呼ばれる)をサポートしているか確認します。
マザーボードの確認:BIOS/UEFIにVT-d/AMD-Viを有効にするオプションがあることを確認します。
ステップ2:BIOS/UEFIでIOMMUを有効にする
Proxmoxホストを再起動し、BIOS/UEFIに入って以下のオプションを探して有効にします:
- Intel:Virtualization Technology for Directed I/O (VT-d)、またはIntel VT-d。
- AMD:AMD IOMMU、またはSVM Mode(IOMMUがこのセクションにある場合があります)。
設定を保存して再起動します。
ステップ3:Proxmoxのカーネル(ホストOS)でIOMMUを有効にする
BIOSで有効にした後、LinuxカーネルにIOMMUを使用するように伝える必要があります。私は通常、GRUBファイルを編集することでこれを行います。
GRUB設定ファイルを開きます:
nano /etc/default/grub
`GRUB_CMDLINE_LINUX_DEFAULT`で始まる行を探します。行の末尾(二重引用符内)に以下のパラメータを追加します:
- Intelの場合:
intel_iommu=on iommu=pt - AMDの場合:
amd_iommu=on iommu=pt
例えば、Intelの場合、私の行はこのようになります:
GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt"
ファイルを保存し(Ctrl+O、Enter、Ctrl+X)、GRUBを更新します:
update-grub
最後に、Proxmoxホストを再起動します:
reboot
再起動後、IOMMUが有効になっているか確認します:
dmesg | grep -e DMAR -e IOMMU
find /sys/kernel/iommu_groups/ -type l
DMAR/IOMMUに関連するメッセージが表示され、`find`コマンドから出力があれば、IOMMUが機能していることを意味します。
ステップ4:PCIデバイスIDとIOMMUグループの特定
デバイスをパススルーするには、そのIDを知り、そのデバイスが独立したIOMMUグループ内にあることを確認する必要があります。これは最も重要なステップの1つです。
すべてのPCIデバイスをリスト表示します:
lspci -nnk
