KVM仮想マシン向けネットワーク割り当て方式の比較
高負荷なデータベースや毎秒数百万パケットを処理するゲートウェイをKVM上で稼働させる際、ハイパーバイザー層におけるネットワークボトルネックは最大の課題となります。現在、Linuxエコシステムでは仮想マシン向けに主に3つのネットワーク割り当て方式が提供されています。
- Linux Bridge / Open vSwitch (VirtIO-Net): デフォルトの仮想化ネットワークモデルです。パケットはホストOSのネットワークスタックおよびvhost-netを経由してVMに到達します。柔軟性が高く、スムーズなLive Migrationに対応している一方、ホストCPUのコンテキストスイッチが頻繁に発生するため、遅延は通常25〜50µs程度に増加します。
- フルNIC PCIe Passthrough: 物理NIC全体を単一のVMに直接割り当てる方式です。ベアメタルと100%同等の性能が得られますが、物理ポート1つにつき1台の仮想マシンしか収容できないため、リソース効率が極めて悪いというデメリットがあります。
- SR-IOV (Single Root I/O Virtualization): 1つの物理ポート(Physical Function – PF)をハードウェアレベルで数十個の独立した仮想NIC(Virtual Functions – VF)に分割するPCI-SIGの拡張規格です。各VFはIOMMU/VFIO経由でVMに直接割り当てられます。パケットはホストカーネルを完全にバイパスしてハードウェアへ直接流れます。
技術仕様の詳細比較
以下の表は、3つの方式における実際の運用基準を比較したものです。
| 項目 | VirtIO-Net (Bridge) | PCIe Passthrough | SR-IOV (VFIO) |
|---|---|---|---|
| Throughput & PPS | ホストCPUクロックに依存・制限 | 100% ワイヤースピード(Line-rate) | ハードウェア性能の約98〜99% |
| レイテンシ (Latency) | 高 (25 – 50 µs) | 極めて低い (< 2 µs) | 非常に低い (1.5 – 3 µs) |
| ホストCPU使用率 | 5〜10 Gbpsのトラフィックで15〜30%消費 | ほぼ0% | ほぼ0% |
| VM集約率 | 制限なし | 1 VM / 1物理ポート | NICチップセットに応じて8〜64+ VF |
| Live Migration | KVM標準対応 | 非対応 | VirtIOとのフェイルオーバー構成が必要 |
どのようなユースケースでSR-IOVが必要になるのか?
一般的なWebサーバーやREST APIでは、利便性の面でVirtIO-Netが最もバランスの取れた選択肢です。SR-IOVへの移行を検討すべきなのは、主に以下のようなシナリオです。
- 高パケットレートが求められるネットワークアプリケーション: 1,000万PPS以上を処理する必要がある5Gコア(UPF)、WebRTCメディアゲートウェイ、パブリックDNSリゾルバー、高頻度金融取引システムなど。
- 分散データベース管理システム: レプリケーションが頻繁に発生し、ジッターのない安定したマイクロ秒単位の低遅延が要求されるRedis Cluster、ScyllaDB、PostgreSQLクラスタなど。
- インフラコストの最適化: Intel X710/E810などの25GbEカード1枚を活用し、ホストCPUを圧迫することなく16〜32台のVMに高速通信回線を同時に割り当てる場合。
Linux KVMにおけるSR-IOV設定手順
ステップ1:BIOSおよびLinuxカーネルでIOMMUを有効化
サーバーのBIOS/UEFI設定を開き、Intel VT-d(またはAMD IOMMU)とSR-IOV Global Enableフラグを有効にします。
次に、GRUBを編集してホストOSのカーネルパラメータを設定します。
sudo nano /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT変数にIOMMUパラメータを追加します。iommu=ptフラグを指定することで、パススルー対象外のデバイスのアドレス変換をスキップし、パフォーマンスを向上させることができます。
# Intel CPU向け
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on iommu=pt"
# AMD CPU向け
# GRUB_CMDLINE_LINUX_DEFAULT="quiet splash amd_iommu=on iommu=pt"
GRUBを更新し、サーバーを再起動します。
sudo update-grub
sudo reboot
再起動後、IOMMUが正しく動作しているか確認します。
dmesg | grep -E "DMAR|IOMMU"
ステップ2:物理ポートからVirtual Functions (VF)を作成
SR-IOVを有効化する物理ネットワークインターフェースを確認します(例:enp4s0f0)。
ip link show enp4s0f0
NICがサポートしている最大VF数を確認します。
cat /sys/class/net/enp4s0f0/device/sriov_totalvfs
このインターフェース上で4つのVirtual Functionを生成します。
echo 4 | sudo tee /sys/class/net/enp4s0f0/device/sriov_numvfs
PCIeレベルで作成された仮想NICの一覧を確認します。
lspci | grep -i ethernet
ステップ3:静的MACアドレスの割り当てとTrust Modeの有効化
ホスト側で固定MACアドレスを割り当てることで、再起動のたびにVFのMACアドレスがランダムに変わるのを防止できます。また、trust onを設定することで、VM内でのプロミスキャスモードの実行やDPDKのシームレスな統合が可能になります。
# VF 0に静的MACアドレスを割り当て、Trust modeを有効化
sudo ip link set enp4s0f0 vf 0 mac 52:54:00:11:22:33 trust on
# (オプション)トランク接続を使用している場合はVLANタグ100を設定
sudo ip link set enp4s0f0 vf 0 vlan 100
ステップ4:Libvirt経由でVFをKVM仮想マシンに割り当て
lspciを使用して、設定したVFのPCIバスアドレスを確認します(例:0000:04:10.0)。
lspci -s 04:10.0
対象仮想マシンのXML設定ファイルを開きます(例:db-prod-vm)。
virsh edit db-prod-vm
<devices>タグ内に以下の<interface type='hostdev'>ブロックを追加します。
<interface type='hostdev' managed='yes'>
<source>
<address type='pci' domain='0x0000' bus='0x04' slot='0x10' function='0x0'/>
</source>
<mac address='52:54:00:11:22:33'/>
</interface>
仮想マシンを起動し、コンソールに接続して確認します。
virsh start db-prod-vm
virsh console db-prod-vm
ゲストOS内でip aを実行すると、ベンダー公式ドライバ(Intel 700/800シリーズならiavf、X520/X540シリーズならixgbevfなど)が直接ロードされた新しいネットワークインターフェースが表示されます。
本番環境でSR-IOVを運用する際の3つの実践的ノウハウ
本番環境への導入において、帯域幅の上限に達していないにもかかわらず、ピーク時に断続的なパケットロス(intermittent packet loss)が発生することがあります。ここでは、最も頻出する3つの問題とその根本的な対処法を解説します。
- ゲストVM内のRing Bufferサイズを拡大する: デフォルトでは、VFドライバの送受信バッファは256〜512ディスクリプタ程度しか割り当てられていません。マイクロバースト(急激なトラフィックスパイク)が発生すると、NICはハードウェアキューの段階でパケットを破棄してしまいます。VM内のRX/TXリングバッファを最大値(通常は4096)まで引き上げてください。
# 現在のリングバッファ上限値を確認
sudo ethtool -g eth1
# バッファサイズを最大値に設定
sudo ethtool -G eth1 rx 4096 tx 4096
- VIP / Keepalived運用時はSpoof Checkingを無効化する: 仮想マシンでVirtual IP(Keepalived、VRRP、CARPなど)を使用する場合、物理NICのアンチスプーフィング機能により未知のMAC/IPアドレスと判定され、パケットが遮断されてしまいます。ホスト側で該当VFのなりすましチェック(spoofchk)を無効化してください。
sudo ip link set enp4s0f0 vf 0 spoofchk off
- 再起動時のVF自動生成を設定する: ホストを再起動すると、
sriov_numvfsの値は自動的に0にリセットされます。最も安定した対策は、oneshotタイプのsystemdサービスを作成してVF数を自動で再設定することです。
# サービスファイル /etc/systemd/system/sriov.service の作成
[Unit]
Description=ブート時にSR-IOV VFを自動初期化
After=network.target
[Service]
Type=oneshot
ExecStart=/bin/sh -c 'echo 4 > /sys/class/net/enp4s0f0/device/sriov_numvfs'
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
サービスを有効化して設定を永続化します。
sudo systemctl enable --now sriov.service

