Cloud RunからCloud SQLへのプライベート接続を試してみた
こんにちは!
コンサルティング部のみゃんです。
すっかり涼しくなって長袖が必要になってきましたね。
夏は暑くて外を歩くのもうんざりしてしまいますが、秋は涼しくて歩いていても気分が良いです。
せっかく過ごしやすくなってきて、心のゆとりができたので今日はGoogle Cloudについてキャッチアップも兼ねてハンズオンをやってみました!
私はAssociate Cloud EngineerというGoogle Cloudの認定資格を持っていますが、実務でのGoogle Cloudの経験はほぼないという状態です。(これまでは主にAWSをメインとした技術支援を行ってきました。)
最近はGoogle Cloudのご相談をいただくことも増えており、私自身がGoogle Cloudを今後ガンガンやっていきたいと思っているので、これからはGoogle Cloudのやってみたブログも執筆していきます!!!
そんな記念すべき最初のブログのテーマに選んだのは、
「Cloud RunからCloud SQLへのプライベート接続を試してみた」です!
理由は資格試験の勉強中に、単純に気になったサービスだからです笑
Cloud RunもCloud SQLもキーワードとしてよく目にするので、この機会に実際に触ってみて理解を深めていきたいと思います。
それではやっていきましょう!
まずは結論から
結論としては、躓くことなくコンソール操作でCloud Runからプライベート通信でCloud SQLのDBへ書き込みすることに成功しました。
今回はプライベート通信のために、「プライベートサービスアクセス」を使用しました。
またDBへはIAM認証を使用しています。
本ブログでは、構築手順もコンソールの画面付きで1つ1つ記載しています。
また検証後、物足りなくてわざと環境を壊してみてどうなるかの実験も3つ行っていますので、是非最後までお付き合いいただけると嬉しいです!
最後まで読んでいただくと、曖昧だった「Cloud Run」や「Cloud SQL」、そしてGoogle Cloudのネットワークの仕組み、「プライベートサービスアクセス」等について解像度が上がり、日々の設計判断等にお役立ていただける状態を目指して書きました。
やってみた
前提
- 構築方法: 初めてなのでコンソールからぽちぽちと
- 単一のプロジェクト内で構築
asia-northeast1に構築- 詳細なサービスやGoogle Cloudの概念に対する解説はせず、ハンズオンとして手を動かした内容にフォーカスしてお届け
構成
今回の構成概要図はこちらです。

Cloud RunからCloud SQLまでプライベートな通信経路を確保するために、VPCネットワークとサブネットを作成します。
そして、Cloud RunからDirect VPC egressとして作成したサブネットを紐づけます。
また作成したVPCネットワークとCloud SQLが起動するGoogle管理のVPCとの間に、「プライベートサービスアクセス」を設定します。
それではやっていきましょう!
Step1: APIの有効化
釈迦に説法ですが、Google CloudではAWSと違い、プロジェクトごとに使うAPIを明示的に有効化する必要があります。
コンソールのメニューから「API とサービス」「ライブラリ」を開き、次の6つを有効化します。
| API | 用途 | 根拠 |
|---|---|---|
| Cloud SQL Admin API | インスタンスの管理。コネクタも使う | connect-run の Step 1 |
| Service Networking API | プライベートサービスアクセスの作成 | private-ip の「API and IAM requirements」 |
| Compute Engine API | VPC とサブネットの作成 | vpc-direct-vpc の gcloud 手順 に gcloud services enable compute.googleapis.com の記載あり |
| Cloud Run Admin API | Cloud Run のデプロイ | Cloud Run のデプロイ |
| Cloud Build API | ソースからコンテナをビルドする | ソースからのデプロイ |
| Artifact Registry API | ビルドしたイメージの保存 | 同上 |
コンソール画面の左側のバーから「APIとサービス」を選択し、「有効なAPIとサービス」を押下します。

「APIとサービスを有効にする」を押下します。

検索窓で有効にするAPI名を検索します。
画像では例で「Cloud SQL Admin API」を検索しています。

「有効にする」を押下します。

「ステータス」が「有効」なことを確認できました。

コンソールからではなく、Cloud Shellでまとめて有効化することもできます。
残りの部分はCloud Shellで有効化します。
コマンドはこちらです。
gcloud services enable \
servicenetworking.googleapis.com \
compute.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com
右上に「Cloud Shell」起動のボタンがあります。

「承認」を押下します。

ターミナルが開いたので、コマンドを実行します。
実行から1,2分で正常に応答が返ってきました。

「Service Networking API」が有効化されています。

「Compute Engine API」が有効化されています。

「Cloud Run Admin API」が有効化されています。

「Cloud Build API」が有効化されています。

「Artifact Registry API」が有効化されています。

無事全てのAPIの有効化ができました。
それでは構築作業に入りましょう。
Step2: VPC ネットワークとサブネットを作る
まずVPCネットワークとサブネットを作成します。この辺りはAWSと似ていますね。
違うのはCIDRをVPC全体に割り当てず、サブネットごとに決める点かなと思います。
コンソールのメニューから「VPCネットワーク」を開きます。

「VPCネットワークを作成」を押下します。

次の値を入力していきます。
- 名前:
handson-vpc - サブネット作成モード: カスタム
「新しいサブネット」に次の値を入力していきます。
- 名前:
handson-run-subnet - リージョン:
asia-northeast1 - IPv4 範囲:
10.10.0.0/26 - プライベート Google アクセス: オフ
- Flow ログ: オフ



「新しいサブネット」の「完了」をクリックし、続けて1番下の「作成」を押下します。

ところで画面にあるMTUとは何でしょうか。
MTU(Maximum Transmission Unit/最大伝送単位)とは、ネットワークが一度に転送できる最大IPパケットサイズです。単位はバイトで、IPヘッダー、TCP・UDPなどのL4ヘッダー、実データを含みます。
Cloud Runのネットワーキングのベストプラクティスでは、MTU設定を変更せず、デフォルトのMTU(1,460バイト)を使用するとあるので、特に設定は変えません。

今回サブネットのアドレス範囲を/26にしている理由としては、公式ドキュメントによると、Cloud RunはIPを16個単位のブロックで確保するため、Cloud Runが使用できる十分なIPv4アドレスを確保するために、サブネットのアドレス範囲を/26以上にすることが必要とされているからです。
正常にVPCネットワークとサブネットが作成されていることが確認できました。
handson-vpcの詳細画面にサブネットhandson-run-subnetが10.10.0.0/26で並んでいますね。

Step3: プライベートサービスアクセスを設定する
続いて、プライベートサービスアクセスを設定していきます。
今回構築するCloud SQLは自前のVPCではなく、Google管理のVPCに作成されます。AWSのRDSとは違いますよね。
そのため、今回Cloud Runからプライベート通信で接続するために、プライベートサービスアクセスが必要となります。
プライベートサービスアクセスを使用すると、自前のVPCとGoogle管理のVPC(Google Cloud サービス プロデューサーの VPC ネットワーク)の間にプライベートな接続を作成することができます。
それでは作っていきます。
先ほど作成したVPCネットワークの詳細画面から、「プライベートサービスアクセス」>「サービスに割り振られたIP範囲」>「IP範囲の割り振り」を押下します。

次の値を入力します。
- 名前:
cloudsql-psa-range - IP 範囲: 「カスタム」を選び
10.60.0.0/16
公式ドキュメントに記載のある通り、割り振るIP範囲は最小サイズは/24ですが、推奨サイズは/16です。
今回は検証なので、最小サイズでも良かったのですが、推奨サイズを採用しています。
「割り振り」を押下します。

無事に割り振りが成功しました。

次にプライベート接続を作成します。
同じ画面から、「サービスへのプライベート接続」タブを選び、「接続を作成」を押下します。

先ほど割り当てたcloudsql-psa-rangeを選択して、「OK」を押下します。

「接続」を押下します。

数分待つと、無事プライベート接続が作成されました。

「VPCネットワークピアリング」タブを開くと、servicenetworking-googleapis-comがステータス有効で表示されました。
AWSのVPCピアリングとは違い、リクエスト、承認の操作はなく、Google側で作成されます。

Step4: Cloud SQL インスタンスを作る
次にCloud SQLを作成します。
AWSではRDSを作成するときに、DBサブネットグループを指定しますが、Cloud SQLでは先ほど作成したプライベートサービスアクセスで繋がったVPCを指定する必要があります。
説明するよりも、実際にやってみたほうが早いので、早速やってみましょう。
コンソールのメニューから「Cloud SQL」>「インスタンス」を押下します。

今回はPostgreSQLでやってみます。
「PostgreSQL」タブから「サンドボックスインスタンスを作成」を押下します。
学習向けの低コストなインスタンスが用意されていて助かりますね!

次の値を入力します。
- インスタンス ID:
handson-pg - パスワード:
postgresユーザーのパスワード。Cloud SQL Studio でのログインに使うので控えておきます。パスワードポリシーを設定することもできます。 - データベースのバージョン:
PostgreSQL 18 - リージョン:
asia-northeast1 - ゾーンの可用性: 単一ゾーン

「構成オプションを表示」を展開します。
「接続」を展開し、次を設定します。
- プライベート IP: チェックを入れます。
- パブリック IP: チェックを外します。
- ネットワーク:
handson-vpc - 「プライベート サービス接続が必要です」と出たら Step3が終わっていないということになります。表示された「接続を設定」から設定が必要です。
先ほど作成したプライベートサービスアクセス接続が正常に作成されていることがわかります。

「データの保護」を展開し、次を設定します。(学習用のため。)
- 「自動日次バックアップ」のチェックを外す
- 「インスタンスの削除を防止する」のチェックを外す

「フラグとパラメータ」を展開し、cloudsql.iam_authentication が on になっているかを確認します。
iam-authentication に「This flag is enabled by default if you create the instances using the Google Cloud console」とあり、コンソールから作れば既定でオンになる。無ければ「フラグを追加」から cloudsql.iam_authentication を on で足すとあるため。(create-edit-iam-instances)
ONになっていたので、このまま進めます。

「インスタンスを作成」を押下します。

10分くらい待つと作成が完了しました。
「接続」からプライベートIP接続の内部IPアドレスが割り当てられていることを確認します。
私の環境では10.60.0.3が割り当てられています。

Step5: データベースとテーブルを作る
検証で使うデータベースとテーブルを作成したCloud SQLに作っていきます。
AWSのRDSであれば踏み台EC2等からRDSへ接続するところですが、Cloud SQLではコンソールのCloud SQL Studioを使います。
作成されたhandson-pgの左メニューから「データベース」を選びます。

「データベースの作成」を押下し、データベース名appdbを入力し、「作成」を押下します。

appdbが作成されました。

次にテーブルを作ります。
handson-pgの左メニューから「Cloud SQL Studio」を押下します。

「データベースへのログイン」で次の値を入力します。
- データベース:
appdb - ユーザー:
postgres - パスワード: Step 4 で設定したもの
「認証」を押下します。

appdbに接続できました。

なにこれみやすい。。。
コンソール上からデータベースに接続してGUIで確認、操作できるのめちゃくちゃ便利ですね!
本題じゃないのに思わず感動してしまいました。
では、エディタに次のSQLを貼りつけてテーブルを作成します。
CREATE TABLE visits (
id SERIAL PRIMARY KEY,
note TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
SQLを貼り付けたら、「実行」を押下します。

正常に実行されました。

左のエクスプローラにvisitテーブルが作成され、反映されていることを確認します。

Step6: アプリ用のサービスアカウントを作る
Cloud SQLは一旦完了したので、次にアプリ用のサービスアカウントを作成します。
Cloud SQLへのIAM認証に必要となります。
コンソールのメニューから「IAMと管理」>「サービスアカウント」を押下します。

「サービスアカウントを作成」を押下します。

サービス アカウント名にcr-app-saを入力します。
「作成して続行」を押下します。

「権限」>「ロールを選択」で次の3つを追加し、「続行」を押下します。
Cloud SQL クライアント(roles/cloudsql.client)Cloud SQL インスタンス ユーザー(roles/cloudsql.instanceUser)ログ書き込み(roles/logging.logWriter)

「完了」を押下します。(アクセス権を持つプリンシパルは省略)

作成が完了したことを確認しました。

Step7: サービスアカウントを DB ユーザーとして登録し、権限を与える
Cloud SQLではPostgreSQL側ではなく、Cloud SQL側でユーザーを登録します。
AWSではRDSに踏み台経由で接続して、psqlでCREATE USERしてから、GRANT rds_iam TO ユーザー名を実行してIAM認証を設定するので、ここでも違いがありますね。
handson-pgの左メニューから「ユーザー」を選びます。

「ユーザー アカウントを追加」を押下します。

認証方法で「Google Cloud IAM」を選びます。
「プリンシパル」にcr-app-sa@PROJECT_ID.iam.gserviceaccount.comを入力します。
「追加」を押下します。

ユーザーの一覧にcr-app-sa@PROJECT_ID.iamが表示されました。
公式ドキュメントで案内されている通り、サービスアカウントIDから.gserviceaccount.comを省いた文字列がPostgreSQL 上のユーザー名になります。

このままだとユーザーは何も権限を持っていません。
再度、handson-pgの左メニューから「Cloud SQL Studio」を選び、appdbに postgresでサインインします。
次のSQLを実行します。(PROJECT_ID は自分の値に置き換えてください。)
GRANT SELECT, INSERT ON TABLE visits TO "cr-app-sa@PROJECT_ID.iam";
GRANT USAGE, SELECT ON SEQUENCE visits_id_seq TO "cr-app-sa@PROJECT_ID.iam";

正常に実行されました。

次のSQLで権限が設定されたかを確認します。
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_name = 'visits';
cr-app-sa@PROJECT_ID.iamにvisitsテーブルに対するINSERTとSELECT権限が付与されていることが確認できました。

Step8: アプリのコードを用意する
いよいよCloud Runの準備に入ります。
まずはCloud Runのインラインエディタに貼るコードを用意します。
main.py
import os
import functions_framework
import sqlalchemy
from google.cloud.sql.connector import Connector, IPTypes
_pool = None
def get_pool():
global _pool
if _pool is not None:
return _pool
connector = Connector(refresh_strategy="LAZY")
def getconn():
return connector.connect(
os.environ["INSTANCE_CONNECTION_NAME"],
"pg8000",
user=os.environ["DB_IAM_USER"],
db=os.environ["DB_NAME"],
enable_iam_auth=True,
ip_type=IPTypes.PRIVATE,
)
_pool = sqlalchemy.create_engine(
"postgresql+pg8000://",
creator=getconn,
pool_size=2,
max_overflow=2,
pool_timeout=30,
pool_recycle=1800,
)
return _pool
INSERT_SQL = "INSERT INTO visits (note) VALUES (:note)"
SELECT_SQL = "SELECT id, note, created_at FROM visits ORDER BY id DESC LIMIT 5"
@functions_framework.http
def main(request):
note = request.args.get("note", "hello")
with get_pool().connect() as conn:
conn.execute(sqlalchemy.text(INSERT_SQL), {"note": note})
conn.commit()
rows = conn.execute(sqlalchemy.text(SELECT_SQL)).fetchall()
out = []
for r in rows:
out.append(str(r.id) + "\t" + r.note + "\t" + str(r.created_at))
return "\n".join(out) + "\n", 200, {"Content-Type": "text/plain"}
requirements.txt
functions-framework
cloud-sql-python-connector[pg8000]
SQLAlchemy
Step9: Cloud Run にデプロイする
コードが準備できたのでいよいよCloud Runをデプロイします。
コンソールのメニューから「Cloud Run」を開きます。

「関数を作成」>「Python」を押下します。

次の値を入力します。
- サービス名:
handson-app - リージョン:
asia-northeast1 - ランタイム:
Python 3.14 - 認証: 認証が必要

サービスのスケーリングで最大インスタンス数を 2 にします。

「コンテナ、ネットワーキング、セキュリティ」を展開します。
「コンテナ」タブを開き、「変数とシークレット」のセクションで「変数を追加」を押下して、次の3つを登録します。
INSTANCE_CONNECTION_NAME は PROJECT:REGION:INSTANCE-ID の形式で、インスタンス概要ページの「接続名」からコピーできます。
DB_IAM_USER には .gserviceaccount.com を含めません。
| 名前 | 値 |
|---|---|
INSTANCE_CONNECTION_NAME |
PROJECT_ID:asia-northeast1:handson-pg |
DB_IAM_USER |
cr-app-sa@PROJECT_ID.iam |
DB_NAME |
appdb |

次に「ネットワーキング」タブを開きます。
「アウトバウンド トラフィック用のVPCに接続する」を押下します。
「VPC に直接トラフィックを送信する」>ネットワーク: handson-vpc、サブネット: handson-run-subnetを選びます。
「トラフィック ルーティング」は「プライベート IP へのリクエストのみを VPC にルーティングする」を選びます。

最後に「セキュリティ」タブを開きます。
「サービス アカウント」のドロップダウンから cr-app-sa@PROJECT_ID.iam.gserviceaccount.com を選びます。

1番下の「作成」を押下します。


「作成」の後、コンソールは自動で「ソース」タブに遷移します。
「関数のエントリ ポイント」に main を指定します。
「コード」セクションで main.py を Step 8 の内容に置き換えます。

requirements.txt を Step 8 の内容に置き換えます。

「保存して再デプロイ」を押下します。

ビルドとデプロイに数分かかりましたが、正常にデプロイが完了しました。

Step10: 動作を確認する
構築は完了したので、いよいよ動作を確認していきます。
Cloud Shellを開いて次のコマンドを実行します。
SERVICE_URL はサービス詳細画面に出ている URL に置き換えます。

curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
"https://handson-app-519258106105.asia-northeast1.run.app/?note=first-visit"
次のような出力が返れば成功です。
1 first-visit 2026-09-11 05:12:34.567890+00:00
成功しました。勢い余って2回実行したので、2行になっています。

Cloud SQL Studio で SELECT * FROM visits ORDER BY id; を実行し、行が入っているかを見てみましょう。
勢い余って2回実行したので、2レコード登録されていますね。

Cloud Run の「ログ」タブでエラーが出ていないか見てみます。
画面では、ソースコードのテスト中のエラーが表示されていますね。(2026-09-12 18:49:59)
コード修正後、実行中にエラーが出ていないことがわかります。 (赤枠)

無事にCloud Runからプライベート接続でCloud SQLへの書き込みに成功しました!
わざと壊してみた
高く積み上げた積み木を倒したくなってくるのが少年心というものです。
ちょうど今日娘と積み上げた積み木を倒す遊びをしていました。
そうだ!今回構築した環境もわざと壊してみよう!
躊躇なく壊せるのが、こういったハンズオンの良いところですよね。
早速壊していきます。
実験1: ネットワーク経路を切る
まずはネットワーク経路を切ってみます。
Cloud Run の「ネットワーキング」タブを開き「アウトバウンド トラフィック用の VPC に接続する」のチェックを外して、「差分を表示して再デプロイ」を押下します。

差分を確認し、ダイレクトVPC設定が削除されているので、「変更内容をデプロイ」を押下します。

再デプロイが完了しました。

リクエストがタイムアウトしました。
upstream request timeout

VPCを再設定して、次の実験に備えます。
実験2: トラフィックルーティングを all-traffic に変える
続いてはネットワークのルーティングの設定を変えてみます。
Cloud Runの「ネットワーキング」タブの「トラフィック ルーティング」を「すべてのトラフィックを VPC にルーティングする」に変えて、「差分を表示して再デプロイ」から「変更をデプロイ」を押下します。


またまたタイムアウトしました。
upstream request timeout

Step 2で「プライベート Google アクセス」をオフにし、Cloud NAT も作っていません。この状態ですべてのトラフィックをVPCに流すと、コネクタが 「Cloud SQL Admin API」に到達できなくなります。
Cloud Run ネットワーキングのベストプラクティス に、インターネットに出るには「you must set up Cloud NAT to reach the public internet」、Google API に出るには プライベート Google アクセス が必要だと書かれています。
試しにサブネットの「プライベート Google アクセス」をONにしたらどうなるのでしょうか?
やってみます。
サブネットの「プライベート Google アクセス」をONにします。

リクエストが正常に実行されました。途中の動作確認のため何度か実行しているので、今回は7行目が該当のリクエストです。

AWSでいうVPC エンドポイントのような役割を果たしていることが分かります。
全ての通信がVPCにルーティングされますが、「Cloud SQL Admin API」への通信がサブネットの「プライベート Google アクセス」経由で到達できているということですね。
それでは最後の実験の前に、Cloud Runのネットワーキング設定およびサブネットの「プライベート Google アクセス」の設定は元に戻しておきましょう。


実験3: IAM のログイン権限を外す
最後はIAMのCloud SQLインスタンスへのアクセス権限を削除してみます。
「IAM と管理」>「IAM」で cr-app-sa から Cloud SQL インスタンス ユーザー ロールを削除します。


Cloud Runを再デプロイします。

「新しいリビジョンをデプロイ」を押下します。

リクエストを実行すると、「500エラー」となりました。
500 Internal Server Error: The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application

Cloud SQLのPostgreSQL のログに Cloud SQL IAM user authentication failed for user "..." が出ているかを確認します。
認証エラーのログが出ていることが確認できました。

以上で今回のやってみた、実験は全て完了です!
お疲れ様でした!
ハンズオンなので、リソースの削除は忘れずにやりましょう。
まとめ
今回はCloud Runからプライベートな通信でCloud SQLに接続して、DBの書き込みを行うハンズオンを構築から、動作検証、そしてわざと環境を壊してみた時の挙動の確認を行いました。
一連の流れから、各サービスやパラメータの意味について理解が深まったり、解像度が上がっていれば幸いです。
私自身も知識として知っているサービスやパラメータについても、実際の挙動やログを確認することで、よりどういった場合に採用したい設計要素なのか、設計時にこういうところに気をつけたいなと実際の実務への意識や解像度が上がったと感じています。
具体的には、Cloud SQLのようなリレーショナルデータベースはエンタープライズの現場ですと、顧客情報であったり、取引情報であったり、よりセキュアなアクセス経路の確保が必要な場面が多いと思いますが、今回のハンズオンはまさにそんなユースケースにぴったりだと思います。1つのパラメータの設定や設計を間違えると意図しない通信が発生したり、思い通りに通信ができなくなってしまうということも身にしみて分かりました。
この記事がGoogle Cloudを活用されている方の参考になれば幸いです。
クラスメソッドは、Google Cloudのパートナー企業として、様々な技術支援実績がございますので、お気軽にご相談ください!
以上、コンサルティング部のみゃんでした!
参考リンク




