はじめに:クラウド時代のインフラを支える「VPCピアリング」の光と影
おい、最近またやっちまったんだよ。本番環境のマイクロサービス間通信を切り替えるときに、うっかり「あれ? なんであっちのVPCからこっちのVPCにパケットが届かないんだ?」って冷や汗をかいた。原因をたぐっていったら、やっぱりVPCピアリングの仕様、それも「推移的ルーティング(Transitive Routing)の不在」というお馴染みの罠に綺麗にハマっていたというね。
クラウドを触り始めた頃は、「VPCピアリングを結べば、Googleの超高速なダークファイバー網の上で、どのVPCともプライベートIPで自由に会話できるぜ!」なんて夢見てしまう。だが、現実はそう甘くない。パケットは気まぐれに飛んでくれるわけではなく、GCP(Google Cloud)の厳格なルートテーブルのルールに則って一歩一歩ルーティングされている。
今回は、GCPの「VPCネットワークピアリング」の心臓部である「双方向ルート交換」と、現場で絶対に知っておかなければならない「痛い制限事項(推移的ルーティングの非サポート・IP重複)」について、パケットの挙動や実務で役立つデバッグ手法を交えながら徹底的に解説しよう。
—
1. VPCネットワークピアリングの基本メカニズム
VPCネットワークピアリングは、2つのVPCネットワーク間をGoogleのグローバルプライベートネットワーク経由で直結する機能だ。インターネットを一切経由せず、パケットは完全にGoogleのファブリック内を流れるため、レイテンシーが低く、セキュアで、外部へのエグレス料金も最小限に抑えられる。
ここで重要なのは、VPCピアリングが「トンネル」や「VPNゲートウェイ」ではないという点だ。IPsec VPNのようにパケットをカプセル化するわけでもなければ、専用のハードウェアアプライアンス(仮想ルーター)をデプロイする必要もない。GCPの分散ソフトウェア定義ネットワーク(SDN)である Jupiter が、両方のVPCのルートテーブルを同期させ、仮想的に「隣り合ったネットワーク」としてパケットをルーティングしているのだ。
双方向ルート交換(Route Exchange)の仕組み
VPCピアリングを設定するとき、片方のVPCから「よし、繋ごうぜ」とオファーを出し、もう片方がそれを「受諾(Accept)」すると、自動的に双方向のルート交換が行われる。
[VPC-A (10.1.0.0/16)] <--- (VPCピアリング) ---> [VPC-B (10.2.0.0/16)]
この瞬間、GCPのコントロールプレーンは以下の処理を水面下で実行する。
1. VPC-A のサブネットIPレンジ(例: 10.1.0.0/16)のルート情報が、VPC-B のルートテーブルに自動で書き込まれる。
2. 同様に、VPC-B のサブネットIPレンジ(例: 10.2.0.0/16)のルート情報が、VPC-A のルートテーブルに自動で書き込まれる。
この自動同期のおかげで、ユーザーが手動でカスタムルート(Static Route)をポチポチ追加しなくても、お互いのプライベートIPに向かうパケットが正しく相手側のVPCへ流れるようになるわけだ。
—
2. 実務の現場で直面する「2大制限事項」
さて、ここからが本題だ。仕様書を斜め読みしただけのエンジニアが本番前夜に泣きを見るポイントを2つ紹介しよう。
制限①:推移的ルーティング(Transitive Routing)の非サポート
これが一番の罠だ。例えば、以下のような3つのVPCをハブ&スポーク型で繋ぎたいとしよう。
- VPC-A(共通基盤・踏み台など)
- VPC-B(アプリケーション層)
- VPC-C(データベース層)
ここで、VPC-A と VPC-B をピアリングし、さらに VPC-B と VPC-C をピアリングしたとする。このとき、「VPC-A から VPC-B を経由して、VPC-C に通信できるか?」 という疑問が生じる。
答えは「NO」だ。
[VPC-A] <---> [VPC-B] <---> [VPC-C]
(VPC-A から VPC-C へは直接通信できない!)
GCPのVPCピアリングは推移的(Transitive)ではない。VPC-B は VPC-A からのパケットを VPC-C に転送してくれない。VPC-A から VPC-C に通信したいのであれば、VPC-A と VPC-C の間に直接ピアリングを追加する必要がある。
もし複雑なマルチVPC構成(ハブ&スポーク)で中央のVPCを経由させたい場合は、VPCピアリングではなく Cloud VPN や VPC Network Firewall / Internal HTTP(S) Load Balancer、あるいはマネージドなルーティングアプライアンスを挟むアーキテクチャ設計にする必要があることを覚えておこう。
制限②:IPアドレス範囲の重複(Overlap)とルーティングの衝突
もう一つの悪夢が「CIDRの重複」だ。
クラウド移行期によくあるのが、社内ネットワークや買収した別会社のVPCと統合する際、両方のVPCで同じプライベートIPレンジ(例えば 10.0.0.0/16)を使っているケース。
もし VPC-A (10.0.0.0/16) と VPC-B (10.0.0.0/16) の間でピアリングを結ぼうとすると、GCPはルートの衝突(Route Conflict)を検知し、ピアリングの作成を拒否するか、作成できたとしても該当する重複サブネット宛ての通信がデッドロック(ルーティングループやブラックホール化)を起こす。
対策:カスタムルートのエクスポート/インポート制御
GCPのVPCピアリングでは、特定のサブネットルートだけを交換しないように「カスタムルートのエクスポート(Export Custom Routes)/インポート(Import Custom Routes)」を制御できるが、IPアドレス自体が完全に重複している場合は、そもそもパケットの宛先(Destinations)をIPレイヤーで一意に特定できないため、根本的な設計としてCIDRの設計を見直し、アドレス空間をリナンバー(再割り当て)する必要がある。
—
3. 実践:GCP CLI (gcloud) によるピアリングの構築とルート確認
口ばかりでなく、実際に手を動かしてみよう。ここでは gcloud コマンドを使って、VPC-A と VPC-B の間にピアリングを張る手順を示す。
ステップ1: VPC-A から VPC-B へのピアリング作成
# VPC-A 側から VPC-B へのピアリングを作成し、カスタムルートの自動交換を有効にする
gcloud compute networks peerings create peer-ab \
--network=vpc-a \
--peer-network=vpc-b \
--auto-create-routes=true
ステップ2: VPC-B から VPC-A へのピアリング作成(双方向化)
ピアリングは必ず両方のVPC側からお互いを指し示す(ピアリングの作成と承認)必要がある。
# VPC-B 側から VPC-A へのピアリングを作成
gcloud compute networks peerings create peer-ba \
--network=vpc-b \
--peer-network=vpc-a \
--auto-create-routes=true
ステップ3: ルートテーブルの確認
本当にルートが正しく交換されているかを、gcloud で確認する。これがデバッグの第一歩だ。
# VPC-A から見えるルーティングテーブルを表示し、VPC-BのCIDRが含まれているか確認する
gcloud compute routes list \
--filter="network:vpc-a"
出力結果の中に、次のようなピアリング由来のルート(NEXT_HOP が PEERING になっているもの)が存在すれば成功だ。
NAME NETWORK DEST_RANGE NEXT_HOP PRIORITY
peering-route-to-vpc-b-10-2-0-0 vpc-a 10.2.0.0/16 PEERING 1000
—
4. トラブルシューティング:パケットが通らないときのチェックリスト
「設定したのに、なぜかAPIのレスポンスが返ってこない!」
そんな修羅場でSREが確認すべきポイントを、優先度順にまとめた。
1. ファイアウォールルール(VPC Firewall Rules)の確認
- 意外と忘れがちなのがこれ。VPCピアリングでルートが交換されても、受信側のVPCのファイアウォールが送信元IP(例: VPC-Aのサブネット)からのアクセスをブロックしていれば、パケットは容赦なくドロップされる。
- 対策:受信側VPCに、送信元VPCのCIDRを許可するIngressルールを必ず記述すること。
2. 推移的ルーティングの罠にハマっていないか?
- 「VPC-A -> VPC-B -> VPC-C」の構成になっていないか、もう一度アーキテクチャ図を見直せ。
3. IAMとサービスの有効化
- 組織のセキュリティポリシー(Organization Policy)で、ピアリングの作成が禁止されていないかも要チェックだ。
—
5. アプリケーション層からの疎通確認(Pythonサンプル)
インフラ層の疎通ができたら、アプリケーション層(Web API)から実際にプライベートIP経由でリクエストが飛ぶか確認してみよう。
以下のPythonコードは、VPC-A 内のCompute Engineから、VPC-B 内で稼働する内部HTTP(S)ロードバランサー(またはプライベートVM)のAPIエンドポイントへ HTTP GET リクエストを投げるサンプルだ。
import urllib.request
import urllib.error
import json
# VPC-B 側に存在するサービスのプライベートIPアドレスとエンドポイント
TARGET_API_URL = "http://10.2.0.50:8080/healthz"
def check_cross_vpc_api():
print(f"Connecting to target API via VPC Peering: {TARGET_API_URL}")
try:
# タイムアウトを3秒に設定し、プライベートネットワーク経由でリクエスト送信
req = urllib.request.Request(
TARGET_API_URL,
headers={"User-Agent": "SRE-VPC-Peering-Checker/1.0"}
)
with urllib.request.urlopen(req, timeout=3.0) as response:
status_code = response.getcode()
body = response.read().decode("utf-8")
print(f"[SUCCESS] HTTP Status: {status_code}")
print(f"[RESPONSE] {body}")
except urllib.error.HTTPError as e:
print(f"[ERROR] HTTP Server returned an error: {e.code} - {e.reason}")
except urllib.error.URLError as e:
print(f"[ERROR] Failed to reach the server (Route/Firewall issue?): {e.reason}")
except Exception as e:
print(f"[ERROR] Unexpected exception occurred: {str(e)}")
if __name__ == "__main__":
check_cross_vpc_api()
このスクリプトを実行して URLError(タイムアウトや接続拒否)が出る場合は、ルートの欠落かファイアウォールのブロックを疑うべきだ。
—
おわりに
GCPのVPCネットワークピアリングは、正しく使えば非常に強力でメンテナンスフリーなネットワーク基盤を提供してくれる。しかし、「推移的ルーティングが使えない」「IPアドレスの重複が許されない」という基本原則を無視して設計を進めると、後々大規模なリファクタリングを強いられることになる。
クラウドのネットワークは、画面の向こうで何が起きているか(パケットがどのルートを辿り、どのファイアウォールに検問されているか)を頭の中でイメージできるかどうかが、エンジニアの腕の見せ所だ。
今回の内容が、あなたの次のマルチVPC設計やトラブルシューティングの助けになれば幸いだ。それじゃ、また次の現場で!
コメント