ネットワークの「渋滞」を華麗にいなす:WREDが教えてくれるTCPの深淵
ネットワークエンジニアとして現場に立っていると、必ずと言っていいほどぶつかる壁がある。それが「バッファ溢れ(Tail Drop)」だ。
スイッチやルーターのキューがいっぱいになると、そこに来たパケットは問答無用で捨てられる。これが「Tail Drop」だ。一見すると公平だが、これが発生すると、その瞬間に通過していた複数のTCPセッションが一斉にウィンドウサイズを縮小し、その直後に一斉に再送を始める。いわゆる「TCPのグローバル同期現象」だ。ネットワーク全体が「渋滞しては動き出し、また渋滞する」という、あの忌々しい呼吸を繰り返すことになる。
この悪循環を断ち切り、川の流れのようにスムーズな通信を実現するために生まれたのが、今回解説する WRED(Weighted Random Early Detection) だ。
—
WREDとは何か? — 賢い「間引き」の美学
WREDは、キューがいっぱいになる前に、確率的にパケットを間引く技術だ。
「えっ、わざと捨てるの?」と思うかもしれないが、ここが面白い。TCPはパケットをロスすると「あ、混雑しているな」と判断して送信レートを落とす。WREDは、キューが完全に飽和する前に「少しだけ」パケットを捨てることで、特定のTCPセッションだけを先に減速させる。 全員が同時に減速するのではなく、一部を早めに間引くことで、ネットワーク全体が「混雑の一歩手前」で踏みとどまるように調整するのだ。
WREDを理解するための3つのパラメーター
WREDの設定で必ず出てくるのが以下の3つの閾値だ。これらを理解しないと、チューニングは不可能な暗闇と化す。
1. Minimum Threshold(最小閾値): この値まではパケットを絶対に捨てない。
2. Maximum Threshold(最大閾値): この値を超えると、設定された確率でドロップが始まる。
3. Mark Probability Denominator(ドロップ確率の分母): 最大閾値に達したときに、どれくらいの確率で捨てるかの逆数。例えば 10 なら、1/10の確率で捨てる。
これらは、キューの深さ(平均キュー長)を見て動的に変化する。瞬間的なバーストでドロップが起きないよう、指数加重移動平均(EWMA)を用いて「平均的な混雑具合」を監視するのが、WREDの賢いところだ。
—
実践:Cisco IOSでのWRED設定
例えば、社内バックボーンのルーターで、特定のQoSクラスに対してWREDを有効にする設定はこうだ。
! クラスマップを作成し、対象のトラフィックを定義
class-map match-any DATA-TRAFFIC
match protocol http
match protocol https
! ポリシーマップでWREDを適用
policy-map QOS-POLICY
class DATA-TRAFFIC
! 平均キュー長が20〜40パケットの範囲でドロップを開始
! 最大ドロップ確率は 1/10 (10)
random-detect exponential-weighting-constant 9
random-detect min-threshold 20
random-detect max-threshold 40
random-detect mark-probability-denominator 10
ここで重要なのは exponential-weighting-constant だ。これは平均キュー長を計算する際の「忘却係数」のようなもの。この値を小さくすると、瞬間的なバーストの影響を強く受けるようになり、大きくすると、より長期的な混雑トレンドを重視するようになる。
—
アプリケーション開発者が知っておくべきこと
Web APIを設計する際、バックエンドエンジニアが意識すべきは「パケットロスが起きたときのTCPの挙動」だ。
もしあなたのAPIが、巨大なJSONレスポンスを一度に返しているなら、それはネットワークのバッファを圧迫する「大物」だ。もしWREDが効いているネットワークを通るなら、途中でパケットが間引かれ、TCPの再送制御が発生する。これによって、クライアント側の Fetch API や curl のレスポンスタイムが「数ミリ秒の遅延」から「数百ミリ秒の遅延」へと跳ね上がる。
Pythonでパケットロスをシミュレートしてみる
もし、あなたのAPIが不安定なネットワーク越しにどう見えるかを確認したいなら、tc (Traffic Control) コマンドと組み合わせて、テスト環境でパケットロスを意図的に起こしてみるのが一番だ。
# eth0インターフェースで 1% のパケットロスを発生させる
sudo tc qdisc add dev eth0 root netem loss 1%
# テスト後に設定を解除する
sudo tc qdisc del dev eth0 root
こうしてロスを発生させた状態で、curl で時間を計測してみると、WREDが効いている環境とそうでない環境での「渋滞の捌け方」の違いが手に取るようにわかるはずだ。
# APIの応答時間をミリ秒単位で確認
curl -w "Time: %{time_total}s\n" -o /dev/null -s https://api.example.com/data
—
現場からのアドバイス:WREDは魔法ではない
最後に、ネットワークスペシャリストとして一つだけ警告しておこう。
WREDは、あくまで「TCPが混雑を検知するのを助ける」ツールに過ぎない。UDPのような、パケットロスを気にしないプロトコルには全く効果がないし、そもそも帯域幅自体が不足している物理的な過負荷状態(オーバーサブスクリプション)を解決する魔法ではない。
真のトラブルシューティングでは、「なぜキューが溜まっているのか?」をまず突き止めること。それは特定のサーバーからの過剰なリクエストか、それとも帯域設計そのもののミスか。WREDはその「渋滞の緩和」には貢献するが、渋滞の原因を取り除くのは、あなたの設計と分析だ。
ネットワークは生き物だ。パケットの一つ一つにストーリーがある。WREDという「交通整理」を味方につけて、しなやかで止まらないサービスを構築してほしい。質問があれば、いつでも現場のコンソール越しに相談してくれ。
コメント