こんにちは!ネットワークの深淵を覗き見するのが大好きなインフラエンジニアの皆さん、そしてこれからネットワークの世界へ一歩を踏み出す初学者の皆さん。日々のインフラ運用、本当にお疲れ様です。
ネットワークの世界に身を置いていると、「なんだか最近、特定のサーバーからの通信がネットワーク全体を圧迫している気がする…」「クラウドのAPIにリクエストを送りすぎて、相手から『ちょっと落ち着いて!』とエラー(レートリミット)を返されてしまった…」そんなトラブルに直面したことはありませんか?
ネットワークを流れるデータは、私たちが蛇口をひねるように常に一定の量で流れてくれるわけではありません。ある瞬間には誰も使っていないのに、次の瞬間には一気に大量のデータが押し寄せる「バーストトラフィック」が日常茶飯事に発生します。この暴れ馬のようなトラフィックを綺麗になだめ、交通整理をしてくれるのが「トラフィックシェーピング」という技術です。
今回は、そのトラフィックシェーピングの裏側で静かに、しかし強力に働き続ける2つの大人気アルゴリズム、「リーキーバケット(Leaky Bucket)」と「トークンバケット(Token Bucket)」について、身近な例えを交えながら優しく紐解いていきましょう!一歩ずつ理解していけば、決して難しいものではありませんよ。それでは、パケットの旅に出てみましょう!
—
1. なぜトラフィックの「平準化」が必要なのか?
まず最初に、なぜデータ通信の交通整理が必要なのかをイメージしてみましょう。
例えば、あなたが大好きな人気テーマパークのアトラクションに並んでいるとします。朝イチの開門直後や、パレードが終わった直後には、一気に何百人ものゲストがアトラクションの入口に押し寄せますよね。もし、その入り口のスタッフが「さあ、全員いっぺんに入ってください!」とゲートを全開にしたらどうなるでしょうか? 通路は大混雑し、将棋倒しが起きたり、アトラクションの機械が悲鳴を上げて故障したりしてしまいます。
ネットワークの世界でも全く同じことが起きています。スイッチやルーター、そしてクラウドサービスの入口には、処理能力の「限界(キャパシティ)」があります。瞬間的に限界を超えるデータが流れ込むと、ルーターの内部にあるメモリ(バケット)がいっぱいになり、入りきらなかった大切なデータが容赦なく捨てられて(パケットロス)しまいます。
これを防ぐために、「一度に流れてくるデータをちょうどいい具合に受け止め、一定のペースで送り出す」のがトラフィックシェーピングの役割です。この交通整理を数学的・論理的に実現するのが、これから紹介する2つのバケット(バケツ)アルゴリズムなのです。
—
2. 水漏れバケツで一定ペースを守る「リーキーバケット(Leaky Bucket)」
最初にご紹介するのは「リーキーバケット(Leaky Bucket)」アルゴリズムです。日本語に訳すと「穴あきバケツ」ですね。名前の通り、なかなかシュールで分かりやすい仕組みをしています。
身近な例え:底に小さな穴が空いたバケツ
バケツの上から、水をどれだけドバドバと勢いよく注ぎ込んでも、バケツの底には小さな穴が1つだけ空いています。水はこの穴から、「一定のスピードでポタポタと」しか流れ出ることができません。
- 注ぐ水 = ネットワークに流れ込んでくるバースト状のデータ
- バケツ = ルーターなどの一時的な保存領域(キュー)
- 底の穴から漏れ出る水 = 綺麗に平準化されて送り出されるデータ
もし、上から注ぐ水の量がバケツの容量を超えてしまうと、バケツから水があふれてしまいます。これがネットワークにおけるパケットロスです。しかし、バケツの中に水がある限り、下から流れ出る水の勢いは常に一定に保たれます。
リーキーバケットのメリット・デメリット
- メリット: 送出レートが完全に一定になるため、下流のネットワークや受信側の機器に一切の負担をかけません。予測可能な美しいトラフィックを作ることができます。
- デメリット: 例え下流の回線に余裕があったとしても、定められた「穴の大きさ(一定速度)」以上のスピードでデータを送ることができません。瞬間的な爆発力(バースト)を活かせないのがもどかしいところです。
—
3. チケット制で自由度を持たせる「トークンバケット(Token Bucket)」
「一定のペースを守るのは大事だけど、たまには思い切り速くデータを送らせてほしい!」
そんな現場のわがまま(?)をスマートに解決してくれるのが、「トークンバケット(Token Bucket)」アルゴリズムです。現代のルーターやクラウドのAPI制限(レートリミット)で圧倒的に多く採用されているのは、実はこっちの仕組みです。
身近な例え:遊園地のアトラクション乗車券(トークン)
今度は、底に穴が空いていない「普通のバケツ」を想像してください。このバケツには、一定の時間ごとに「トークン(切符)」がパラパラと補充されます(例:1秒間に10枚まで)。
データをネットワークに送り出すには、この「トークン」をチケットとして1枚消費しなければなりません。
1. トークンの貯金: バケツの中にトークンが最大容量(バケットサイズ)までたまっています。
2. バースト送信: 「今だ!一気にデータを送りたい!」という時、バケツの中にたまっているトークンをまとめて使えば、一瞬だけ大量のデータをドカンと送り出すことができます(バーストの許容)。
3. 枯渇と制限: トークンを使い果たしてしまうと、新しいトークンが補充されるまでの間、データは送信を待たされます。結果として、平均すると一定の速度に収まるようになります。
トークンバケットの数理モデルとパラメータ
ネットワーク機器(CiscoルーターやLinuxの tc コマンドなど)でトークンバケットを設定する際、主に次の3つのパラメータを調整します。
- CIR (Committed Information Rate / 契約帯域): トークンが補充される基本のスピード(例:
10Mbps)。 - Bc (Committed Burst Size / 許容バーストサイズ): バケツの最大容量(トークンを最大でどれくらい貯めておけるか)。
- Be (Excess Burst Size / 超過バーストサイズ): さらに超過したトラフィックをどこまで許容するか(オプション)。
—
4. 実務で使える!Linux tc (Traffic Control) による実装例
「理屈は分かったけれど、実際のインフラ現場ではどうやって設定するの?」
そんな気loopsなあなたの為に、Linux環境(UbuntuやCentOSなど)でトークンバケットアルゴリズムを使ったトラフィックシェーピングを実際に適用する設定スクリプトをご紹介します。実務のテスト環境などでそのまま参考になるはずです。
今回は、Linuxのカーネルに標準搭載されている tc (Traffic Control) コマンドと htb (Hierarchical Token Bucket) というキューイング規律を使用します。
#!/bin/bash
# =====================================================================
# Linux Traffic Control (tc) を使ったトークンバケットによる帯域制御スクリプト
# 対象インターフェース: eth0
# 目標: 送信帯域を 1Mbps に制限し、最大バーストサイズを 15KB に設定する
# =====================================================================
IFACE="eth0"
# 1. 既存のqdisc(キューイング規律)を一度クリアしてクリーンな状態にする
sudo tc qdisc del dev $IFACE root 2>/dev/null
# 2. ルートとなる HTB (Hierarchical Token Bucket) qdisc を作成する
# handle 1:0 は識別子、default 10 は指定なき場合のデフォルトクラス
sudo tc qdisc add dev $IFACE root handle 1: htb default 10
# 3. 親クラス(帯域の全体コンテナ)を定義
# 最高で使える帯域を 1Mbps に設定
sudo tc class add dev $IFACE parent 1: classid 1:1 htb rate 1mbit ceil 1mbit
# 4. 子クラス(実際のトークンバケットを適用するクラス)を定義
# rate: 帯域の保証値 (1Mbps)
# ceil: 借りられる上限値 (1Mbps)
# burst: バケツの大きさ(トークン容量 = 15KB。パケットのバーストをここまで許容)
sudo tc class add dev $IFACE parent 1:1 classid 1:10 htb rate 1mbit ceil 1mbit burst 15k
# 5. 仕上げに、設定したクラスにトラフィックを紐付けるフィルタ規則を追加
sudo tc filter add dev $IFACE protocol ip parent 1:0 prio 1 u32 match ip dst 0.0.0.0/0 flowid 1:10
echo "トポロジーの設定が完了しました。eth0 からの送信帯域が 1Mbps にシェーピングされています。"
設定のポイント
上記のスクリプト内にある burst 15k という記述こそが、まさにトークンバケットのバケツの大きさを指定している部分です。この値を小さくしすぎると、ちょっとしたパケットの固まりですぐに頭打ちになってしまい、逆に大きくしすぎると瞬間的なバーストが強すぎて下流のスイッチに負荷をかけてしまいます。回線の遅延(RTT)やアプリケーションの特性を見極めながら、現場ごとにチューニングを行うのがインフラエンジニアの腕の見せ所です!
—
5. おわりに:バケツの仕組みを知れば、ネットワークが見えてくる
今回は、ネットワークの交通整理に欠かせない「リーキーバケット」と「トークンバケット」の2つのアルゴリズムについて、身近な例えと実際のLinux設定を交えて解説しました。
- リーキーバケット: 水漏れバケツのように、完全に一定のペースでデータを送り出す(安全第一)。
- トークンバケット: 切符の仕組みを使って、普段は一定、いざという時はバースト(爆発的な送信)を許可する(柔軟性とスピードの両立)。
ネットワークの背後にあるこうしたアルゴリズムの数理モデルを知ると、ただ流れているように見えるパケットたちが、まるで緻密にコントロールされた美しいダンスを踊っているかのように見えてきませんか?
日々のインフラ運用やクラウドアーキテクチャの設計で「レートリミット」や「QoS(Quality of Service)」という言葉に出会ったときは、ぜひ今回の「バケツとトークン」の姿を思い出してみてください。きっと、より精度の高い設計やトラブルシューティングができるはずです。
それでは、また次回の深淵なネットワークの世界でお会いしましょう!良きインフラライフを!
コメント