KVM/ProxmoxでのSR-IOV設定:10GbE NICを共有してベアメタル並みのネットワーク性能を実現する

Virtualization tutorial - IT technology blog
Virtualization tutorial - IT technology blog

実際の課題:仮想化環境における10GbEネットワークのボトルネック

仮想マシン(VM)間の帯域幅を最大限に活用するために、Intel X520-DA2やMellanox ConnectX-3などの10GbEネットワークカードをProxmoxラボ環境に導入するケースは少なくありません。スロットに装着すれば10Gリンクとして正常に認識されます。しかし、実際の通信速度を測定してみると、期待を裏切る結果になることがよくあります。

筆者の環境では、3ノードのCephクラスタに加えて、複数のバックエンドPostgreSQL VMやデータストリーミング用VMを運用しています。同一のMikrotik CRS305光スイッチおよびSFP+ DACケーブルを介して接続した際、以下の3つの大きな問題に直面しました。

  • 高レイテンシとジッターの発生:標準のLinux Bridgeを介した2台のVM間のPing応答時間は、0.3msから1ms近くまで大きく変動しました。特に負荷の高いI/O処理中には、顕著なジッターが確認されました。
  • ソフト割り込み(softirq)によるCPUの枯渇:iperf3で8 Gbps付近まで負荷をかけると、ホスト上のXeon E5 CPU 2コアの使用率が即座に90〜100%に達しました。主な原因は、毎秒数百万個もの小さなパケットを処理するためにksoftirqdプロセスがCPUを専有したことです。
  • パケットドロップ:トラフィックが急増した際、Linux Bridgeのソフトウェアバッファがオーバーフローを起こしました。その結果、TCP再送が多発し、スループットが5〜6 Gbpsまで大幅に低下しました。

デフォルトの仮想化ネットワーク構成が低速な理由

ベアメタル環境でネイティブ動作させた場合、10GbEカードは容易に9.8 Gbpsに到達します。では、なぜVMを経由すると性能が著しく落ちてしまうのでしょうか。

VirtIOとLinux BridgeまたはOpen vSwitchを組み合わせた構成では、パケットは次のような複雑な中間処理パイプラインを通過する必要があります。

  1. 物理NICがフレームを受信し、ハードウェア割り込み(Hardware IRQ)を発行してホストCPUに通知する。
  2. ホストカーネルのネットワークドライバがパケットを取りまとめ、Linux Bridgeへ渡す。
  3. BridgeがMACアドレステーブルを参照し、該当VMのtapインターフェースへデータをコピーする。
  4. QEMUが仮想ネットワーク割り込みをエミュレートし、VMのメモリ空間へデータを転送する。
  5. ゲストOSがvirtio-netドライバを介してパケットを受信する。

この一連のフローにより、カーネル空間とユーザー空間の間で大量のコンテキストスイッチが発生します。また、RAM上でのメモリコピーも複数回行われます。トラフィックが毎秒100万パケット(1 Mpps)を超えると、ホストCPUが実質的なソフトウェアスイッチとして動作することになり、処理能力の限界を迎えてしまいます。

Proxmoxにおけるネットワークボトルネック解消の3つのアプローチ

ハードウェア構成と要件に応じて、主に以下の3つの選択肢が存在します。

1. VirtIOにおけるvhost-netおよびMultiqueueの有効化

vhost-netは、パケット処理をQEMUプロセス経由ではなくホストカーネル内で直接処理できるようにします。これにMultiqueueを組み合わせることで、ネットワーク割り込みの負荷を複数のvCPUに均等に分散できます。

メリット:設定が容易でハードウェアを選ばず、Proxmoxのライブマイグレーション機能もそのまま利用可能。
デメリット:性能向上は25〜30%程度に留まり、依然としてホストCPUにネットワーク負荷がかかるため、レイテンシの大幅な削減は期待できません。

2. ネットワークカード全体のPCIeパススルー

VFIOを利用して、物理PCIeポート全体を単一のVMに直接割り当てます。

メリット:ベアメタルとほぼ100%同等のパフォーマンスを発揮。超低レイテンシを実現し、ホストCPUへの負荷は皆無。
デメリット:ハードウェアリソースを大量に消費します。1つの10Gポートを特定のVMに専有させると、他のVMではそのポートを利用できなくなります。

3. SR-IOV(Single Root I/O Virtualization)による分割

SR-IOVを利用すると、1枚の物理ネットワークカードをハードウェアレベルで複数の独立した仮想ポートへと直接多重化できます。

  • Physical Function(PF):オリジナルの物理ポート。ホストOSによって管理されます。
  • Virtual Function(VF):NICチップ自体が生成する仮想ポート。各VFは独自のRX/TXキュー、DMAコントローラ、レジスタを備えています。

それぞれのVFをVMへ直接パススルーすることで、VMは仮想スイッチやホストカーネルを完全にバイパスし、NICハードウェアへ直接データを読み書きできるようになります。

Proxmox VEでのSR-IOV設定手順ガイド

本ハンズオンでは、ホームラボや中小エンタープライズで広く利用されているIntel X520(82599ESチップ、ixgbeドライバ使用)を例に解説します。

ステップ1:BIOS/UEFIで仮想化機能を有効化

サーバーのBIOS画面に入り、以下の3項目を確認して有効にします。

  • Intel VT-d(Intel製CPUの場合)または AMD IOMMU / SVM(AMD製CPUの場合)。
  • SR-IOV Support: Enabledに設定。
  • Above 4G Decoding:複数のPCIeデバイスを搭載している場合、メモリアドレス不足によるエラーを防ぐため有効にします。

ステップ2:ProxmoxホストでIOMMUを有効化

Linuxのブートローダー設定ファイルを開きます。

nano /etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT変数にIOMMUパラメータを追加します。

# Intel CPU搭載機の場合:
GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt"

# AMD CPU搭載機の場合:
GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on iommu=pt"

ファイルを保存し、ブートローダーの設定を更新してサーバーを再起動します。

update-grub
# ProxmoxでZFS(systemd-boot)を使用している場合:
# proxmox-boot-tool refresh
reboot

再起動後、IOMMUが正常に有効化されているか確認します。

dmesg | grep -e DMAR -e IOMMU

出力にDMAR: IOMMU enabledが表示されていれば、準備は完了です。

ステップ3:仮想機能(VF)の初期化

ip linkコマンドで10Gネットワークポート名を確認します。ここではenp4s0f0と仮定します。

カードがサポートする最大VF数を確認します。

cat /sys/class/net/enp4s0f0/device/sriov_totalvfs

Intel X520では通常63と表示されます。テストとして4つのVFを作成します。

echo 4 > /sys/class/net/enp4s0f0/device/sriov_numvfs

作成されたVFをlspciで確認します。

lspci -nn | grep -i "Virtual Function"

システム上に82599 Virtual Functionチップを搭載した04:10.0、04:10.2などのIDを持つ4つの新しいデバイスが表示されます。

ステップ4:起動時の設定の永続化

ステップ3で手動作成したVFは、再起動すると消失します。自動的にVFを作成し、固定MACアドレスを付与するには、/etc/network/interfacesファイルを編集します。

auto enp4s0f0
iface enp4s0f0 inet manual
    # ネットワークカードUP時に4つのVFを初期化
    post-up echo 4 > /sys/class/net/enp4s0f0/device/sriov_numvfs
    # 固定MACを設定し、VM側で自由にIP変更できるようにspoofcheckを無効化
    post-up ip link set enp4s0f0 vf 0 mac ba:ad:1a:20:00:01 spoofchk off
    post-up ip link set enp4s0f0 vf 1 mac ba:ad:1a:20:00:02 spoofchk off
    post-down echo 0 > /sys/class/net/enp4s0f0/device/sriov_numvfs

ステップ5:仮想マシンへのVFの割り当て

ProxmoxのWeb GUIを使用するのが最も簡単です。

  1. 割り当て対象のVMを選択し、ハードウェアタブを開きます。
  2. 追加をクリックし、PCIデバイスを選択します。
  3. Raw Deviceを選択し、割り当てたいVFのアドレス(例:0000:04:10.0)を指定します。
  4. PCI-Expressにチェックを入れます。注意:単一のVFのみを割り当てるため、「All Functions」にはチェックを入れないでください。
  5. 追加をクリックして確定し、VMを起動します。

ステップ6:実環境でのパフォーマンス測定

VMにログインし、lspci -kを実行します。使用中のドライバとしてixgbevfが表示され、VMが物理カードを直接認識していることが確認できます。

SR-IOVを使用する2台のVM間でiperf3によるベンチマークを実行します。

# VM 1(サーバー側):
iperf3 -s

# VM 2(クライアント側):
iperf3 -c 192.168.10.50 -P 8

結果には劇的な違いが現れます。

  • スループット:9.42 Gbpsに達し、物理リンクの理論限界値にほぼ肉薄。
  • ホストCPU負荷:転送中を通じて、従来の85%からわずか2〜4%程度まで劇的に減少。
  • Pingレイテンシ:応答時間は0.05〜0.07msまで低下し、高負荷時でもパケットドロップが完全に解消。

ただし、SR-IOVを利用するトレードオフとして、通常のライブマイグレーションが利用できなくなります。高可用性(HA)が求められる構成の場合は、SR-IOVポートと予備のVirtIOポートでBondingを構成し、フォールバックによってオンラインマイグレーションの可用性を維持する方法が推奨されます。

Share: