Cloud RunからCloud SQLへの通信がCloud NATを経由してしまう事象とその解決策

記事タイトルとURLをコピーする

G-gen の佐々木です。当記事では Cloud Run 上のアプリケーションで Cloud SQL Auth Proxy を使って Cloud SQL へ接続する際に接続が失敗した事象と、その対策について解説します。

はじめに

Cloud Run 上のアプリケーションから Cloud SQL への接続に Cloud SQL Auth Proxy を利用している状況において、意図せず通信が Cloud NAT を経由してしまったことにより、Cloud NAT のポート数上限に抵触して DB 接続が失敗する事象がおきました。

その原因と対策について解説します。

前提知識

各種サービスについて

当記事内に出てくる各種サービスの内容については説明を割愛します。
それぞれ以下の記事で詳細に解説されているので、こちらをご一読ください。

■ Cloud Run blog.g-gen.co.jp

■ Cloud SQL blog.g-gen.co.jp

■ Cloud NAT blog.g-gen.co.jp

Cloud Run から Cloud NAT を使用するケース

Cloud Run ではサーバーレスの仕様により、処理を行っていないときはインスタンスを終了してインフラリソースを解放してしまうため、インスタンスが使用する IP アドレスは都度変わってしまいます。

Cloud Run 上のアプリケーションからアクセス元 IP を制限するような外部サービスに接続する必要がある場合には、Cloud Run からインターネットへのアウトバウンド接続は Cloud NAT を経由するようにすることで、接続元 IP アドレスを Cloud NAT の静的 IP アドレスに固定できます。

Cloud Run から Cloud NAT のような VPC リソースにアクセスするためには、サーバーレス VPC アクセスを構成する必要があります。詳細は以下の記事で解説しています。

blog.g-gen.co.jp

事象

発生した事象

ある Cloud Run 上のアプリケーションは、Cloud NAT を経由して外部サービスの API にアクセスする必要があります。外部サービス側で接続元 IP アドレスをホワイトリスト制限しているため、Cloud NAT を経由することで接続元 IP を固定したいためです。

またそのアプリケーションはデータベースとして Cloud SQL を利用しており、Cloud SQL への認証をシンプルにするため Cloud SQL Auth Proxy を使っていました。

このアプリにおいて、リリースに向けた負荷試験を実施したとき、以下のようなエラーメッセージが出力されました。

"Cloud SQL connection failed. Please see https://cloud.google.com/sql/docs/mysql/connect-run for additional details: connection to Cloud SQL instance at {Cloud SQLインスタンスのパブリックIP}:3307 failed: timed out after 10s"

想定していた通信経路

この構成で当初想定していた通信経路は以下のようなものでした。

Cloud Run から Cloud SQL と Cloud NAT に接続する構成(想定しているもの)

サーバーレス VPC アクセスのコネクタインスタンスは、負荷によってスケールアウトすると、その後一切スケールインできない仕様があります(スケールインするためには作り直しが必要です)。

そのためサーバーレス VPC アクセスは外部 API へのアクセスのみで使用し、Cloud SQL への接続は Cloud SQL Auth Proxy を使用してパブリック IP で接続したいと考えていました。Cloud SQL Auth Proxy を使うと接続先は Cloud SQL のパブリック IP アドレスとなるため、直感的には Cloud Run から Cloud SQL への接続は VPC (サーバーレス VPC アクセス) を通らず、直接 Cloud SQL に届くようなイメージでした。

実際の通信経路

しかしその想定は、実際には異なっていました。前述のエラーメッセージが出力されたことから詳細に調べた結果、実際の通信経路は以下のようなものでした。

実際の通信経路

Cloud Run から Cloud SQL にパブリック IP で直接接続してほしかったのですが、実際にはサーバーレス VPC アクセスのコネクタインスタンスと Cloud NAT を経由して接続されていました。

これにより、想定していなかった負荷がコネクタインスタンスや Cloud NAT にかかってしまい、コネクタインスタンスのリソース不足や Cloud NAT のポート数制限などがデータベース接続に影響を及ぼしていました。

原因

Cloud SQL への通信がサーバーレス VPC アクセスと Cloud NAT を経由してしまう原因は、Cloud Run から送信されるトラフィックのルーティングの仕様にあります。

Cloud Run でサーバーレス VPC アクセスを使用する際、どのトラフィックをサーバーレス VPC アクセスコネクタに送信するかを設定する項目があります。外部 API のようなパブリック IP へのリクエストを Cloud NAT 経由で行うには、ここで「すべてのトラフィックを VPC にルーティングする」を選択する必要があります。

Cloud Run のアウトバウンド通信をすべてサーバーレス VPC アクセスコネクタに送信する設定

これにより、Cloud Run は全てのアウトバウンド通信を VPC へルーティングしてしまい、よって Auth Proxy を使った Cloud SQL への通信であっても、Cloud NAT 経由となってしまったのでした。

Cloud Run の仕様からすると当然の挙動なのですが、Cloud SQL Auth Proxy を使うことで Cloud SQL に直接接続すると直感的に感じてしまったことが、原因へ気がつくことを遅らせました。

解決策

仕様上、Cloud SQL に対する通信だけを Cloud NAT を使わない直接通信とすることはできません。

妥協案となってしまいますが、すべての通信がサーバーレス VPC アクセスコネクタを経由することは許容し、プライベート IP を使用して Cloud SQL に接続することで、データベース接続が Cloud NAT を経由してしまうことを防ぐことができます。

これにより、少なくとも Cloud NAT の上限に抵触することでデータベース接続が失敗してしまうような状況は回避できます。

Cloud SQL への接続にプライベート IP を使用し、Cloud NAT を経由しないようにする

余談

Cloud NAT 同様、Cloud Run からの接続にサーバーレス VPC アクセスが必要となるサービスとして Memorystore などがありますが、こちらはプライベート IP 接続以外の選択肢がないため、Cloud Run で「プライベート IP へのリクエストのみを VPC にルーティングする」の設定がされていれば、Cloud SQL への接続はパブリック IP で行うことが可能です。

プライベート IP へのアウトバウンド接続のみサーバーレス VPC アクセスコネクタに送信する設定

佐々木 駿太 (記事一覧)

G-gen最北端、北海道在住のクラウドソリューション部エンジニア

2022年6月にG-genにジョイン。Google Cloud Partner Top Engineer 2024に選出。好きなGoogle CloudプロダクトはCloud Run。

趣味はコーヒー、小説(SF、ミステリ)、カラオケなど。