トラフィックを「制御」せよ:Leaky BucketとToken Bucketが支えるネットワークの秩序
ネットワークエンジニアの諸君、今日もパケットの海を泳いでいるか?
インフラを構築していると、必ずぶち当たる壁がある。「帯域制限」だ。APIのレートリミット設計から、スイッチのQoS設定に至るまで、トラフィックをいかに制御するかはエンジニアの腕の見せ所だ。
今日は、その根幹をなす2つのアルゴリズム「Leaky Bucket(リーキーバケット)」と「Token Bucket(トークンバケット)」について、現場の視点から掘り下げよう。教科書には載っていない、なぜこの仕組みが必要なのか、という「現場の泥臭い理由」を含めて解説する。
—
なぜ「バケット(バケツ)」が必要なのか?
ネットワークトラフィックは気まぐれだ。Web APIへのリクエストは一瞬でスパイクするし、ストリーミングデータは恒常的に帯域を食いつぶす。
もし、ルーターやサーバーがやってくるパケットをすべて無制限に受け入れたらどうなるか? 答えは明白、バッファオーバーフローによるパケットロス、そして「バーストによるサービス停止」だ。
これらを制御するために、我々は「バケツ」という概念を持ち込んだ。
—
1. Leaky Bucket:厳格な「等速」の規律
Leaky Bucketは、穴の空いたバケツをイメージしてほしい。
- 仕組み: どんなに大量の水(パケット)が注ぎ込まれても、バケツの穴からは「一定の速度」でしか水が流れ出ない。
- 特徴: トラフィックがどれだけバーストしようが、出力は必ず一定になる。
実務での適用
これは、ビデオ会議のストリーミングや、厳密な帯域保証が必要な回路で使われる。しかし、Web APIの世界では少し融通が効かない。「多少のバーストは許容して、短時間なら高速に処理したい」という要件には向かないからだ。
—
2. Token Bucket:柔軟な「バースト」の許容
現代のネットワーク設計で主流なのが、このToken Bucketだ。
- 仕組み: バケツの中に「トークン(通行手形)」を一定間隔で補充する。パケットを送信するには、このトークンが必要だ。
- 特徴: バケツにトークンが溜まっていれば、短時間に大量のパケット(バースト)を送信できる。トークンが尽きれば、トークンの補充速度(レート)まで送信速度が落ちる。
パラメーターの重要性
実務で必ず調整することになるパラメーターが以下の2つだ。
1. CIR (Committed Information Rate): トークンが補充される平均速度。これが実効帯域の目安になる。
2. Bc (Committed Burst Size): バケツの容量。一度にどれだけバーストできるかを決める。
—
実践:APIレートリミットへの応用(Python実装例)
Web APIの設計において、Token Bucketは「トークンバケットアルゴリズム」としてそのまま実装されることが多い。Pythonで簡単なデモを作ってみた。
import time
class TokenBucket:
def __init__(self, capacity, fill_rate):
self.capacity = capacity # バケツの最大容量 (Bc)
self.fill_rate = fill_rate # 1秒あたりの補充量 (CIR)
self.tokens = capacity # 現在のトークン数
self.last_fill = time.time() # 最後に補充した時間
def consume(self, amount):
# 経過時間分だけトークンを補充
now = time.time()
elapsed = now - self.last_fill
self.tokens = min(self.capacity, self.tokens + elapsed * self.fill_rate)
self.last_fill = now
# トークンが足りるかチェック
if self.tokens >= amount:
self.tokens -= amount
return True # 通過
return False # レート制限(429 Too Many Requests)
# 毎秒5リクエスト、バースト許容数10のバケツ
limiter = TokenBucket(capacity=10, fill_rate=5)
# APIリクエストのシミュレーション
for i in range(15):
if limiter.consume(1):
print(f"リクエスト {i+1}: 成功")
else:
print(f"リクエスト {i+1}: 失敗 (429 Too Many Requests)")
time.sleep(0.1)
—
ネットワーク機器での設定(Cisco IOS例)
現場のスイッチやルーターでは、policy-map を使ってこれを実現する。Ciscoの例を見てみよう。
# 帯域幅を10Mbpsに制限し、バーストサイズを考慮する設定
policy-map SHAPE_TRAFFIC
class class-default
# 10Mbps (CIR) で整形し、バーストサイズを 256k に設定
shape average 10000000 256000
# インターフェースへの適用
interface GigabitEthernet0/1
service-policy output SHAPE_TRAFFIC
*注: ここでの 256000 が Bc に該当する。これを小さくしすぎるとバーストが効かず、大きくしすぎるとジッターの原因になる。このバランス取りこそが、エンジニアの腕の見せ所だ。*
—
現場のエンジニアへのアドバイス
最後に、トラブルシューティングの際のTipsを一つ。
「通信が遅い」というクレームが来たとき、多くの若手はまず回線帯域を疑う。しかし、実はその手前の「トークンバケットの設定(特に Bc)」が小さすぎて、小刻みなバーストが制限され、TCPのウィンドウサイズが縮小しているだけのケースが非常に多い。
show policy-map interface コマンドを叩いて、drop カウンタが回っていないか確認してくれ。もし drop が増えているなら、それはネットワークが悪いのではなく、君が設計した「バケツ」が少しだけ小さすぎたのかもしれない。
理論を理解し、数値を読み解く。それだけで、君のネットワークは一段上の信頼性を手に入れるはずだ。健闘を祈る。
コメント