現実的な課題:高性能なカードを使っているのに、なぜ仮想マシンのネットワークは遅いのか?
10Gbpsのカードを搭載したサーバーで、仮想マシン(VM)の速度が3〜4Gbpsしか出ず、一方でホストCPUの使用率が40〜50%まで跳ね上がっていることに疑問を感じたことはありませんか? KVMを使い始めたばかりの多くの人は、使いやすさからLinux Bridge (br0)を選択します。この方法は軽量なWebサーバーには適していますが、大規模なデータベース、VoIP、ビデオストリーミングなどを実行する場合には致命的な問題となります。
以前、動画トランスコーディングを実行するVMクラスターを扱った際、トラフィックが5Gbpsに達するとパケットドロップが頻発しました。物理ネットワークカードの帯域には余裕があるにもかかわらず、ホストマシンのCPUがソフトウェアスイッチングの処理で過負荷になっていたのです。それはまるで、スーパーカーに乗っているのに、手動の料金所が1レーンしかない渋滞に巻き込まれているような感覚でした。
ボトルネック:ソフトウェアがハードウェアに追いつかない時
主な理由は、Linuxカーネルのネットワークスタックにあります。Bridgeを使用すると、VMに入るすべてのパケットがルーティングのためにホストCPUを経由する必要があります。CPUが余計な仕事をすることになり、貴重な処理サイクルを消費してしまいます。
たとえvirtioドライバーを使用していたとしても、パケットはホストメモリと仮想マシンの間でコピーされる必要があります。その結果、以下のような問題が生じます。
- 遅延(レイテンシ)の増大:カーネルを通過するステップごとに約50〜100マイクロ秒が加算されます。
- CPUの過負荷:高負荷時に毎秒数百万もの割り込み(interrupts)を処理する必要があります。
一般的なネットワークソリューションの比較
この課題を解決するために、通常は3つの選択肢があります。
- Linux Bridge / Macvtap:柔軟ですが、パフォーマンスは最も低いです。検証環境や軽量なWebサーバーに適しています。
- PCI Passthrough:物理カードを直接VMに割り当てます。速度は非常に速いですが、非常に非効率です。1枚のカードを1つのVMでしか使用できません。
- SR-IOV (Single Root I/O Virtualization):これこそが「強力な武器」です。PCI Passthrough並みの速度を実現しながら、1枚のネットワークカードを数十台の仮想マシンで共有できます。
SR-IOVはどのように動作するのか?
SR-IOVを使用すると、1つのPCIeデバイス(Intel X520やX710などのカード)を複数の独立した仮想デバイスに「分身」させることができます。具体的には以下の通りです。
- Physical Function (PF):実際の物理ネットワークカードです。
- Virtual Function (VF):PFから生成される仮想カードです。各VFは独自のMACアドレスを持ち、仮想マシンからは本物の物理カードとして認識されます。
SR-IOVを使用すると、データはDMA(Direct Memory Access)を通じてネットワークカードから仮想マシンのRAMに直接送られます。このとき、ホストCPUの負荷はほぼゼロになります。実際、10Gbpsでデータ転送を行った際、CPU負荷が25%からわずか2%に低下するのをテストで確認しました。
LinuxでのSR-IOV設定手順
SR-IOVをサポートするネットワークカード(ほとんどのIntel ServerシリーズやMellanox製)と、ハードウェア仮想化が有効になったマザーボードが必要です。
ステップ1:IOMMUの有効化
まず、BIOS/UEFIでIntel VT-d(またはAMD-Vi)を有効にします。次に、LinuxがIOMMUを認識できるようにGRUB設定ファイルを編集します。
sudo nano /etc/default/grub
以下のパラメータをGRUB_CMDLINE_LINUX_DEFAULT行に追加します:
# Intelを使用する場合:
GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt"
# AMDを使用する場合:
GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on iommu=pt"
システムを更新して再起動します:
sudo update-grub && sudo reboot
ステップ2:ネットワークカードの機能確認
ip link showコマンドで物理ネットワークインターフェース(PF)の名前を確認します。ここではカード名をenp1s0f0と仮定します。作成可能な仮想カード(VF)の最大数を確認します:
cat /sys/class/net/enp1s0f0/device/sriov_totalvfs
結果が32や63であれば、次のステップに進む準備ができています。
ステップ3:Virtual Function (VF) の作成
以下のコマンドで4つの仮想ネットワークカードを作成してみます:
echo 4 | sudo tee /sys/class/net/enp1s0f0/device/sriov_numvfs
lspci | grep Ethernetコマンドで確認します。「Virtual Function」という行が表示されるはずです。再起動後もこの設定を維持するには、このコマンドを/etc/rc.localに追加するか、Netplan経由で設定します。
ステップ4:KVM仮想マシンへの仮想カードの割り当て
virsh nodedev-list | grep pciコマンドでVFのPCIアドレスを特定します。その後、仮想マシンの設定ファイルを編集します:
virsh edit 仮想マシン名
<devices>タグの中に以下のコードを追加します(VF의 バス、スロットアドレスを正しく書き換えてください):
<interface type='hostdev' managed='yes'>
<source>
<address type='pci' domain='0x0000' bus='0x01' slot='0x10' function='0x0'/>
</source>
</interface>
仮想マシンを起動して確認します。内部に本物のIntelネットワークカードが認識され、低遅延で高速なトラフィックを処理できる状態になっているはずです。
終わりに
SR-IOVの設定はLinux Bridgeよりも手間がかかりますが、それに見合うだけの十分な効果があります。ホストCPUを他の処理のために解放し、仮想マシンのネットワークを物理マシンのようにスムーズに動作させることができます。重要なサービスを運用している場合は、ぜひ今日からSR-IOVの導入を検討してみてください。システムパフォーマンスの明らかな違いを実感できるはずです。

