TCP/IP sysctlパラメータのチューニングによるLinuxネットワークスループットの最適化
スループット、バッファ、輻輳制御、安全なテストのための実践的なLinux TCP sysctlチューニング。
TCP/IP sysctlパラメータのチューニングによるLinuxネットワークスループットの最適化
Linuxネットワークスループットのチューニングは、「実際に何が遅いのか」という地味な問いから始まります。10 Gbpsリンクで300 Mbpsが上限のサーバーは、TCPウィンドウ問題、ディスク問題、CPU割り込み問題、仮想NIC設定の不良、パケットロス問題、またはアプリケーションがデータを細かく分割して送信している可能性があります。sysctlのチューニングは、その一部にしか効果がありません。
そのため、私はTCP/IP sysctlの変更を、魔法のパフォーマンスレシピではなく、管理された実験として扱います。ベースラインを測定し、小さな設定グループを1つ変更し、再度テストし、メモを取ります。インターネットから巨大なチューニングブロックをコピーして/etc/sysctl.confに貼り付けると、あるワークロードは改善しても、別のワークロードに静かに悪影響を及ぼす可能性があります。
以下の設定は、高スループットサービス(アーティファクトリポジトリ、バックアップサーバー、オブジェクトストレージゲートウェイ、ビジーなリバースプロキシ、大規模ログを転送するデータベースレプリカ、長距離リンクでトラフィックを移動するLinuxホスト)を実行する場合に役立ちます。ボトルネックがTLS暗号化CPU、低速ストレージ、アプリケーションのロック、クラウドプロバイダーの制限、またはホスト外部のパケットロスである場合、効果は薄いでしょう。
変更を加える前に、クイックベースラインを収集します:
ip -s link
ss -s
nstat -az | egrep 'TcpRetransSegs|TcpExtTCPLoss|TcpExtTCPTimeouts|TcpExtListenOverflows'
sar -n DEV,TCP,ETCP 1 10
iperf3 -c test-host -P 4 -t 30
再送信が増加している場合は、バッファを増やす前にロスを修正します。top、mpstat、perfでCPUがすでに限界に達している場合、sysctlは症状を隠すかもしれませんが、ボトルネックを取り除くわけではありません。iperf3は高速でもアプリが遅い場合は、カーネルをチューニングする前にアプリケーションパスを調査します。
sysctlがネットワークチューニングにどのように適合するか
sysctlはシステム稼働中にカーネルパラメータを公開します。ネットワーク設定は通常、net.ipv4、net.ipv6、net.core以下にあります。値は次のように読み取れます:
sysctl net.ipv4.tcp_congestion_control
sysctl net.ipv4.tcp_rmem
sysctl net.core.rmem_max
一時的な変更は次のようにテストできます:
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
一時的な変更は再起動後に消えます。永続的な変更は、/etc/sysctl.d/90-network-throughput.confのような専用ファイルに、説明を付けて保存します。/etc/sysctl.confに無造作に散らばらせるのは避けます。
sudo install -m 0644 /dev/null /etc/sysctl.d/90-network-throughput.conf
sudo editor /etc/sysctl.d/90-network-throughput.conf
sudo sysctl --system
別ファイルを使用する理由は、ロールバックが簡単だからです:ファイルを移動してsudo sysctl --systemを再実行するだけです。これは、本番トラフィック下で設定が悪影響を及ぼした場合に重要です。
TCPバッファ:長時間接続に余裕を与える
最初に注目されるのはバッファサイズです。TCPは、確認応答がネットワークを通過する間にデータを転送中に保つために、十分な送受信ウィンドウスペースを必要とします。有用な考え方は帯域幅遅延積です:高帯域幅・高遅延の接続は、低遅延のLAN接続よりも多くの未処理データを必要とします。
例えば、1 msのデータセンターパスでの1 Gbps転送は、70 msのWANパスでの1 Gbps転送よりもはるかに少ない転送中データで済みます。受信ウィンドウが小さすぎると、リンクに余裕があっても送信側は一時停止します。
LinuxはTCPメモリチューニングに3つの値の配列を使用します:
net.ipv4.tcp_rmem = 4096 131072 33554432
net.ipv4.tcp_wmem = 4096 131072 33554432
3つの数値は、ソケットあたりのバッファサイズの最小値、デフォルト値、最大値(バイト単位)です。正確な値は、ワークロード、メモリ予算、カーネルの動作に合わせる必要があります。上記の例では最大値を32 MiBに引き上げており、これは無謀にならずにビジーなサーバーには多くの場合十分です。一部の長距離またはストレージ重視のシステムではより大きな値を使用しますが、実際のトラフィックでテストする必要があります。
net.coreの制限はソケットバッファに上限を設けます:
net.core.rmem_max = 33554432
net.core.wmem_max = 33554432
tcp_rmemがTCPは32 MiBまで拡張可能としていても、net.core.rmem_maxがそれよりはるかに低い場合、実際には低い方の上限が適用されます。特別な理由がない限り、上限をTCPの最大値に合わせておきます。
多数の同時接続があるマシンでバッファを盲目的に増やさないでください。少数の大規模フローを持つファイルサーバーは、フローあたりのバッファを大きくしても問題ありません。数十万の接続を処理するプロキシは、すべてのソケットが巨大なバッファを使用可能にすると、すぐにメモリを消費します。
自動チューニングはすでに機能している
最新のLinuxカーネルはすでにTCPバッファを自動チューニングしています。つまり、通常はアプリケーションで巨大な固定ソケットバッファを設定する必要はありません。カーネルは、接続がより多くのスペースを必要とする場合にバッファを拡張します。
あなたの役割は、主に上限が低すぎないことを確認することです。長距離ネットワークでスループットが低く、ss -tinが小さな受信ウィンドウや受信側によってブロックされている送信側を示している場合、tcp_rmem、tcp_wmem、rmem_max、wmem_maxを引き上げると効果があります。
アクティブな接続を確認するには:
ss -tin dst <ピアIP>
cwnd、rtt、rto、bytes_acked、bytes_received、再送信カウンターなどのフィールドを探します。これらは、単一の速度テストよりも多くの情報を提供します。
輻輳制御:CUBIC、BBR、そして現実
輻輳制御アルゴリズムは、TCPが送信レートをどのように増減させるかを決定します。多くのLinuxシステムでは、CUBICがデフォルトであり、一般的なインターネットおよびデータセンタートラフィックでうまく機能します。BBRは、パケットロスのみに反応するのではなく、ボトルネック帯域幅とラウンドトリップ時間をモデル化するため、ロスの多い経路や長距離経路でスループットとレイテンシを改善できます。
利用可能なアルゴリズムを確認:
sysctl net.ipv4.tcp_available_congestion_control
sysctl net.ipv4.tcp_congestion_control
カーネルで利用可能な場合のみBBRを有効にします:
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
永続化する場合:
net.ipv4.tcp_congestion_control = bbr
一部のシステムでは、BBRの動作を良好にするために公平キューイングパケットスケジューラも必要です:
net.core.default_qdisc = fq
BBRが常に高速であるとは限りません。他のフローとの公平性を変える可能性があり、BBRのバージョンによってカーネル間で動作が異なります。関心のあるトラフィックパターン(多数の小さなAPI呼び出し、少数のバルク転送、レプリケーションデータベーストラフィック、または本番環境に近い混合負荷)でテストしてください。
リスンキュー:ドロップを謎のままにしない
スループットの問題は、トラフィックスパイク時の接続失敗として現れることがあります。サービスが新しいTCP接続を受け入れる速度がクライアントの作成速度より遅い場合、カーネルキューが満杯になります。
関連設定:
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 8192
somaxconnは、アプリケーションがlisten(2)で要求する完了した接続バックログの上限です。tcp_max_syn_backlogはハーフオープンSYNキューの容量に影響します。これらを引き上げると、ビジーなWebサーバー、プロキシ、ロードバランサーに役立ちますが、アプリケーションも十分な大きさのバックログを要求する必要があります。Nginx、HAProxy、Envoy、アプリケーションサーバーには、独自のバックログ設定があることがよくあります。
オーバーフローを監視:
nstat -az | egrep 'ListenOverflows|ListenDrops|Syncookies'
ss -ltn
ListenOverflowsが増加している場合、カーネルキューが追いついていません。CPUが飽和しているか、アプリがダウンストリームサービスでブロックされている場合、キューサイズを増やすとクライアントエラーが一時的に減少するかもしれませんが、サービスは修正されません。
バックログとパケット処理
net.core.netdev_max_backlogは、カーネルがパケットを処理できる速度よりも速く受信した場合に、入力キューで待機できるパケット数を制御します。
net.core.netdev_max_backlog = 250000
これは、特に仮想化ネットワークにおいて、高速インターフェースでのバースト時に役立ちます。ただし、ホストを大きなパケット待機室に変えると、レイテンシが増加する可能性があります。まずインターフェースのドロップを確認します:
ip -s link show dev eth0
ethtool -S eth0 | egrep 'drop|err|timeout|miss|fifo'
ドライバーレベルのドロップが増加している場合は、NICリングサイズ、割り込み分散、RSSキュー、CPUアフィニティも調査します。これらはsysctlの範囲外ですが、10 Gbps以上のホストではTCPバッファよりも重要なことがよくあります。
TIME_WAITとポート枯渇
高スループットのクライアント、プロキシ、ジョブランナーは、一時的なポートを使い果たしたり、多くのソケットがTIME_WAIT状態で蓄積されることがあります。古いチューニングアドバイスは有害になる可能性があるため、注意が必要です。
現在の範囲を確認:
sysctl net.ipv4.ip_local_port_range
ss -tan state time-wait | wc -l
クライアント側の合理的な調整は、一時的なポート範囲を広げることです:
net.ipv4.ip_local_port_range = 10240 60999
tcp_tw_recycleを推奨する古いアドバイスは避けてください。これは、特にNATの背後で正当なトラフィックを壊すため、Linuxから削除されました。tcp_tw_reuseは多くのカーネルに存在しますが、その動作は時間とともに変化しています。デフォルトのスループット設定として有効にしないでください。必要だと思われる場合は、正確なカーネルとトラフィックパターンで注意深くテストしてください。
サーバーでは、TIME_WAITソケットの山は多くの場合正常です。クライアントでは、ポート枯渇は通常、コネクションプーリング、キープアライブ、HTTP/2、短命なアウトバウンド接続の削減、またはより多くの送信元IPが必要であることを意味します。
控えめな初期設定ファイル
以下は、高スループットサーバー向けの実用的な出発点です。意図的に極端ではありません:
# /etc/sysctl.d/90-network-throughput.conf
# 高帯域幅または高遅延パス向けのTCP自動チューニング上限を拡大
net.core.rmem_max = 33554432
net.core.wmem_max = 33554432
net.ipv4.tcp_rmem = 4096 131072 33554432
net.ipv4.tcp_wmem = 4096 131072 33554432
# バースト的なインバウンドトラフィック向けのキューを拡大
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 8192
net.core.netdev_max_backlog = 250000
# オプション:グローバルに有効にする前にテスト
# net.core.default_qdisc = fq
# net.ipv4.tcp_congestion_control = bbr
適用:
sudo sysctl --system
その後、再度測定します。同じテストサイズ、時間枠、並列ストリーム数、ネットワークパスを使用します。5つの変数を変更した前後テストは証拠になりません。
よくある間違い
最初の間違いは、ロスの多いパスでチューニングすることです。TCPはロスを輻輳と見なします。より大きなバッファはスループットを少し向上させるかもしれませんが、レイテンシを増加させ、本当の問題を隠す可能性もあります。まず、不良ケーブル、過負荷の仮想スイッチ、パケットポリシング、MTU不一致、不安定なVPNパスを修正します。
2番目の間違いは、iperf3 -P 8がアプリケーションパフォーマンスを証明すると仮定することです。並列ストリームは、単一の実際のアプリケーション接続ができない場合でもリンクを満たすことができます。これは有用な情報ですが、全体像ではありません。
3番目の間違いは、共有ホストに巨大なバッファを設定することです。カーネルが必要に応じてバッファを拡張する場合、より大きな上限は問題ありませんが、メモリプレッシャーはすべてを変えます。変更後は、free、slabtop、TCPメモリ、アプリケーションメモリを監視します。
4番目の間違いは、ロールバックを忘れることです。以前の値を変更チケットやランブックに保存しておきます:
sysctl -a | egrep 'net.core.rmem_max|net.core.wmem_max|net.ipv4.tcp_rmem|net.ipv4.tcp_wmem|net.ipv4.tcp_congestion_control'
sysctlが解決策ではない場合
1つのCPUコアがビジーで他がアイドル状態の場合、割り込み処理、RSS、RPS/XPS、アプリケーションスレッド化を調査します。ディスク待機時間が長い場合、ネットワークがストレージを待っている可能性があります。TLSがCPUを消費している場合、暗号化ありとなしでテストし、ハードウェア、暗号スイート、接続再利用を検討します。Kubernetesやクラウドロードバランサーがパスにある場合、サービスレベルの制限とconntrackテーブルを確認します。
NATが多いホストでは、conntrackも調査します:
sysctl net.netfilter.nf_conntrack_count
sysctl net.netfilter.nf_conntrack_max
これはTCPスループットチューニングではありませんが、conntrackの枯渇はランダムなネットワークの遅延や接続断のように見えることがあります。
自己欺瞞なくテストする
iperf3をネットワークツールとして使用し、アプリケーションが修正された証拠として使用しないでください。単一ストリームテストは、1つのTCP接続が何ができるかを示すため有用です:
iperf3 -c test-host -t 30
並列テストは、リンクが複数のフローで満たされるかどうかを示します:
iperf3 -c test-host -P 8 -t 30
並列ストリームが高速でも単一ストリームが遅い場合、輻輳制御、TCPウィンドウ成長、RTT、パケットロスを調査します。両方が遅い場合、より低いレイヤー(インターフェースエラー、クラウド帯域幅制限、CPU飽和、MTU、ファイアウォール検査、送受信側のストレージ)を調査します。
テストパスを現実的に保ちます。同じラック内の2台のホストをテストしても、リージョンをまたぐバックアップジョブについてはあまりわかりません。小さなファイルでテストしても、定常状態のスループットは明らかになりません。VPN経由のテストでは、Linux TCPよりもVPNアプライアンスを測定している可能性があります。
各変更後、同じカウンターを取得します:
nstat -az > /tmp/nstat-after.txt
ss -s
sar -n DEV,TCP,ETCP 1 10
有用な結果は「数値が上がった」だけではありません。再送信が減少したか、キューがオーバーフローしなくなったか、CPUが妥当な範囲に収まったか、小さなリクエストのレイテンシが悪化しなかったかを知る必要があります。
優れたLinuxネットワークチューニングは、測定可能で元に戻せます。パスがより大きなウィンドウを必要とする場合にTCPバッファ上限を引き上げます。1つのアルゴリズムがすべての場面で勝つと仮定せず、輻輳制御をテストします。キュードロップが見られたらリスンキューを増やし、アプリケーションが十分な速度で受け入れられない場合は修正します。sysctlは有用ですが、より大きなシステムの1つの層にすぎません。