LinuxにおけるSR-IOV設定ガイド:KVM仮想マシンでベアメタル並みのネットワーク性能を実現する

Network tutorial - IT technology blog
Network tutorial - IT technology blog

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つの問題とその根本的な対処法を解説します。

  1. ゲストVM内のRing Bufferサイズを拡大する: デフォルトでは、VFドライバの送受信バッファは256〜512ディスクリプタ程度しか割り当てられていません。マイクロバースト(急激なトラフィックスパイク)が発生すると、NICはハードウェアキューの段階でパケットを破棄してしまいます。VM内のRX/TXリングバッファを最大値(通常は4096)まで引き上げてください。
# 現在のリングバッファ上限値を確認
sudo ethtool -g eth1

# バッファサイズを最大値に設定
sudo ethtool -G eth1 rx 4096 tx 4096
  1. VIP / Keepalived運用時はSpoof Checkingを無効化する: 仮想マシンでVirtual IP(Keepalived、VRRP、CARPなど)を使用する場合、物理NICのアンチスプーフィング機能により未知のMAC/IPアドレスと判定され、パケットが遮断されてしまいます。ホスト側で該当VFのなりすましチェック(spoofchk)を無効化してください。
sudo ip link set enp4s0f0 vf 0 spoofchk off
  1. 再起動時の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
Share: