パケットはVPC内でどう迷わず届くのか?AWSの「ローカルルート」とVPC内通信の裏側を徹底解剖する
こんにちは。数々の修羅場――深夜のパケットロス、原因不明のレイテンシー悪化、そして本番環境でのルーティングループ――を乗り越えてきたシニアSREの私です。
Web APIの設計やマイクロサービスのインフラ構築において、私たちは日々何気なく「同じVPC内だから」「プライベートIPだから」という理由で、コンテナや仮想サーバー同士の通信を行っています。例えば、KubernetesのPodから同一VPC内のRDSへ接続したり、フロントエンドのコンテナからバックエンドのAPIサーバーへリクエストを投げたりするときです。
しかし、あなたの放ったそのパケットは、AWS(あるいはGCP)の巨大な仮想ネットワークのなかで、一体どのような仕組みで宛先を見つけ、迷うことなく相手のインターフェースにたどり着いているのでしょうか?
今回は、クラウドネットワークの基礎でありながら、トラブルシューティングの現場で最も立ち返るべき「ローカルルート(Local Route)とVPC内通信のデフォルト動作」について、パケットの旅路とともに深く掘り下げていきましょう。
—
1. ルートテーブルの主役:「local」という静かなる巨人
AWSなどのVPCを作成したとき、ルートテーブルを覗いたことがあるでしょうか?
そこには必ず、以下のようなエントリが最初から刻まれています。
- 送信先 (Destination):
10.0.0.0/16(VPCのCIDRブロック) - ターゲット (Target):
local
この local というターゲット、実は通常のインターネット宛ての igw-xxxxxxxx(インターネットゲートウェイ)や、VPN・Direct Connect宛ての vgw-xxxxxxxx とは毛色が全く違います。APIやコンソール上でも編集や削除ができない、VPCの根幹をなす特殊なルートです。
標準RFC仕様としての背景とクラウドの抽象化
インターネットの基本は、RFC 791(IP)やRFC 1812(ルータの要件)に基づき、ルーターがルーティングテーブルを参照して「次のホップ(Next Hop)」のIPアドレスを決定し、L2(MACアドレス)の書き換えを行ってパケットを転送します。
しかし、クラウドのVPCは「ソフトウェア定義ネットワーク(SDN)」です。物理的なルーターが数珠繋ぎになっているわけではなく、ハイパーバイザーやスマートNIC(AWSのNitro Systemなど)のレイヤーで仮想化されています。
ターゲット local は、「このVPCのCIDRブロックに属するIPアドレス宛てのトラフィックは、外部のルーターに投げず、VPCの分散ルーター(ハイパーバイザー)が直接キャッチして、同一ネットワーク内の宛先インスタンスへダイレクトに配送せよ」という、クラウド特有の抽象化された指令なのです。
—
2. パケットの旅路:同一VPC内インスタンス同士の通信シーケンス
では、実際に同じVPC内の異なるサブネットに存在する、フロントエンドのコンテナ(またはEC2)からバックエンドのAPIサーバーへ、HTTPリクエストを送る際の通信フローを追ってみましょう。
通信の全体像とARP/NDPの秘密
ここで一つ、ネットワークエンジニアなら誰もが一度は疑問に思うポイントがあります。
「AWSのVPC内では、異なるサブネット間であっても、あるいは同一サブネットであっても、インスタンス同士はどのようにMACアドレスを解決しているのか?」
一般的な物理ネットワークであれば、同一セグメント(L2)でなければARP(Address Resolution Protocol)は通りません。しかしVPC内では、AWSのSDN(仮想ルーター)が一種の「プロキシARP(あるいはそれに類する機能)」のように振る舞います。
1. アプリ層からの送出:
フロントエンドのアプリが http://10.0.2.50/api/v1/users に向けてリクエストを発行する。
2. OSのルーティング判定:
OSは自身のIPアドレスとサブネットマスク、そしてルートテーブルを参照し、宛先 10.0.2.50 が自身のCIDR内(またはローカルルート対象)であることを確認。
3. 仮想NIC(ENI)とハイパーバイザーの介入:
パケットがOSから仮想インターフェース(ENI)に送出された瞬間、ハイパーバイザーの分散ルーターがパケットをインターセプトします。
4. ローカルルートの適用:
ルートテーブルの local ターゲットにより、パケットはインターネットゲートウェイやNATゲートウェイへは向かわず、VPCの内部ファブリックへ直接ルーティングされます。
5. 宛先ENIへのデリバリー:
ハイパーバイザーは、宛先IP 10.0.2.50 がどのホスト(あるいはどのENI)にアタッチされているかを内部のトポロジデータベースから一瞬で引き当て、直接相手のENIへとパケットを送り届けます。
この一連のプロセスにおいて、途中に存在するサブネットの境界(ルーターのホップ数)は、パケットのTTL(Time to Live)を減算させません。VPC内の通信は、論理的に「単一の巨大なL2スイッチ空間(あるいはダイレクトなL3網)」のように高速かつ低遅延で処理されます。
—
3. 実務で役立つ!コードと設定のサンプル
インフラ設計やWeb APIのクライアント実装において、このローカルルートの挙動を前提としたコードを書く際のポイントを見ていきましょう。
A. Python (requests) による同一VPC内APIコール
マイクロサービス間で通信を行う際、サービスディスカバリーやKubernetesのCoreDNS経由で解決されたプライベートIPに対してリクエストを送る典型的なコードです。
import requests
from requests.exceptions import RequestException
# 同一VPC内にデプロイされたバックエンドAPIのプライベートIP(または内向きロードバランサーのIP)
# この通信はインターネットに出ることなく、VPCの local ルートを通って直接配送されます。
API_ENDPOINT = "http://10.0.2.50:8080/health"
def check_backend_health():
try:
# タイムアウトを適切に設定し、VPC内通信であってもハンギングを防ぐ
response = requests.get(API_ENDPOINT, timeout=3.0)
# ステータスコードのチェック
if response.status_code == 200:
print(f"[SUCCESS] バックエンドは正常です: {response.json()}")
else:
print(f"[WARNING] 予期せぬステータスコード: {response.status_code}")
except RequestException as e:
# 現場の教訓: 「VPC内だから絶対に繋がる」と思い込まず、必ず例外処理を入れること
# セキュリティグループ(SG)のミスや、ENIの枯渇などで通信が失敗することは普通にあります。
print(f"[ERROR] バックエンドへの接続に失敗しました: {e}")
if __name__ == "__main__":
check_backend_health()
B. Kubernetes (ClusterIP / Headless Service) との関連性
KubernetesをAWS(EKS)上で運用している場合、Pod間通信には kube-proxy や eBPFベースのCiliumなどが使われますが、ノード間を跨ぐPod通信やAWSのENIを直接割り当てるCNI(Amazon VPC CNI)を使用している場合、パケットのルーティングは完全にAWSのVPCルートテーブル(ローカルルート)に依存します。
以下は、同一VPC内の別のシステムから、K8sクラスター内のサービスにアクセスするためのKubernetesマニフェスト(LoadBalancer / 内部向け)の例です。
apiVersion: v1
kind: Service
metadata:
name: internal-api-service
namespace: production
annotations:
# AWS環境で内部専用のNLB(Network Load Balancer)をVPC内に作成するアノテーション
service.beta.kubernetes.io/aws-load-balancer-internal: "true"
service.beta.kubernetes.io/aws-load-balancer-type: "external"
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "instance"
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
selector:
app: backend-api
このNLBに割り当てられたプライベートIP(例: 10.0.3.15)宛てのトラフィックも、VPC内のクライアントからは local ルートによってダイレクトにルーティングされます。
—
4. 現場のトラブルシューティング:ローカルルート周辺でハマる「3つの罠」
最後に、数々の現場で私を悩ませ、そして解決に導いてきた「VPC内通信のトラブルシューティングTIPS」を共有します。もし「同じVPC内なのに通信できない!」という事態に陥ったら、以下の順番で疑ってください。
1. セキュリティグループ(Security Group)のステートフルな誤解
「ローカルルートで直接届くんだから、相手側のセキュリティグループは適当でも……」というのは大間違いです。
VPC内通信であっても、送信元のセキュリティグループ(アウトバウンド)と宛先のセキュリティグループ(インバウンド)の両方が許可されていなければ、パケットはハイパーバイザーの壁で容赦なくドロップされます。
特に、デフォルトのアウトバウンド(全許可)を閉じている環境では、このミスが多発します。
2. ネットワークACL(NACL)のステートレスな双方向ブロック
NACLはステートレス(状態を保持しない)です。ルートテーブルの local ルートで往路が通っても、復路(Return Traffic)のためのNACLエントリがインバウンド・アウトバウンド共に正しく許可されていないと通信は成立しません。
サブネットをまたぐ通信のデバッグでは、必ずNACLの番号とルール(特にハイ&ローのエントリ評価順序)を確認してください。
3. CIDRの重複・オーバーラップの呪縛
マルチVPC構成(VPCピアリングやAWS Transit Gatewayを利用する環境)において、最も恐ろしいのが「CIDRの重複」です。
例えば、VPC A (10.0.0.0/16) と VPC B (10.0.0.0/16) の間でピアリングを結ぼうとしたり、あるいはオンプレミス環境のネットワークセグメントとVPCのCIDRが部分的に被っている場合、ルートテーブルの最優先評価(Most Specific Route)や local ルートの挙動により、パケットが意図しない方向へ吸い込まれるルーティング迷子が発生します。
「VPCのCIDR設計は、組織全体で厳格に一意に保つ」――これはシニアアーキテクトからの血の滲むような教訓です。
—
まとめ
VPCの「ローカルルート(ターゲット:local)」は、私たちが普段意識することのないクラウドの深層で、驚異的な速度と効率でパケットを宛先へと導き続けています。
しかし、その「見えない便利さ」に甘えていると、セキュリティグループの迷宮や、複雑なマルチVPC環境でのルーティング衝突に足元をすくわれることになります。
「パケットが今、どのルートテーブルのどのエントリに導かれ、どのENIを叩いているのか」――その頭の中のメンタルモデルを常に鮮明に保ちながら、堅牢で美しいクラウドネットワークを設計・運用していきましょう。
それでは、また次回のインフラ深掘り記事でお会いしましょう!
コメント