🦓

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 のポート数上限に抵触してデータベース接続が失敗する事象がおきました。

  1. エラーについて
  2. 仮説を立てる
  3. 解決策
  4. 改善前後の負荷テストで動作確認をする
  5. なぜこの問題は広く知られていないのか
  6. まとめ

エラーについて

問題の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が問題を解決する理由

  1. トラフィックの分離

外部API呼び出し:

  • VPC → Cloud NAT → インターネット → 外部API
  • Cloud NATの静的IPを使用 ✅

Cloud SQL接続:

  • VPC → Cloud SQLのプライベートIP(VPC内部)
  • Cloud NATを経由しない ✅
  1. 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

  1. NATゲートウェイへの負荷上昇と同時にDB接続の中断を確認
  2. NATのロギングをオンにし、ログでコネクションがドロップされたことを確認

プライベートIP

  1. プライベートIPアドレスに切り替えることで、NATを経由せずDBへ直接接続されることを確認

【検証結果】
「Cloud FunctionsがCloud SQL Auth Proxyではなく、VPCとCloud NAT経由でCloud SQLへ接続」という仮説を実証することができました。

なぜこの問題は広く知られていないのか

  1. Googleのドキュメントギャップ
  • 各サービスの個別ドキュメントは充実
  • サービス間の相互作用についての説明が不足
  • 「すべてのトラフィックをVPCにルーティング」の影響範囲が明示されていない
  1. 発見が遅れる要因
  • 開発環境では問題が顕在化しない
  • 本番のアクセス集中時に初めて発覚
  • エラーメッセージが汎用的("connection timeout")
  1. 公式の既知の問題として記載
    Googleの公式ドキュメント「Known issues in Cloud Functions」に、この問題が記載されていた形跡がありました。

https://stackoverflow.com/questions/74724146/experiencing-random-timeout-between-cloud-Functions-and-cloud-sql-using-vpc-connector

まとめ

本記事で解説した問題は、Cloud Functionsの仕様を正しく理解していれば避けられますが、直感に反する動作のため、以下の条件に当てはまる場合、本記事の解決策を適用してみてください。

✅ Cloud Functionsを使用している
✅ Cloud SQLを使用している
✅ 外部API接続で静的IPが必要
✅ 「すべてのトラフィックをVPCにルーティング」を使用

同じ問題で困っている方の助けになれば幸いです。

https://cloud.google.com/sql/docs/mysql/connect-functions?hl=ja
https://cloud.google.com/sql/docs/mysql/connect-instance-auth-proxy?hl=ja
https://cloud.google.com/sql/docs/mysql/connect-auth-proxy?hl=ja
https://cloud.google.com/sql/docs/mysql/best-practices?hl=ja
https://cloud.google.com/vpc/docs/configure-serverless-vpc-access?hl=ja
https://cloud.google.com/vpc/docs/serverless-vpc-access?hl=ja

Discussion