LinuxでTCP BBRを有効化・チューニングして長距離転送を高速化する方法

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

課題:国境を越えるネットワーク通信が低速化する原因

フランクフルトとシンガポール間で数十ギガバイト規模のPostgresバックアップを同期した経験はないでしょうか。双方の拠点で1 Gbpsのポート帯域を確保しているにもかかわらず、太平洋や大西洋をまたぐ転送速度がわずか30 Mbps程度まで落ち込むことがあります。CPUには余裕があり、帯域も余っているのに、転送完了まで何時間もかかってしまいます。

この原因はハードウェアの故障やプロバイダのSLA違反ではなく、従来のTCP輻輳制御アルゴリズムにおける旧来の設計計算にあります。Linuxでは長年、デフォルトでCUBIC(その前はReno)が採用されてきました。これらのアルゴリズムは「パケットロス基準(loss-based)」で動作します。つまり、パケットの損失が発生すると、経路上のルーターが通信過多で輻輳していると判断してしまいます。そのため、パケットロスが1つでも起きると、CUBICは輻輳ウィンドウ(cwnd)を30%〜50%も急激に縮小し、再び時間をかけて少しずつウィンドウを広げようとします。

この前提はローカルデータセンター内であれば問題ありません。しかし、RTT(ラウンドトリップタイム)が180 msに達するような大西洋横断の光ファイバー回線などでは、スイッチが過負荷になっていなくても、光信号のノイズや一時的なインターフェースのゆらぎによって日常的にパケットロスが発生します。

高レイテンシ環境でわずか1%のパケットロスが発生しただけでも、CUBICはウィンドウ縮小からの回復動作に終始することになります。さらに厄介なことに、実際に経路上でバッファが溢れそうになった場合でも、CUBICはキューが満杯になるまでパケットを詰め込んでからようやく抑制を開始します。これにより数百ミリ秒規模のジッターが生じます。これがいわゆる「Bufferbloat(バッファブロート)」と呼ばれる慢性的な問題です。

基本概念:TCP BBRがボトルネックを解消する仕組み

Googleはこの悪循環を打破するためにBBR (Bottleneck Bandwidth and Round-trip propagation time)を開発しました。BBRはパケットロスを過剰に警戒するのではなく、ネットワーク経路の物理的な限界値を継続的に測定・モデル化します。

1. モデル基準(Model-Based)とパケットロス基準(Loss-Based)の輻輳制御

BBRは以下の2つの重要な物理的指標を継続的に測定します。

  • 最大帯域幅(BtlBw): 経路上で最も低速な物理リンクにおける最大のデータ転送レート。
  • 最小RTT(RTprop): 途中のキューが完全に空の状態で測定された、光ファイバー内を光が伝播する純粋な物理遅延時間。

BBRはBtlBw × RTprop、すなわちBDP(帯域幅遅延積:Bandwidth-Delay Product)に合わせてパケットの送信ペース(Pacing)を制御するため、経路上のバッファを圧迫することなくパイプの容量に合わせた最適なパケット量を送信します。海底ケーブルで突発的に1%のパケットロスが発生しても、BBRはそれを単なるノイズとして無視します。回線自体の実容量には余裕があると把握しているため、最高転送速度を維持できるのです。

2. Fair Queuing(fq)の役割

BBRが効果を発揮するには、高精度なパケットペーシングが不可欠です。40個のパケットを一気にバースト送信してしまうと、スイッチのバッファが瞬時に溢れてしまいます。これを防ぐため、LinuxではBBRとfq(Fair Queuing)キューイング規則(qdisc)を組み合わせて利用します。fqスケジューラがマイクロ秒単位の間隔でパケットを均等に分散送出することで、ローカルのNIC(ネットワークインターフェースカード)直下でのバーストによるパケットドロップを防ぎます。

実践:LinuxでのBBRの有効化とチューニング

BBRの導入は5分もかからず完了し、リモートバックアップやリージョンをまたぐデータベースレプリケーション、エッジCDNの通信速度を即座に引き上げます。以下では、カーネルの設定、ペーシングの確認、そして高遅延リンクに対応するためのソケットバッファの拡張手順を解説します。

ステップ1:カーネルバージョンの確認

BBR v1はLinuxカーネル 4.9で導入されました。Ubuntu 22.04/24.04、Debian 12、Rocky Linux 9などの最新ディストリビューションであれば、標準で5.15や6.x以降のカーネルが稼働しています。まず、現在実行中のバージョンを確認します。

uname -r

バージョン4.9.0以上が必要です。CentOS 7などの古いエンタープライズ向けリリースを使用している場合は、作業を進める前に最新のLTSカーネルへアップグレードしてください。

ステップ2:現在のTCP設定の確認

現在有効になっている輻輳制御アルゴリズムと、カーネルメモリに読み込まれている利用可能なアルゴリズムを確認します。

# 現在有効なアルゴリズムを確認
sysctl net.ipv4.tcp_congestion_control

# メモリに読み込まれている利用可能なアルゴリズムを一覧表示
sysctl net.ipv4.tcp_available_congestion_control

デフォルト状態のサーバーでは、通常cubicおよびrenoが出力されます。

ステップ3:BBRカーネルモジュールのロード

多くのディストリビューションでは、BBRをカーネルイメージに直接組み込まず、ロード可能なモジュールとしてビルドしています。実行中のセッションにモジュールをロードします。

sudo modprobe tcp_bbr

# モジュールが有効であることを確認
lsmod | grep bbr

システム再起動後もモジュールが自動的にロードされるよう、モジュールリストに永続化設定を追加します。

echo "tcp_bbr" | sudo tee -a /etc/modules-load.d/bbr.conf

ステップ4:BBRとFair Queuingの永続的な有効化

独自のネットワーク設定を/etc/sysctl.confに直接追記することは推奨されません。システムのアップグレード時に上書きされるリスクがあるためです。代わりに、/etc/sysctl.d/配下に設定ファイルを分割して配置します。

/etc/sysctl.d/99-tcp-bbr.confに設定を書き込みます。

sudo tee /etc/sysctl.d/99-tcp-bbr.conf << 'EOF'
# BBRに必要なパケットスケジューラ「Fair Queuing」を設定
net.core.default_qdisc = fq

# デフォルトの輻輳制御アルゴリズムをBBRに切り替え
net.ipv4.tcp_congestion_control = bbr
EOF

サーバーを再起動することなく、新しい設定を即座に適用します。

sudo sysctl --system

ステップ5:有効な設定の検証

カーネルに変更が正しく適用されたことを確認します。

sysctl net.ipv4.tcp_congestion_control
sysctl net.core.default_qdisc

両方のパラメータで切り替えが確認できるはずです。

net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq

通信負荷がかかっているアクティブなソケットの状態を確認するには、ssコマンドでリアルタイムのソケット統計を確認します。

ss -tin

出力結果にbbrと表示され、pacing_rateやdelivery_rate、rttのメトリクスがリアルタイムに表示されていることが確認できます。

ステップ6:高BDP回線に向けたTCPメモリウィンドウのチューニング

BBRを有効化するだけでは対策として不十分です。TCPのメモリバッファが狭いままでは、回線の物理帯域に達する前にメモリの上限に達してしまいます。バッファサイズは通信経路の帯域幅遅延積(BDP: 帯域幅 × RTT)に見合った値を設定する必要があります。

例えば、米国東部と東京を結ぶRTT 160 ms、1 Gbpsの回線を考えてみましょう。そのBDPは以下のようになります。

BDP = (1,000,000,000 bits/sec / 8) * 0.160 sec ≈ 20 Megabytes

Linuxのデフォルト設定では、ソケットの最大メモリバッファが4 MBや6 MB程度に制限されています。この制限があると、BBRがどれほど効率的にパケットを送信しようとしても、1 Gbpsの太平洋横断回線では最大200〜300 Mbps程度に頭打ちになってしまいます。チューニングしたバッファ設定をsysctl設定ファイルに追記しましょう。

sudo tee -a /etc/sysctl.d/99-tcp-bbr.conf << 'EOF'
# OSソケットの送受信バッファの最大値(64 MB)
net.core.rmem_max = 67108864
net.core.wmem_max = 67108864

# TCPバッファ自動調整の制限値:最小値、デフォルト値、最大値(バイト)
# デフォルト値は一般的な低RTT回線をカバーし、最大値で高BDP回線の帯域制限を解放
net.ipv4.tcp_rmem = 4096 131072 67108864
net.ipv4.tcp_wmem = 4096 65536 67108864

# TCPウィンドウイスケーリングを有効化(RFC 7323)
net.ipv4.tcp_window_scaling = 1
EOF

設定を再読み込みします。

sudo sysctl --system

まとめ

CUBICからBBRへの移行は、サーバーにおける長距離パケット配送の挙動を根本から改善します。突発的なパケットロスに過剰反応する代わりに、実際の回線容量を算出し、fqによってパケットをスムーズに送出することで、キュー遅延を肥大化させることなく理論上の最大帯域を維持できます。

ただし、最大ウィンドウサイズを64 MBに設定する際は、RAMの消費量に注意が必要です。Linuxはアクティブな接続ごとにバッファメモリを割り当てます。リバースプロキシなどで50,000件の同時接続を処理している場合、メモリ上限の引き上げによってホストの空きメモリが一気に枯渇するおそれがあります。しかし、データベースのレプリケーション専用ホストやバックアップエンドポイント、動画配信オリジンなどにおいては、BBRを適用することで不安定な大陸間リンクを信頼性の高い高速な専用パイプへと生まれ変わらせることができます。

Share: