【実務・中級編】 バーストトラフィック発生時におけるNATゲートウェイの帯域幅制限仕様 – クラウド&コンテナネットワーク実践ガイド

NATゲートウェイの「見えない天井」:バーストトラフィックで落ちないための生存戦略

エンジニアの皆さん、こんにちは。現場で修羅場をくぐり抜けてきたSREとして、今日は皆さんが意外と見落としがちな「NATゲートウェイ(NAT GW)の帯域幅制限」という名の罠についてお話しします。

「クラウドだから勝手にスケールするんでしょ?」という甘い認識で設計していると、突発的なバーストトラフィックに襲われた瞬間、Web APIがタイムアウトの嵐に見舞われ、原因切り分けで数時間をドブに捨てることになります。今日は、その「勝手にスケールする」という魔法の裏側にある、泥臭い現実を紐解いていきましょう。

—

1. NATゲートウェイの「自動スケーリング」という幻想

AWSのNATゲートウェイを例に挙げましょう。ドキュメントには「自動的にスケーリングする」と書かれていますが、重要なのは「どのくらいの速度でスケーリングするのか」です。

NATゲートウェイは、開始時の帯域幅が5 Gbpsで、最大45 Gbpsまでスケールします。しかし、これは「瞬時に45 Gbpsになる」わけではありません。以前のトラフィック実績に基づき、過去の最大ピークの120%までがバーストの許容範囲とされています。

つまり、「普段ほとんど通信していないNAT GWに、いきなり10 Gbpsのトラフィックをぶつける」と、高確率でパケットロスが発生します。

なぜパケットロスが起きるのか

NAT GWは、急激なトラフィック変動に対して、スループットを段階的に引き上げる「プログレッシブ・スケールアップ」という挙動をとります。このスケールアップが追いつかない間のトラフィックは、容赦なく破棄されるか、またはシェーピング(遅延)されます。TCP通信であれば再送が発生し、レイテンシが跳ね上がり、結果としてAPIのHTTPステータス 504 Gateway Timeout や 502 Bad Gateway が返ることになります。

—

2. 現場で使えるデバッグと検証の作法

もしAPIの応答が急に悪くなったら、まずは「NAT GWがボトルネックになっていないか」を疑ってください。AWSであれば CloudWatch Metrics の ErrorPortAllocation や Throughput を見るのが定石です。

もし再現テストをするのであれば、iperf3 を使って疑似的にトラフィックを流すのが一番手っ取り早いです。

# パブリックサブネット側のEC2(クライアント)で実行
# ターゲットとなるNAT GW経由の通信をシミュレート
iperf3 -c <APIの接続先IP> -t 60 -P 10 --bandwidth 2G

# このコマンドで、NAT GWが追従できるかスループットを確認する
# 途中でパケットロス率が増加し、レイテンシが不安定になれば限界です

—

3. 実践:バーストに強いWeb API設計

インフラ側でどうしようもない「物理の壁」がある以上、アプリケーション側での防衛策が必須です。

Pythonによるリトライ戦略(指数バックオフ)

急激なバーストでパケットが落ちるなら、再送タイミングをずらす「指数バックオフ」を実装しましょう。これを怠ると、全クライアントが同時に再送を試みる「サンダリング・ハード(Thundering Herd)問題」を引き起こし、状況を悪化させます。

import time
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

def get_session_with_retry():
    session = requests.Session()
    # 指数バックオフの設定: 最初の再送は1秒後、次は2秒、次は4秒...
    retries = Retry(
        total=5,
        backoff_factor=1,  # 再送間隔の倍率
        status_forcelist=[502, 503, 504] # NAT GW起因のタイムアウトを想定
    )
    session.mount('https://', HTTPAdapter(max_retries=retries))
    return session

# API呼び出し実行
session = get_session_with_retry()
try:
    response = session.get("https://api.example.com/data")
    response.raise_for_status()
except requests.exceptions.RequestException as e:
    print(f"致命的なエラー: {e}")

—

4. プロの設計判断:NAT GWを分ける勇気

もし皆さんが高トラフィックなマイクロサービスを運用しているなら、「NAT GWをサービスごとに分ける」という設計も検討してください。

  • サービスA(高トラフィック): 専用のNAT GWを割り当て、常にトラフィックを流して「温めておく(ウォームアップ)」。
  • サービスB(低トラフィック): 別のNAT GWに寄せる。

NAT GWを共有していると、サービスAのバーストが原因で、サービスBまで巻き添えを食らいます。コストは増えますが、可用性とトラブルシューティングの切り分けやすさは劇的に向上します。

設定のヒント(Terraform例)

# サービスごとのNATゲートウェイを作成する設計
resource "aws_nat_gateway" "service_a_nat" {
  allocation_id = aws_eip.nat_a.id
  subnet_id     = aws_subnet.public_a.id

  tags = {
    Name = "service-a-nat-gw"
  }
}

—

最後に:ネットワークを「生き物」として扱う

クラウドネットワークは、単なるパイプではありません。トラフィックの増減に合わせて微妙に挙動を変える「生き物」です。

「設計図通り動くはずだ」と信じ込むのではなく、「いつかNAT GWが悲鳴を上げるかもしれない」という前提で、適切な監視アラートを仕込み、アプリケーション側でリトライを制御し、必要であればNAT GWを分離する。 この泥臭い積み重ねこそが、大規模トラフィックを捌くSREの真髄です。

皆さんのシステムが、予期せぬバーストでも涼しい顔をして動き続けることを願っています。それでは、また現場でお会いしましょう。

コメント

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