Cloud Functionsで全トラフィックをVPC経由にした際のCloud SQL接続失敗問題と解決策
はじめに
Cloud Functions 上のアプリケーションで外部APIとCloud SQLの両方に接続する際に発生する、意図しないトラフィックルーティングの問題とその解決策についてまとめてみました。
特に、クレジットカード決済APIなどIPホワイトリストが必要な外部サービスを利用する場合に遭遇する可能性が高い問題です。
tl;dr
Cloud Functions 上のアプリケーションから Cloud SQL Auth Proxy を経由して Cloud SQL への接続する際に 、意図せず通信が Cloud NAT を経由してしまったことにより、Cloud NAT のポート数上限に抵触してデータベース接続が失敗する事象がおきました。
- エラーについて
- 仮説を立てる
- 解決策
- 改善前後の負荷テストで動作確認をする
- なぜこの問題は広く知られていないのか
- まとめ
エラーについて
問題のWebアプリケーションでは、以下の両方の接続が必要になります:
1. 外部API接続(例:クレジットカード決済、SMS送信など)
- セキュリティのため、接続元IPアドレスのホワイトリスト登録が必要
- Cloud Functionsは通常、インスタンスごとに異なるIPアドレスを使用
- 静的IPアドレスが必要 → Cloud NATを使用
2. Cloud SQL接続(データベース)
- アプリケーションデータの永続化
- Cloud SQL Auth Proxyを使用した認証
開発者が期待する動作:
- 外部API呼び出し → Cloud NATを経由(静的IP提供のため)
- Cloud SQL接続 → Auth Proxyを使用してパブリックIPで直接接続
しかし、実際には:
外部API呼び出し → Cloud NATを経由 ✅
Cloud SQL接続 → Cloud NATを経由 ❌
エラーメッセージ例:
TCP Connection Error: connection to Cloud SQL instance at {Cloud SQLインスタンスのパブリックIP}:3307 failed: connect tcp ... connection was refused
Unix Socket Error: Error: connect ECONNREFUSED /cloudsql/xxx-xxx:asia-northeast1:xxx-xxx-xxx
3. 発生タイミング
- 開発環境や低負荷時:問題なく動作
- 負荷テストやアクセスが集中した本番環境:接続タイムアウトが頻発
4. Cloud Functionsのトラフィックルーティング設定
Cloud FunctionsでServerless VPC Accessを使用する際、以下の2つの設定があります:
オプション1:プライベートIPのみをVPCにルーティング
- RFC 1918およびRFC 6598のIPアドレス範囲のみVPC経由
- その他のトラフィックは直接インターネットへ
- 推奨される設定(ほとんどのケース)
オプション2:すべてのトラフィックをVPCにルーティング
- すべてのアウトバウンド通信をVPC経由
- Cloud NATを使用して静的IP提供が可能
- Cloud SQLへの接続も含まれてしまう

仮説を立てる
静的IPが必要な外部API接続のため 「すべてのトラフィックをVPCにルーティング」 を選択すると、Cloud Functionsは仕様として例外なく全通信をVPCへ送信します。
Cloud SQL Auth Proxyを使用していても、接続先がパブリックIPアドレスである限り、この通信もVPC(およびCloud NAT)を経由してしまいます。
解決策
Cloud SQLをプライベートIPで接続
- Cloud SQLインスタンスにプライベートIPを設定
- VPCコネクタを指定:「すべてのトラフィックをVPCにルーティング」を維持
- 接続ホストを変更:Cloud SQLのプライベートIPを使用
プライベートIPが問題を解決する理由
- トラフィックの分離
外部API呼び出し:
- VPC → Cloud NAT → インターネット → 外部API
- Cloud NATの静的IPを使用 ✅
Cloud SQL接続:
- VPC → Cloud SQLのプライベートIP(VPC内部)
- Cloud NATを経由しない ✅
- VPC内部通信の仕組み
- プライベートIPアドレスへの接続は、Google CloudのVPC内部ネットワークを使用します:
- Cloud NATはインターネット向けトラフィック用
- VPC内部の通信はNATを必要としない
- より低レイテンシ、高パフォーマンス
改善前後の負荷テストで動作確認をする
パブリック接続とプライベート接続それぞれで、特定のAPIに対して負荷テストを実施し、接続エラーが改善されたことを確認しました。
改善前
テスト結果:

エラー発生状況:

データベースエラーログ:
Cloud SQL connection failed. Please see
https://cloud.google.com/sql/docs/mysql/connect-Functions
for additional details: connection to Cloud SQL instance
at {Cloud SQLインスタンスのパブリックIP}:3307 failed:
timed out after 10s
NATゲートウェイオープンコネクション数:320

NATエラーログ
- NAT IPアドレス/ポートの割り当てに失敗
- 接続がドロップされた
DEFAULT 2025-07-26T11:04:47.333559693Z {"allocation_status":"DROPPED", "connection":{…}, "endpoint":{…}, "gateway_identifiers":{…}, "vpc":{…}}
DEFAULT 2025-07-26T11:04:47.333804489Z {"allocation_status":"DROPPED", "connection":{…}, "endpoint":{…}, "gateway_identifiers":{…}, "vpc":{…}}
DEFAULT 2025-07-26T11:04:47.355464562Z {"allocation_status":"DROPPED", "connection":{…}, "endpoint":{…}, "gateway_identifiers":{…}, "vpc":{…}}
改善後
テスト結果:

エラー発生状況:

NATゲートウェイオープンコネクション数:79

【確認できた事象】
パブリックIP:
- NATゲートウェイへの負荷上昇と同時にDB接続の中断を確認
- NATのロギングをオンにし、ログでコネクションがドロップされたことを確認
プライベートIP:
- プライベートIPアドレスに切り替えることで、NATを経由せずDBへ直接接続されることを確認
【検証結果】
「Cloud FunctionsがCloud SQL Auth Proxyではなく、VPCとCloud NAT経由でCloud SQLへ接続」という仮説を実証することができました。
なぜこの問題は広く知られていないのか
- Googleのドキュメントギャップ
- 各サービスの個別ドキュメントは充実
- サービス間の相互作用についての説明が不足
- 「すべてのトラフィックをVPCにルーティング」の影響範囲が明示されていない
- 発見が遅れる要因
- 開発環境では問題が顕在化しない
- 本番のアクセス集中時に初めて発覚
- エラーメッセージが汎用的("connection timeout")
-
公式の既知の問題として記載
Googleの公式ドキュメント「Known issues in Cloud Functions」に、この問題が記載されていた形跡がありました。
まとめ
本記事で解説した問題は、Cloud Functionsの仕様を正しく理解していれば避けられますが、直感に反する動作のため、以下の条件に当てはまる場合、本記事の解決策を適用してみてください。
✅ Cloud Functionsを使用している
✅ Cloud SQLを使用している
✅ 外部API接続で静的IPが必要
✅ 「すべてのトラフィックをVPCにルーティング」を使用
同じ問題で困っている方の助けになれば幸いです。
Discussion