【実務・中級編】 Cloud Armorのレートリミッティング(Rate Limiting)と過負荷制御 – クラウドインフラと仮想化ネットワーク実践ガイド

Cloud Armorで「正当なトラフィック」を守り抜く――レートリミッティングの現場的解釈

システムを運用していると、必ずと言っていいほど直面する「想定外のトラフィック」という悪夢。それは攻撃者の執拗なブルートフォースかもしれませんし、あるいは仕様を読み違えたクライアントの「行儀の悪い」再試行ロジックかもしれません。

ロードバランサ(GCLB)の最前線でこれらを食い止めるのが、Google Cloud Armorのレートリミッティングです。今日は、教科書的なマニュアルには載っていない、現場でハマりがちなポイントを交えながら、この強力な武器の使い方を深掘りしていきましょう。

—

なぜ「429 Too Many Requests」が重要なのか

RFC 6585で定義された 429 Too Many Requests ステータスコード。これは単なるエラーではなく、サーバーからの「今はこれ以上受け付けられないから、少し落ち着いてくれ」という切実なメッセージです。

Cloud Armorでレートリミッティングを有効にすると、設定した閾値を超えたリクエストに対して、GCLBが即座にこのコードを返します。バックエンドのアプリケーションに到達する前に境界で遮断することで、貴重なCPUやメモリリソースを保護し、本来の正当なユーザーへのレスポンス速度を維持する。これがSREとしての第一の防衛線です。

—

レートリミッティングの基本パラメーターを理解する

Cloud Armorの設定画面や gcloud コマンドで設定する際、以下のパラメータが肝になります。

  • rate-limit-threshold: 指定した期間内に許容するリクエスト数。
  • interval: 期間(例: 60 seconds)。
  • enforce-on-key: これが最も重要です。何を単位に「頻度」をカウントするかを決めます。

IP アドレス単位で制限するのが最も一般的ですが、APIの場合は ALL(全体)や HTTP-HEADER(特定のAPIキーなど)を指定することで、より柔軟な制御が可能になります。

設定例:特定のIPからの攻撃を防ぐ(CLI)

以下は、特定のパスに対して1分間に100リクエストを超えたIPを遮断する設定です。

# セキュリティポリシーを作成
gcloud compute security-policies rules create 1000 \
    --security-policy=my-policy \
    --expression="request.path.matches('/api/v1/login')" \
    --action=rate-based-ban \
    --rate-limit-threshold-count=100 \
    --rate-limit-threshold-interval-sec=60 \
    --conform-action=allow \
    --exceed-action=deny-429 \
    --enforce-on-key=IP

—

現場で遭遇する「罠」とトラブルシューティング

レートリミッティング導入時に、必ずと言っていいほど直面する問題が「誤検知」です。

1. 共有IP環境の落とし穴

オフィスや大学、あるいはNATゲートウェイ経由のトラフィックを IP 単位で制限すると、その背後にいる全ユーザーがまとめて遮断されます。「特定のAPIキー」や「セッションID」を HTTP-HEADER に指定してキーにするのが、API設計における賢い選択です。

2. クライアント側の再試行ロジック

429 を受け取ったクライアントが、即座にリトライを繰り返すと状況は悪化します。クライアント側の実装には、必ず「指数バックオフ」を組み込むよう、フロントエンドやモバイルアプリのチームと握っておきましょう。

以下は、Pythonでレートリミットを考慮したリクエスト送信のサンプルです。

import requests
import time

def call_api_with_retry(url):
    while True:
        response = requests.get(url)
        
        # 429 Too Many Requests の場合の処理
        if response.status_code == 429:
            retry_after = int(response.headers.get("Retry-After", 5))
            print(f"制限超過。{retry_after}秒待機します...")
            time.sleep(retry_after)
            continue
            
        return response

# 実行
call_api_with_retry("https://api.example.com/data")

—

デバッグと監視の重要性

Cloud Armorが「今、誰をブロックしているのか」を確認するには、Cloud Logging(旧Stackdriver)が欠かせません。以下のクエリで、実際に 429 を返しているトラフィックを確認できます。

# Cloud Logging 用のクエリ例
jsonPayload.enforcedSecurityPolicy.name = "my-policy"
jsonPayload.enforcedSecurityPolicy.outcome = "DENY"
httpRequest.status = 429

設定したルールが適切かどうかは、導入初期は rate-limit-threshold をあえて緩めに設定し、ログを観察しながら徐々に締め上げていくのが、本番環境での鉄則です。

—

最後に:防御は「余白」が大事

ネットワークエンジニアとして言えることは、レートリミットは「システムを守るためのクッション」であり、それ自体が目的ではないということです。

過剰に制限を厳しくすれば、UXは確実に低下します。しかし、何もしなければシステムはダウンし、全ユーザーに迷惑がかかる。そのバランスを取るために、Cloud Armorのようなインフラレベルでの制御と、アプリケーション側での適切なエラーハンドリング(Retry-Afterヘッダーの活用など)を組み合わせるのが、現代のクラウドネイティブな開発スタイルなのです。

皆さんのインフラが、今日も平穏無事に稼働し続けることを願っています。それでは、また次回の深掘りでお会いしましょう。

コメント

タイトルとURLをコピーしました