クラウドの「外」へ繋ぐという決断:EKS/FargateにおけるNATゲートウェイの深淵
SREの現場にいると、「なぜかコンテナから外部APIが叩けない」「たまにタイムアウトする」という相談を、週に一度は耳にします。
KubernetesのPodやAWS Fargateのタスクから外の世界(インターネット上のAPIなど)へ通信する際、私たちは当たり前のように「NATゲートウェイ」を配置します。しかし、そのパケットがどう変換され、どの程度の負荷を考慮すべきかを理解している人は意外と少ない。
今回は、AWSにおけるコンテナネットワークの「出口戦略」について、パケットの挙動を追いながら、現場目線で紐解いていきましょう。
—
1. パケットはどこを駆け巡るのか?:通信のシーケンス
プライベートサブネットに配置されたPodから、外部の api.example.com へリクエストを送る場合、パケットは以下の旅路を辿ります。
1. Podの送信: Podは自身のプライベートIPから宛先IP(外部API)へパケットを投げます。
2. VPCルーター: VPCのルートテーブルを参照し、宛先がVPC外であれば「ターゲット:nat-xxxxxxxx」へとルーティングされます。
3. NATゲートウェイの魔法: ここでNATゲートウェイが「送信元プライベートIP」を「NATゲートウェイ自身のパブリックIP」へと書き換えます(SNAT: Source NAT)。
4. インターネットへ: AWSのバックボーンを通ってインターネットへ到達。
5. レスポンス: API側からの応答はNATゲートウェイに戻り、NATゲートウェイがコネクション情報を元に元のPodのプライベートIPへパケットを書き戻して返します。
この仕組みがあるからこそ、PodはパブリックIPを持たずに安全に外部と通信できるのです。
—
2. 現場で直面する「ポート枯渇」という罠
NATゲートウェイには「1つのIPアドレスあたり、最大64,512個の同時コネクション」という制限があります。もし、あなたのサービスがマイクロサービス化されており、膨大な数のPodから一斉に外部APIを叩くとどうなるか。
そう、SNATポート枯渇です。
実践:タイムアウトを回避する実装の心得
外部APIを叩く際、毎回 TCPコネクション を張っていてはNATゲートウェイのポートを食いつぶします。必ず「コネクションプーリング」を意識してください。
Python (requests) でのコネクション再利用例
import requests
# セッションオブジェクトを使って接続を維持(コネクションプール機能)
session = requests.Session()
# アダプターを設定してリトライや接続数を最適化
adapter = requests.adapters.HTTPAdapter(pool_connections=50, pool_maxsize=50)
session.mount('https://', adapter)
def call_external_api():
# 毎回接続を確立せず、プールされたコネクションを再利用する
response = session.get('https://api.example.com/data', timeout=5)
return response.json()
このように、クライアント側で Session や Connection Pool を明示的に制御することが、インフラコストと障害防止の観点から極めて重要です。
—
3. トラブルシューティング:パケットはどこで消えた?
「APIが返ってこない」という報告があったら、まずは以下の手順で切り分けを行います。
ステップ1: コンテナ内からの導通確認
まずは kubectl や aws ecs execute-command でコンテナに入り、curl で詳細を確認します。
# 接続先までの経路をトレース
# -v: 詳細出力、--connect-timeout: 接続タイムアウト時間を指定
curl -v --connect-timeout 5 https://api.example.com/health
ステップ2: VPCフローログの解析
NATゲートウェイを通っているパケットが REJECT されていないか確認します。AWSマネジメントコンソールから「VPCフローログ」を有効にし、以下のクエリで確認するのが定石です。
-- NATゲートウェイのインターフェースID(eni-xxxx)を指定して通信を確認
SELECT srcaddr, dstaddr, action, protocol
FROM vpc_flow_logs
WHERE interface_id = 'eni-xxxxxxxx'
AND action = 'REJECT';
—
4. アーキテクトからのアドバイス:コストと可用性の最適化
NATゲートウェイは便利ですが、データ処理量に応じた課金が発生します。
- リージョン間通信を避ける: NATゲートウェイを経由して別リージョンのAWSサービスを叩くのはコストの無駄です。VPCエンドポイント(Interface型 / Gateway型)を活用し、AWSネットワーク内だけで完結させましょう。
- マルチAZ構成: NATゲートウェイはAZごとに配置する必要があります。1つのAZだけでNATゲートウェイを運用するのは、そのAZが死んだ瞬間に全AZの外部通信が止まるという「単一障害点」を作る行為です。
最後に
NATゲートウェイは、コンテナを「隔離された安全な檻」から「外の世界と繋がる広場」へ解き放つゲートです。しかし、そのゲートの幅には限りがあり、通り方(実装)次第では渋滞が起きます。
コードを書くときは、その先にあるネットワークの挙動を想像してみてください。パケットがNATゲートウェイを通過するその一瞬に想いを馳せることこそが、真のSREへの第一歩です。
皆さんのインフラが、今日も安定して外部APIと握手できていることを願っています。
コメント