オンプレミスからGCPへ、そしてインターネットへ。Cloud NATとInterconnectで構築する「出口戦略」の深淵
こんにちは。日々、クラウドの設計図と格闘し、夜中にパケットの海を泳いでいるSREです。
ハイブリッドクラウドの設計において、避けて通れないのが「オンプレミスからのインターネットアクセス」という命題です。特にGCPの Dedicated Interconnect や Partner Interconnect を導入した際、オンプレミスのサーバーから外部のSaaS APIを叩くために、「わざわざオンプレミス側にプロキシを置くべきか? それともGCPを経由させるべきか?」と頭を抱えた経験はないでしょうか。
今回は、GCPの Cloud NAT を活用して、Interconnect越しにやってくるオンプレミスのトラフィックを、まるでGCP上のリソースのように「パブリックIPへ変換して外に出す」という、通好みなネットワーキングの裏側を解説します。
—
なぜ、わざわざGCP経由で外に出すのか?
通常、オンプレミスの通信は自社のエッジルーターを経由して直接インターネットへ出ます。しかし、セキュリティポリシーが厳しいエンタープライズ環境では、インターネットへの出口を特定の「ゲートウェイ(GCP)」に一元化し、そこに Cloud Armor や Cloud IDS といったGoogleの強力な防御層を適用したいという要望が頻出します。
ここで重要になるのが、GCPのネットワーク(VPC)を通過するプライベートIPのパケットを、どうやってインターネット側で認識可能なパブリックIPに変換するかという点です。
パケットの旅:オンプレからインターネットまで
通信のフローは以下の通りです。
1. オンプレミス: サーバーが curl 等で外部APIへリクエスト。宛先はプライベートIPではなくインターネット上のホスト。
2. Interconnect: 通信が Cloud Router 経由でGCP VPC内へルーティングされる。
3. VPC内: 宛先がインターネットのため、Cloud NAT のルールが適用される。
4. Cloud NAT: 送信元プライベートIPを Cloud NAT に割り当てられたパブリックIP(NAT IP)に書き換える(SNAT)。
5. インターネット: 変換後のパブリックIPとしてリクエストが届く。
この仕組みの肝は、「VPC内のプライベートサブネットに属さないIP(オンプレのIP範囲)であっても、Cloud NATの Subnet-based NAT 設定を工夫すれば通過させられる」という点です。
—
実践:Cloud NATの設定とデバッグの勘所
まず、gcloud コマンドでCloud NATを構築する際の、実務的なポイントを押さえておきましょう。
# Cloud NATの作成(オンプレのIPレンジを明示的に含めることが重要)
gcloud compute routers nats create nat-config \
--router=my-cloud-router \
--region=asia-northeast1 \
--nat-all-subnet-ip-ranges \
--nat-external-ip-pool=NAT_IP_ADDRESS \
--enable-logging # トラブルシューティングには必須のログ有効化
現場で死ぬほど役に立つTips:ログの確認
通信がうまくいかない場合、まず疑うのは「そもそもパケットがNATの対象になっているか」です。Cloud Logging で以下のクエリを叩き、パケットが DROP されていないか確認しましょう。
resource.type="nat_gateway"
jsonPayload.error="NAT_IP_PORT_EXHAUSTION" # ポート枯渇は定番の障害
もしポート枯渇が発生しているなら、--min-ports-per-vm を増やすか、--endpoint-independent-mapping を検討してください。ただし、後者はセキュリティ的に緩くなる側面もあるため、設計判断が必要です。
—
オンプレミスからの疎通確認コード
オンプレミス側のサーバーで、GCPのNAT経由で外に出ているかを確認するためのPythonスクリプトです。ifconfig.me 等のサービスを利用して、返ってくるIPアドレスが「自社のグローバルIP」ではなく「GCPのNAT用IP」になっているかを確認します。
import requests
def check_egress_ip():
# 外部のIP確認APIを叩く
# GCPのCloud NATを経由していれば、GCPのNAT IPが返ってくるはず
try:
response = requests.get('https://ifconfig.me', timeout=5)
print(f"現在のグローバルIPアドレス: {response.text}")
except requests.exceptions.RequestException as e:
print(f"通信失敗: {e}")
if __name__ == "__main__":
check_egress_ip()
—
アーキテクトからの助言:ハマりどころと注意点
最後に、現場で泣きを見ないための注意点をいくつか。
- ルーティングの優先順位: オンプレミスからGCPに向かう際、VPC内のルートテーブルで「デフォルトルート(
0.0.0.0/0)」が適切にCloud Routerを経由しているか確認してください。ここが疎通していないと、パケットはVPCにすら到達しません。 - MTUの不一致: InterconnectのMTUとオンプレのMTUが一致していないと、パケットが分割され、API通信でタイムアウトが多発します。
1460バイト以下に抑えるのが鉄則です。 - 非対称ルーティング: 戻りパケットがInterconnectを通らず、オンプレ側の直接回線から帰ってくると、ファイアウォールでドロップされます。ステートフルなNATの挙動を理解し、必ず通信が往復でGCPを通るように設計しましょう。
クラウドのネットワークは目に見えません。だからこそ、CLIから得られる情報と、パケットが通る道のりを頭の中で正確にトレースできる能力が、シニアエンジニアとしての「武器」になります。
皆さんのハイブリッドクラウド環境が、今日も健やかに安定稼働することを願っています。何か詰まったら、まずは tcpdump ではなく Cloud NAT Logging を信じてみてください。案外、答えはそこに転がっているものです。
コメント