10Gbpsにおけるボトルネックの現実
高性能なSFP+モジュールをサーバーに差し込み、輝かしい10Gbpsのストリームを期待したのに、実際には4.2Gbpsで停滞しているのを見たことはありませんか?私はあります。他のシステムがアイドル状態である一方で、単一のCPUコアが100%に張り付いているのを見るのは、非常にフラストレーションの溜まる目覚まし体験です。Linuxにおける高速ネットワークは「プラグアンドプレイ」ではありません。データを運ぶ光ファイバーと同じくらい、OSも高速である必要があります。
標準的なLinuxディストリビューションは互換性を優先しています。それらは1GbpsのオフィスLANや家庭用接続に最適化された設定で出荷されています。10Gbps、25Gbps、あるいは100Gbpsへとスケールアップすると、これらのデフォルト設定は大きな足かせとなります。1,500バイトのパケットを10Gbpsの速度で処理すると、CPUは毎秒80万回以上の割り込みを処理せざるを得ません。これは「千切りにされる(death by a thousand cuts)」ような状況を作り出し、プロセッサは実際のデータを移動させることよりも、ヘッダーの管理に多くの時間を勝てることになります。
Cephストレージクラスター、高トラフィックのNVMe-over-Fabricsノード、または仮想化ホストを構築している場合、これらのチューニングはオプションではありません。理論上の最大値である9.41Gbpsを引き出すか、十分に活用できていないハードウェアに数千ドルを無駄にするかの分かれ目となります。
なぜデフォルト設定は高速通信で失敗するのか
設定を変更する前に、Linuxネットワークスタックにおける3つの主要な制限要因を理解する必要があります。
1. MTUとジャンボフレーム
MTU(Maximum Transmission Unit)は、回線上で許可される最大のパケットサイズを規定します。標準の1500バイトというMTUは、1980年代のイーサネットの名残です。これを9000バイト(ジャンボフレーム)に増やすことで、1つのパケットに6倍のデータを詰め込むことができます。この単純な変更により、同じ量のデータを移動させるために必要なCPU割り込みの数が激減します。
2. TCPウィンドウ制御(Window Scaling)
TCPは、送信側が確認応答(ACK)を必要とする前に、どれだけのデータを送信状態で保持できるかを決定するために「ウィンドウ」を使用します。高遅延または高帯域幅のリンクでは、ウィンドウが小さいと送信側が一時停止して待機してしまいます。これにより、帯域幅は利用可能なのにプロトコルがそれを使用することを拒否する「LFN(Long Fat Network)」問題が発生します。
3. リングバッファと割り込み
ネットワークインターフェースカード(NIC)には、リングバッファと呼ばれる内部キューがあります。これらのバッファが小さすぎると、CPUがパケットを確認する前にNICがパケットをドロップしてしまいます。さらに、NICがCPUに通知する方法(割り込み合算:Interrupt Coalescing)を調整することで、マイクロ秒単位のレイテンシよりも生のスループットを優先させることができます。
ステップ1:ジャンボフレーム(MTU 9000)の実装
この変更は最大のパフォーマンス向上をもたらしますが、注意点があります。データパス内のすべてのデバイスがジャンボフレームをサポートしている必要があります。 これには、送信元、送信先、およびその間にあるすべてのスイッチが含まります。途中に1500に制限されたレガシーなスイッチが1つでもあると、パケットはドロップされるか断片化され、パフォーマンスが壊滅的になります。
eth0 のようなインターフェースで一時的にジャンボフレームをテストするには:
sudo ip link set dev eth0 mtu 9000
変更をすぐに確認するには:
ip link show eth0
UbuntuまたはDebianで恒久的に変更するには、/etc/netplan/ にあるNetplanのYAMLファイルを更新します:
network:
version: 2
ethernets:
eth0:
mtu: 9000
addresses:
- 10.0.0.10/24
sudo netplan apply を実行して、新しいMTUを適用します。
ステップ2:カーネルネットワークスタックのチューニング (sysctl)
Linuxカーネルは、10Gbpsストリームの膨大なデータ量を処理するために、より大きなメモリバッファを必要とします。バースト時にシステムが窒息しないよう、これらを拡張する必要があります。
/etc/sysctl.conf を開き、以下のパラメータを追加します。これらの値、特に32MBの最大値は、本番環境で10Gリンクを安定させるために実証済みの設定です:
# 最大バッファサイズを32MBに増やす
net.core.rmem_max = 33554432
net.core.wmem_max = 33554432
# TCPバッファサイズ: [最小, デフォルト, 最大] (バイト単位)
net.ipv4.tcp_rmem = 4096 87380 33554432
net.ipv4.tcp_wmem = 4096 65536 33554432
# プロセッサ入力キューの長さを増やす
net.core.netdev_max_backlog = 10000
# TCPウィンドウ制御 (RFC 1323) が有効であることを確認
net.ipv4.tcp_window_scaling = 1
# 同時接続数の制限を引き上げる
net.core.somaxconn = 4096
再起動せずに変更を有効にします:
sudo sysctl -p
ステップ3:ethtoolによるNICハードウェアの最適化
ハードウェアレベルのボトルネックは、NIC自体の設定に隠れていることがよくあります。ethtool ユーティリティを使用して、ハードウェアがソフトウェアの足を引っ張っていないか確認してください。
リングバッファの増量
現在のハードウェア容量を確認します:
ethtool -g eth0
「Current hardware settings」が「Pre-set maximums」よりも大幅に低い場合は、値を引き上げます。多くのIntelやMellanoxカードでは、512から4096に変更することを意味します:
sudo ethtool -G eth0 rx 4096 tx 4096
割り込み合算(Interrupt Coalescing)
大きなファイル転送で最大のスループットを目指す場合は、CPUに割り込みをかける前に30マイクロ秒待つようにNICに指示します。これにより、パケットを効率的にバッチ処理できるようになります:
sudo ethtool -C eth0 rx-usecs 30
ステップ4:検証とテスト
コマンドがエラーを返さなかったからといって、設定が機能したと思い込まないでください。ping に「断片化禁止」(DF)フラグを立てて、ジャンボフレームがエンドツーエンドで機能しているか確認します:
ping -M do -s 8972 10.0.0.11
注:ペイロードには28バイトのICMPおよびIPヘッダーを含める必要があるため、8972を使用します(9000 – 28 = 8972)。pingが返ってくれば、経路は正常です。
最後に、iperf3 を使用して実際の通信テストを実行します。MTUを9000にし、カーネルをチューニングした状態では、ベースラインテストよりも大幅に低いCPU使用率で、9.4Gbps付近の結果が得られるはずです。
最後に
10Gbpsネットワークの最適化は、単一の魔法のような設定ではありません。カーネルメモリから物理スイッチに至るまで、パス全体がデータに対して十分に広いことを確認することです。もしスイッチを制御できない場合は、ジャンボフレームは諦めて、sysctl のチューニングとNICリングバッファに専念してください。最終的に9.4Gbpsの持続的な転送レートを達成できれば、その努力はシステムの安定性とパフォーマンスという形で見返りをもたらします。

