クラウドの隠れた吸血鬼:クロスAZ・NATゲートウェイ通信のコスト最適化とパケット局所化の極意
クラウドネイティブなシステム設計において、私たちは「高可用性(HA)」という錦の御旗のもと、マルチAZ(可用性ゾーン)構成をデフォルトとして採用してきた。アプリケーション層を複数のAZに分散させ、ロードバランサーがトラフィックを綺麗に振り分ける。美しく、堅牢なアーキテクチャだ。
しかし、その裏でAWSやGCPなどのメガクラウドの利用明細(Bill)を見たCFOが青ざめる瞬間がある。「なぜ、同一リージョン内の通信なのに、これほどのデータ転送量課金が発生しているのか?」と。
犯人は、プライベートサブネットからインターネット方向(あるいは外部サービス)へ向かうトラフィックをさばくために配置された NAT Gateway である。そしてさらにタチが悪いのは、「同一リージョン内の別AZに存在するリソース同士が、なぜかNAT Gatewayを経由して通信し、無駄なクロスAZデータ転送費とNAT処理費用をダブルでドブに捨てている」という設計アンチパターンが、世界中のプロダクション環境で今この瞬間も蔓延しているという事実だ。
今回は、ネットワークプロトコルとLinuxカーネルの内部挙動を愛してやまないSREの視点から、この「隠れた吸血鬼」を駆逐し、パケットを最短経路で流すためのディープなネットワーク最適化手法を解説しよう。
—
1. パケットはなぜ遠回りをするのか:クロスAZ・NAT経由の悲劇
まずは、現場で何が起きているのかをパケットの旅路から追ってみよう。
通常、VPC(Virtual Private Cloud)内において、AZ-aにあるプライベートサブネットのアプリケーションコンテナから、AZ-bにあるデータベースや、あるいは別のマイクロサービス(あるいは外部API)へリクエストを送る際、適切に設計されていればプライベートIP同士がVPCの内部バックボーンを通って直結される。
だが、以下のような「事故」が起きると話が変わる。
1. ルーティングテーブルの誤設定や、特定のエンドポイントの欠落により、宛先IPがローカルVPC CIDR外と判定される。
2. トラフィックがデフォルトルート (0.0.0.0/0) に従って、ローカルAZにある NAT Gateway へ吸い寄せられる。
3. NAT Gateway でSNAT(Source NAT)が実行され、パケットの送信元IPがNATのパブリックIP(あるいはプライベートIP)に書き換わる。
4. パケットはAWSのネットワークファブリックを経由し、なぜか別AZに存在する宛先リソースへとルーティングされる。
5. 応答パケットも同様の逆経路を辿る。
この一連の動きにより、以下の2点においてコストの二重取りが発生する。
- クロスAZデータ転送コスト: AZ間を行き来するデータ量に応じた課金(Gigabyte単価)。
- NAT Gateway処理コスト: NAT Gatewayを通過するデータ量に応じた課金。
数百万リクエスト/日を超えるマイクロサービス群において、この「不要なNAT経由のクロスAZ通信」は、インフラコストの数割を無駄に食い潰す癌細胞となる。
—
2. 徹底的なトラフィック局所化:VPC内ルーティングとエンドポイントの極意
この問題を根絶するための第一歩は、「トラフィックを絶対に自分のAZ(Zone-local)に閉じ込める」という強い意志を持ったネットワーク設計だ。
2.1 Gateway型VPCエンドポイント(S3 / DynamoDB)の落とし穴
AWS環境において、Amazon S3やDynamoDBへのアクセスに NAT Gateway を使っているチームがいまだに存在する。これはコスト面でもセキュリティ面でも大罪だ。
必ず Gateway型VPCエンドポイント を作成し、各AZのルートテーブルにプレフィックスリスト(pl-xxxxxx)を紐づけたルートを追加する必要がある。
ここで重要なのが、「どのAZのエンドポイントが使われるか」の制御だ。AWSのGateway型エンドポイントはVPC内の全AZからルーティング可能だが、コストとレイテンシを最小化するためには、ルーティングテーブルを各AZのサブネットごとに細かく分割し、ローカル完結させることが鉄則となる。
2.2 Interface型VPCエンドポイント(AWS PrivateLink)とAZ Awareness
外部APIやAWSのマネージドサービス(Secrets Manager, KMS,SQSなど)へアクセスするために Interface型VPCエンドポイント(PrivateLink)を配置する場合、AZアウェアネス(Zone-awareness)を有効にしなければならない。
Interfaceエンドポイントを作成する際、すべてのAZにENI(Elastic Network Interface)を配置し、アプリケーションが属する同一AZ内のENIへ名前解決(DNS)されるように構成する。
# AWS CLIを用いた、特定AZに閉じたInterface型VPCエンドポイント作成の例
aws ec2 create-vpc-endpoint \
--vpc-id vpc-0123456789abcdef0 \
--vpc-endpoint-type Interface \
--service-name com.amazonaws.ap-northeast-1.sqs \
--subnet-ids subnet-AZ-a-private \
--security-group-ids sg-0123456789abcdef0 \
--private-dns-enabled true
*解説:* 上記のように、サブネットを指定して特定のAZ(ここでは subnet-AZ-a-private)にのみENIをデプロイすることで、AZ-aのコンテナからのSQSトラフィックが、他のAZのNATやENIに迷い込む余地を完全に断つことができる。
—
3. Linuxカーネルとトランスポート層のチューニング:RTT削減とバッファ最適化
パケットが同一リージョン内、あるいはVPCピアリングを跨いで通信する際、AZ間の物理的な距離や仮想ネットワークのオーバーヘッドにより、ミリ秒単位のレイテンシ(RTT: Round Trip Time)が発生する。これを極限まで削るためのLinuxカーネルパラメータのチューニングを見ていこう。
コンテナ(KubernetesのPod等)のネットワーク名前空間において、TCP/IPスタックがデフォルトのままであるならば、それはポルシェのエンジンに軽自動車のタイヤを履かせているようなものだ。
3.1 sysctlパラメータによるTCPウィンドウとバッファの最適化
高スループットかつ低レイテンシを維持するためには、/etc/sysctl.conf(またはKubernetesのセキュリティコンテキスト / InitContainerでの動的設定)で以下のカーネルパラメータを調整する。
# 最大TCP受信バッファおよび送信バッファのサイズを拡大 (バイト単位)
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
# TCPの自動チューニング用メモリバッファ範囲 (最小、デフォルト、最大)
# [min, default, max]
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# クロスAZ通信におけるパケットロス耐性と輻輳制御アルゴリズムの変更
# BBR (Bottleneck Bandwidth and RTT) を採用し、パケットロスを恐れない高スループットを実現
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
# TIME_WAIT状態のソケットを迅速に再利用し、コネクション枯渇を防ぐ
net.ipv4.tcp_tw_reuse = 1
*技術的背景:*
特に net.ipv4.tcp_congestion_control = bbr の導入は、クロスAZ間での微小なパケットロスや揺らぎに対して極めて有効だ。従来のCUBICアルゴリズムが「パケットロス=輻輳(混雑)」と誤認してウィンドウサイズを半減させていたのに対し、BBRは実際の帯域幅と伝搬遅延を測定して動的にペース配分を行うため、クラウド上の仮想ネットワーク環境において驚異的なパフォーマンスを発揮する。
—
4. トランスポートセキュリティ(TLS)ハンドシェイクの極限最適化
マイクロサービス間の通信において、ゼロトラストの観点から mTLS(相互TLS)を強制することは現代のインフラストラクチャにおける常識である。しかし、TLSハンドシェイクはTCPの3wayハンドシェイクのあとにさらに往復(RTT)を要求するため、レイテンシの大きなボトルネックとなる。
クロスAZ通信やVPC間通信において、このオーバーヘッドを極限まで削るための設計指針は以下の通りだ。
1. TLS 1.3の強制と 0-RTT(Resumption)の活用
TLS 1.3では、ハンドシェイクの往復が「1.5 RTT」から「1 RTT」へと短縮された。さらに、過去に接続実績のあるセッションであれば、セッションチケットを用いた 0-RTT Resumption により、クライアント側がハンドシェイクの完了を待たずにリクエストデータを送信できる。
2. ALPN(Application-Layer Protocol Negotiation)によるHTTP/2・HTTP/3の選択
マルチプレクシング(多重化)をサポートするHTTP/2や、UDPベースのQUIC(HTTP/3)をセッションレイヤーで強制することで、同一AZ/別AZ間のコネクション確立コストを劇的に削減する。
—
5. 脆弱性回避とセキュリティ:クロスAZトラフィックの盗聴リスクに対する備え
「プライベートサブネット内だから安全」「VPC内だから暗号化は不要」という神話は、セキュリティ専門家の間では何年も前に崩壊している。クラウドプロバイダーの物理基盤やSDN(Software-Defined Networking)レイヤーにおいて、テナント分離は厳格に行われているものの、コンテナのコンプロマイズやサイドカープロキシの誤設定により、同一VPC内の悪意ある(あるいは乗っ取られた)ワークロードからトラフィックを覗き見られるリスク(いわゆるEast-West方向の盗聴)は常に存在する。
5.1 ネットワークポリシー(NetworkPolicy)による厳格なトポロジ制限
Kubernetes環境において、クロスAZ通信や不要な名前空間間通信を物理的・論理的にブロックするためには、CNI(CiliumやCalicoなど)を用いた NetworkPolicy の適用が不可欠である。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-cross-zone-traffic
namespace: production
spec:
podSelector:
matchLabels:
app: payment-processor
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
environment: production
podSelector:
matchLabels:
tier: api-gateway
egress:
- to:
- ipBlock:
cidr: 10.100.0.0/16 # 自VPCのCIDRに限定
# 外部への通信は明示的に許可されたエンドポイント経由のみにする
このようなポリシーを厳格に適用することで、万が一アプリケーションに脆弱性(RCE等)が発見された場合でも、攻撃者がVPC内をラテラルムーブメント(横展開)して他のAZの機密リソースにアクセスし、結果的に不要なNATやクロスAZ通信を発生させるリスクを根本から断つことができる。
—
6. まとめ:パケットの「お行儀」を見直し、クラウドコストと性能を最適化せよ
クラウドアーキテクチャの美しさは、コードの行数だけでなく、目に見えないパケットがどれだけ無駄なく、最短距離で目的地に到達しているかという「物理的(論理的)なエレガンス」に宿る。
今回のポイントをもう一度おさらいしよう。
1. NAT Gatewayの罠に気づく: プライベートサブネットからの通信が、意図せず別AZのNATを経由していないかルーティングテーブルを監査する。
2. トラフィックの局所化: Gateway型/Interface型VPCエンドポイントのAZアウェアネスを正しく設定し、データがAZを跨ぐ頻度を最小化する。
3. カーネルとプロトコルのチューニング: TCP BBRの採用やバッファサイズの拡大により、クラウドの仮想ネットワーク特性に合わせたスループットの限界を引き出す。
4. ゼロトラストの徹底: ネットワークポリシーを用いて、AZ間およびポッド間の不要なパケット往来を物理的にシャットアウトする。
インフラストラクチャのコスト削減とは、リソースのサイズを削るケチな作業ではない。ネットワークの挙動を深く理解し、パケットにとって最も自然で無駄のない「流れるような経路」をデザインすることそのものなのだ。今夜は、お使いのクラウドのVPCフローログを開き、あなたのパケットがどこを旅しているのか、じっくりと観察してみてはいかがだろうか。
コメント