このページの目次

ネットワークパフォーマンスとチューニング

アプリケーションバッファからTCP輻輳制御、NICオフロードまで——各層に調整可能なパラメータが存在します。BDPは帯域を最大限に活用するために必要なバッファサイズを決定し、BBRはモデルを用いてパケット損失検知に代わることで、バッファブローテッドなリンクにおいてCUBICよりも優れています。fq_codel/cakeはキューを能動的に管理し、バッファの過剰な埋め込みを防ぎます。

概要

ネット上で流通している「TCPチューニングの秘訣」の多くは、ユースケースを考慮せずに sysctl パラメータを羅列したものに過ぎません。効果的なチューニングには、たった一つの正道があります。⁠アプリケーションからネットワークケーブルに至るまで、データが通過するキューを特定し、ボトルネックがどの層にあるかを測定し、その層のノブ(パラメータ)を調整することです。 パケットの送信パス上の各ノードがボトルネックとなる可能性があり、それぞれに独自の調整方法があります。

flowchart LR
    APP["📦 アプリケーションバッファ<br><small>writeのブロックサイズ</small>"] --> SK["ソケットバッファ<br><small>tcp_wmem / tcp_rmem</small>"]
    SK --> CC["TCP輻輳制御<br><small>cubic / bbr</small>"]
    CC --> QD["qdiscキュー<br><small>fq_codel / cake</small>"]
    QD --> DRV["ドライバリングバッファ<br><small>ethtool -G</small>"]
    DRV --> NIC["NICオフロード<br><small>TSO / GSO / GRO</small>"]
    NIC --> NET(("🌐 ネットワーク<br><small>ここでのキューは制御不可<br>→ bufferbloat</small>"))
    classDef knob fill:#4493f81f,stroke:#4493f8,stroke-width:2px
    classDef wild fill:#f8514926,stroke:#f85149,stroke-width:2.5px
    class APP,SK,CC,QD,DRV,NIC knob
    class NET wild

3つの典型的な問題は、それぞれ異なる層に対応しています。⁠帯域が最大限に活用されない場合は、ソケットバッファがBDPより小さいか、輻輳アルゴリズムが不適切であることが多いです。⁠ロード時にレイテンシが急上昇するのはキューの問題(bufferbloat)です。⁠小包のスループット/CPU使用率の最大化は、割り込みとオフロードの問題です。以下、これら3つのカテゴリについて詳しく解説します。

帯域が最大限に活用されない: BDPとバッファサイズ

TCPがリンクの帯域を最大限に活用するためには、「伝送路上を飛行中」の未確認データがパイプ全体を満たす必要があります。パイプの容量は BDP(帯域幅 × RTT) であり、in-flight(送信中)の上限は min(cwnd, 受信ウィンドウ, 送信バッファ) によって制限されます。この3つの値のいずれかがBDPより小さい場合、帯域は最大限に活用されません。

リンクBDPLinuxデフォルトで十分か
1Gbps × 30ms(国内長距離)3.75 MB十分 (tcp_rmem max デフォルトは約6MB)
1Gbps × 150ms(越境)18.75 MB不十分
10Gbps × 200ms250 MBはるかに不十分
# トリプル: min / default / max —— 自動調整は min..max の間で伸縮する
sysctl net.ipv4.tcp_rmem="4096 131072 67108864"   # 受信側、上限64MB
sysctl net.ipv4.tcp_wmem="4096 16384 67108864"    # 送信側
sysctl net.core.rmem_max=67108864                  # SO_RCVBUF 手動設定の上限

検証方法: ss -ti で単一接続の cwndrttbytes_acked を確認する。cwnd がリンクの変動に合わせて変動するのではなく、バッファの上限に張り付いている場合、バッファがネックになっています。

注意⁠: バッファは大きいほど良いわけではありません。BDPの低いリンクでは、大きなバッファは単にメモリを無駄に消費するだけであり、自身もボトルネックとなるデバイスではキューイングレイテンシを悪化させることがあります。実際の最大BDPに合わせて設定し、2倍の余裕を持たせれば十分です。

ロード時にレイテンシが急上昇: Bufferbloatとキュー管理

古典的な症状:⁠アイドル時はpingが10msだが、ダウンロードを開始すると500msに上昇⁠。原因は帯域幅ではなくキューにあります。パス上の何らかのデバイス(多くの場合、家庭用ルーターやONU)が巨大なFIFOバッファを持っており、CUBICのようなパケット損失駆動型のアルゴリズムは、それが満たされるまでパケットを送り続けます。その結果、すべてのトラフィックがこの深いキューで待機することになります。

自分で制御可能な両端では、FIFOの代わりに AQM(アクティブキュー管理) を使用します。

tc qdisc replace dev eth0 root fq_codel              # 多くのディストリビューションでデフォルト
tc qdisc replace dev eth0 root cake bandwidth 950mbit # 家庭用エグジットに最適
  • fq_codel = 公平キュー + CoDel。CoDelの考え方は、パケットの滞留時間に注目することです。5msを継続的に超えると、パケットをドロップし始めます(キューの先頭からドロップし、シグナルが速く伝わる)。これにより送信側の速度低下を促し、キューが短くなればレイテンシは自然に低下します。公平キュー部分は、大きなフロー(エレファントフロー)が隣接するSSH接続を飢えさせないように保証します。
  • cake は fq_codel の実装上の改良版です:整形器を内蔵(bandwidth を実際のアップリンク速度よりわずかに低く設定し、キューをONUから自デバイスへ戻す)、ホストごとの分離(1台のデバイスでBTを起動しても家族全員に影響を与えない)、DiffServ対応。

送信側で bbr と組み合わせると最も効果的です。BBRは元々キューを満たしません(原理は TCP Deep Dive の輻輳制御セクションを参照)。

sysctl net.ipv4.tcp_congestion_control=bbr

一般的なTCPノブ

ノブ役割調整すべきタイミングリスク
tcp_congestion_control=bbrモデル駆動型輻輳制御長距離/太いリンク、パケット損失あり、bufferbloat なリンクCUBICフローとの共存時の公平性(v1)
tcp_fastopen=3SYNパケットにデータを含め、リターンユーザーはRTTを1回節約短時間接続が密集するAPIサービス中間ボックスがTFOパケットをドロップする可能性
tcp_tw_reuse=1クライアント側でTIME_WAIT状態を再利用高並列短時間接続のクライアントクライアント側でのみ安全。すでに削除された tw_recycle を触らないこと
somaxconn + tcp_max_syn_backlog完全/半接続キューの深さss -lnt で listen キューの溢れを確認した場合キューを深くするだけで、処理しきれない場合はスケーリングが必要
tcp_notsent_lowat=16384送信バッファ内の「まだ送信されていない」量を制限レイテンシが敏感なストリーミングアプリケーション極端なスループットがわずかに低下

原則:⁠変更するたびに、ベンチマークで比較する⁠。ノブ同士は相互作用するため、すべてを一度に変更するのは調整していないのと同じです。

小包とCPU: NICオフロード

10Gbps環境では、1秒間に百万パケットに達することもあり、パケットごとの割り込みでは、帯域幅が枯渇する前にCPUが先にパンクします。アプローチはバッチ処理⁠——パケットの断片化や結合をカーネルからNICへ移すことです。

オフロード方向動作
TSO送信カーネルが64KBの大きなブロックをNICに渡し、NICがハードウェアでMSSサイズに分割
GSO送信TSOのソフトウェアフォールバック(仮想NIC、TSO非対応ハードウェア)、ドライバの手前で最後の瞬間まで分割しない
GRO受信同一フローの連続した小包を結合して大きなブロックにし、プロトコルスタックに渡すことで、割り込みと走査を節約
ethtool -k eth0 | grep -v fixed     # 現在のオフロード状態を確認
ethtool -K eth0 tso off             # トラブルシューティング時に一時的に無効化
ethtool -G eth0 rx 4096 tx 4096     # ドライバリングバッファ、バーストトラフィックでパケットロスが発生した場合に拡大

tcpdumpの古典的な落とし穴⁠: 64KBの「巨大パケット」がキャプチャされていても、それは異常ではありません。TSO/GROが有効になっており、キャプチャポイントが分割前/結合後にあるためです。実際のオンライン通信ではMTUサイズで転送されます。逆に、オフロードが問題を引き起こしている(特に仮想環境でのチェックサムエラー)と疑われる場合は、まず ethtool -K ... off で排除します。

ボトルネックの特定順序

  1. ベースライン⁠: iperf3 -c <server> -R で双方向を測定し、純粋な帯域幅を取得——まずボトルネックがネットワークにあることを確認します。
  2. ロード下でのレイテンシ測定⁠: iperf3 を実行しながら ping を実行し、レイテンシが10倍に増加 → bufferbloat、キューを調整。
  3. 単一接続の確認⁠: ss -ti の cwnd/rtt/retrans を確認。cwnd が上がらない場合はバッファと輻輳アルゴリズムを確認、retrans が多い場合はリンク品質を確認。
  4. システム確認⁠: nstat -a | grep -i retransethtool -S eth0 | grep -i drop(NIC層でのパケットドロップ)、softnet_stat のバックプレッシャー。
  5. 1つの変数を変更し、ステップ1に戻ります。

体系的なパケットキャプチャ分析については tcpdump と Wireshark、トラブルシューティングのアプローチについては ネットワーク障害診断方法论 を参照してください。

参考文献

  • BBR: github.com/google/bbr · RFC 9438
  • fq_codel/CoDel: RFC 8290, RFC 8289 · bufferbloat.net
  • cake: man tc-cake · RFC 8290 の実装版
  • Brendan Gregg: 「Systems Performance」第10章 (Network)

Keywords: BDP, BBR, CUBIC, bufferbloat, fq_codel, cake, AQM, TCPチューニング, tcp_rmem, TSO, GSO, GRO, ethtool, iperf3