概要
Google Cloud 上にサーバレスで API を立て、その API から RDBMS にアクセスしたい、というケースにおいて、どのような構成で具体的にどう設定すればいいのかを説明します。
先に結論から言うと、API は Cloud Run Functions に置き、DB は Cloud SQL にアクセスするという構成を作ります。
ここで作ったアプリケーションのサンプルは Github にあるのでよければ使ってください。
構成図
今回登場する Google Cloud のサービスは以下です。
Cloud Run Functions とは
Cloud Run Functions は、Google Cloud が提供するサーバーレスのプラットフォームです。コンテナ化されたアプリケーションや関数を、インフラストストラクチャの管理なしにデプロイ・実行できます。
イベント駆動型でスケールし、リクエスト数に応じて自動的にゼロまでスケールイン(利用がないときは課金が発生しない)し、大量のリクエストにも自動でスケールアウトします。ちょっとした API を置いておきたい、という用途にマッチする選択肢だと思います。
通常、Cloud Run Functions はインターネット経由でサービスを提供しますが、内部ネットワークリソースへのアクセスには VPC Access Connector を利用します。
Cloud SQL とは
Cloud SQL は、Google Cloud が提供するフルマネージドなリレーショナルデータベースサービスです。MySQL、PostgreSQL、SQL Server などのデータベースエンジンをサポートしており、データベースのプロビジョニング、パッチ適用、バックアップ、レプリケーション、高可用性といった運用タスクを Google Cloud が自動的に管理します。
データベースをインターネットに公開することなく、VPC(Virtual Private Cloud)内のプライベートIPアドレスでアクセスできる設定にしておくとセキュアです。
VPC Access Connector とは
VPC Access Connector は、Cloud Run Functions、Cloud Functions、App Engine Standard などといったサーバーレス環境が、VPC (Virtual Private Cloud) 内のプライベートIPアドレスを持つリソース(例:Cloud SQL、Compute Engine、Memorystoreなど)に安全にアクセスするためのサービスです。
サーバーレス環境は通常 VPC の外に存在しますが、VPC Access Connectorをデプロイすることで、サーバーレスサービスからのトラフィックをVPC内にルーティングし、プライベートな通信経路を確立します。これにより、インターネット経由のアクセスが不要になり、セキュリティの向上とネットワークレイテンシの削減ができます。サービスアカウントを介した認証と組み合わせることで、きめ細やかなアクセス制御も可能になります。
この記事の前提
- サンプルのコマンドは MacOS を前提に書いていますが、Windows 用に読み替えていただければ問題ないです。
- Google Cloud はすでに登録済みの前提で書きます。
- Google Cloud の課金設定を先にしておかないといけません。今回はその手順はスキップします。
- サンプルのアプリケーションは
Node.js
Express
で作ります。Node.js
の実行環境がある前提で書きます。
Google Cloud の設定
事前準備
Cloud Run Functions へアプリケーションをデプロイするためには、gcloud CLI をインストールする必要があります。以下の手順に従ってインストールしてください。
Homebrew でインストール
-
ターミナルを開き、以下のコマンドを実行して gcloud CLI をインストールします:
brew install --cask google-cloud-sdk
-
インストールが完了したら、以下のコマンドを実行して
gcloud
コマンドが正しくインストールされていることを確認します:gcloud --version
ログインとプロジェクトの設定
最初に Google Cloud へのログインとカレントプロジェクトの設定をしておきます。プロジェクトIDは Google Cloud コンソール上で確認できます。
gcloud auth login
gcloud config set project YOUR_PROJECT_ID
1. VPC ネットワークの作成と設定
1.1 VPC ネットワークの作成
最初に VPC ネットワークを作成します。
個人開発だったら最初からある「default」をそのまま使ってもいいと思います。
1.2 VPC アクセスコネクタの作成
- VPC ネットワーク > サーバーレス VPC アクセス に移動します。
- ここで
Serverless VPC Access API
が有効になっていない場合は先に有効にする必要があります。 - このページに、作成済みのVPCアクセス コネクタの一覧が表示されます。
- 「コネクタを作成」を押下します。
- 以下のように入力して作成します。
- 個人開発なのでインスタンス数やインスタンスタイプは最も安くなる設定にしています。
- ここでサブネットを入力する必要があります。サブネットの指定方法に悩んだら下の「サブネットの決め方」を参考にしてください。
サブネットの決め方
既存のどのサブネットとも被らない未使用の範囲を指定する必要があります。既存のサブネットを確認するには以下のコマンドが便利です。
gcloud compute networks subnets list --network=default
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
あたりのプライベートIPアドレス範囲からサブネットを選ぶのが衝突のリスクが低いと思います。
VPCコネクタが使うIPアドレスはインスタンス数にもよりますが、10〜20個程度あれば十分でしょう。例えば 10.8.0.0/28
(16個のIPアドレス) などと指定します。
An internal error occurred エラーが出るとき
私が最初VPCコネクタを作ろうとしたとき、以下のエラーが出て作成失敗し、しばらくハマりました。
An internal error occurred: VPC Access connector failed to get healthy. Please check GCE quotas, logs and org policies and recreate.
指定したサブネットの範囲が悪かったのかと思いましたが、いろいろ試してもダメでした。
そこでリージョンを「asia-northeast1」から「asia-northeast2」に変えたら上手くいきました。どうやらリージョンによって一時的な問題やリソースの枯渇が発生して、上手くいかないことがあるようです。
もし同様のエラーが出たらまずはサブネットの範囲を調整してみて、それでも解決できなければリージョンを変えることも検討してください。
2. サービスアカウントの作成
Cloud Run Functions が Cloud SQL に接続するための権限を持つサービスアカウントが必要です。
- Google Cloud Console にアクセスします。
- 左側のメニューから 「IAM と管理」 > 「サービス アカウント」 を選択します。
- 「サービス アカウントを作成」 をクリックします。
- 以下の情報を入力します:
-
サービスアカウント名: 任意の名前を入力 (例:
cloud-run-functions-sa
) - サービスアカウントID: 自動生成されるか、任意で指定
- 説明: 任意で入力
-
サービスアカウント名: 任意の名前を入力 (例:
- 「作成して続行」 をクリックします。
- 必要なロールを割り当てます
-
Cloud SQL クライアント
: Cloud SQL インスタンスに接続するための基本的な権限。
-
- 「完了」 をクリックします。
3. Cloud SQL インスタンスの作成と設定
続いて Cloud SQL インスタンスを作成します。コマンドから作成してもいいのですが、最初は Google Cloud Console から作ったほうがわかりやすいと思います。
3.1 Cloud SQL インスタンスの作成
Google Cloud Console にアクセスし、ページ上部の検索バーに「SQL」と入力し、出てきた「SQL」というサービスを押下して Cloud SQL のページに移動します。すると以下のようなページが表示されます。
以下の手順に従ってインスタンスを作成します。
- 「インスタンスを作成」をクリックします。
- データベースエンジンを選択します。ここでは PostgreSQL を使うことにします。
- 人によっては「Cloud SQL の一部の機能を有効にするには、Compute Engine API が必要です。」とメッセージが表示されるので、「APIを有効にする」を押下します。
- 必要情報を入力しインスタンスを作成します。私は個人開発で試したかっただけなので、とにかく安さ重視で以下のように設定しました。
- エディション: Enterprise
- エディションのプリセット: サンドボックス
- データベースのバージョン: PostgreSQL 17
- インスタンスID: sandbox
- リージョン: asia-northeast2(大阪)
- ゾーン: シングルゾーン
- マシン: 共有コア、1 vCPU、
- ストレージ: HDD、10GB、ストレージの自動増量は無効
- 接続: プライベートIPでの接続を有効にする、ネットワークは「default」VPC ネットワークを使用
- ここでついでに Service Networking API を有効にする
- プライベートパスを有効にする
- 自動日次バックアップ無効(通常は有効にすべきと思います)
- その他の設定はデフォルトのままで作成
- インスタンスの作成が開始されます。作成完了するまでに10分くらいかかります。
3.2 データベースとユーザーの作成
Cloud Run Functions が接続するためのデータベースとユーザーを作成します。
- 作成した Cloud SQL インスタンスの詳細ページに移動します。
- 「データベース」タブを選択し、「データベースを作成」をクリックして新しいデータベースを作成します。ここでは「sample」という名前にしました。
- Cloud Run Functions が使用するユーザを作成します。「ユーザー」タブを選択し、「ユーザーアカウントを作成」をクリックして、ユーザー名とパスワードを持つ新しいユーザーを作成します。ここでは「cloud-run-functions」という名前のユーザを作りました。
3.3 シークレットの作成
先ほど作成した「cloud-run-functions」ユーザのパスワードはシークレットに保管することをおすすめします。
- セキュリティ > Secret Manager に移動します。
- ここで
Secret Manager API
がまだ有効になっていない場合は有効にします。 - 「シークレットを作成」ボタンをクリックします。
- 適当な名前をつけ、先ほど作成した DB のパスワードを値として入力します。他の値は一旦デフォルトのままでいいと思います。入力できたら作成します。
さらに、シークレットに対し Cloud Run Functions がアクセスできるように、関数が使用するサービスアカウントに権限を付与する必要があります。
- 作成したシークレットの名前(例: my-cloudsql-db-password)をクリックし、詳細ページを開きます。
- 「権限」タブをクリックします。
- 「アクセスを許可」ボタンをクリックします。
- 新しいメンバーを追加します。以下のように入力して設定します。
- プリンシパルの追加: サービスアカウントのメールアドレスを入れます
- ロール: Secret Manager のシークレットアクセサー
3.4 Cloud SQL Admin API を有効にする
Cloud Run Functions が Cloud SQL に接続するために、Cloud SQL Admin API
を有効にする必要があります。
Cloud Run Functions のサービスコンテナに Cloud SQL Proxy が組み込まれて実行され、このプロキシが Cloud SQL インスタンスへのセキュアなトンネルを確立します。このプロキシが接続時に、Cloud SQL インスタンスの情報を取得するために Cloud SQL Admin API
を利用します。
4. Cloud Run Functions の設定
さていよいよ Cloud Run Functions 上で動く API を作ります。そして API が Cloud SQL に接続するためにいくつかの設定をします。
ここでは Node.js
Express
を使って Cloud SQL に接続する簡易的なアプリケーションを作ってみます。
Node.js
でいうと Express
以外のフレームワークも選択できると思いますが、相性が良いもの、悪いものがあるようです。私は最初 Hono
で作ってみたのですが、上手く動きませんでした。
4.1 Cloud Run Functions のアプリケーションを書く
まずプロジェクトのディレクトリ下で以下のコマンドを実行し、必要なパッケージをインストールします。
npm install @google-cloud/cloud-sql-connector dotenv-flow express pg
npm install -D @types/express @types/pg tsx typescript
package.json
の scripts
にビルドコマンドを書いておきます。
{
"name": "cloud-run-functions-sample",
"version": "1.0.0",
"main": "dist/index.js",
"type": "module",
"scripts": {
"build": "tsc"
},
"dependencies": {
"@google-cloud/cloud-sql-connector": "^1.8.1",
"express": "^4.17.1",
"pg": "^8.7.1"
},
"devDependencies": {
"@types/express": "^4.17.23",
"@types/pg": "^8.15.4",
"tsx": "^4.19.4",
"typescript": "^4.5.4"
}
}
tsconfig.json
も作っておきます。
中身の設定はお好みですが、例えば以下のような感じです。
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Node",
"lib": [
"ES2022",
"DOM"
],
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"isolatedModules": true
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules",
"dist"
]
}
src/index.ts
に以下のプログラムを置きます。
import express, { Request, Response } from 'express'
import { Connector, IpAddressTypes } from '@google-cloud/cloud-sql-connector'
import { Pool, PoolConfig } from 'pg'
const app = express()
// Cloud SQL Connector のインスタンスをグローバルスコープで一度だけ作成
// アプリケーションのライフサイクル全体で再利用されます
const connector = new Connector()
// データベース接続プールを保持する変数
let db: Pool
/**
* データベース接続プールを初期化する非同期関数。
* コールドスタート時に一度だけ実行されることを想定。
*/
const initializeDb = async () => {
if (db) return // 既に初期化済みの場合は何もしない
// データベースの認証情報と接続名を環境変数から取得
// Cloud Run にデプロイする際にこれらの環境変数を設定します
const dbUser = process.env.DB_USER!
const dbPassword = process.env.DB_PASSWORD!
const dbName = process.env.DB_NAME!
const instanceConnectionName = process.env.INSTANCE_CONNECTION_NAME!
if (!instanceConnectionName) {
throw new Error('INSTANCE_CONNECTION_NAME environment variable is not set.')
}
if (!dbUser || !dbPassword || !dbName) {
throw new Error('DB_USER, DB_PASSWORD, and DB_NAME environment variables must be set.')
}
// Cloud SQL Connector を介して PostgreSQL の接続オプションを取得
const clientOpts = await connector.getOptions({
instanceConnectionName: instanceConnectionName,
// VPC コネクタを使用する場合、PRIVATE を指定することで Private IP 経由の接続を優先します
ipType: IpAddressTypes.PRIVATE,
})
const poolConfig: PoolConfig = {
user: dbUser,
password: dbPassword,
database: dbName,
...clientOpts,
}
db = new Pool(poolConfig)
console.log('Cloud SQL connection pool initialized.')
}
/**
* リクエストごとにデータベース接続を初期化(または再利用)するミドルウェア。
* Cloud Run のコールドスタート対策として利用します。
*/
app.use(async (req, res, next) => {
try {
await initializeDb()
next()
} catch (err) {
console.error('Error during database initialization:', err)
res.status(500).send('Service unavailable due to database connection issue.')
}
})
/**
* サンプルエンドポイント: 現在の時刻をデータベースから取得して返す
*/
app.get('/hello', async (req: Request, res: Response) => {
try {
const result = await db.query('SELECT NOW()')
res.json(result.rows)
} catch (err) {
console.error('Error executing database query:', err)
res.status(500).send('Error retrieving data from the database.')
}
})
// Express アプリケーションをエクスポート
// Cloud Run Functions のエントリーポイントとして使用されます
export { app }
/**
* アプリケーションシャットダウン時のクリーンアップ処理。
* Cloud Run がインスタンスを停止する際に実行されることがあります。
*/
process.on('beforeExit', () => {
console.log('Closing Cloud SQL connector and database pool...')
connector.close()
if (db) {
db.end(() => console.log('PostgreSQL connection pool closed.'))
}
})
ビルドしておきます。
npm run build
4.2 Cloud Run Functions にデプロイする
作ったアプリケーションを Cloud Run Funtions にデプロイします。
Cloud Run Functions にデプロイします。
ここで先ほど設定したサービスアカウントとVPCコネクタを指定します。また、環境変数も合わせてセットしたい場合は --set-env-vars
に入れてください。
gcloud functions deploy cloud-run-functions-sample \
--gen2 \
--runtime=nodejs20 \
--region=YOUR_REGION \
--source=. \
--entry-point=app \
--trigger-http \
--service-account=YOUR_SERVICE_ACCOUNT_EMAIL \
--vpc-connector=projects/YOUR_PROJECT_ID/locations/YOUR_REGION/connectors/YOUR_VPC_CONNECTOR_NAME \
--set-env-vars DB_USER=your_cloudsql_user,DB_NAME=your_cloudsql_database,INSTANCE_CONNECTION_NAME=[YOUR_PROJECT_ID]:[YOUR_REGION]:[YOUR_CLOUD_SQL_INSTANCE_NAME] \
--update-secrets DB_PASSWORD=your-db-password-secret-id:latest \
--memory=512Mi \
--timeout=60s
オプション名 | 説明 |
---|---|
FUNCTION_NAME | デプロイする関数の名前です。この名前が Cloud Run のサービス名として使用されます。例: my-api-function |
--gen2 | Cloud Functions の第2世代ランタイムを使用することを指定します。これにより、Cloud Run を基盤とした、より堅牢な機能が利用可能になります。 |
--runtime=RUNTIME | 関数を実行するためのランタイム環境を指定します。Node.js の場合、nodejs20 (Node.js 20の場合) のように指定します。ご使用のコードベースのNode.jsバージョンに合わせてください。 |
--region=REGION | 関数をデプロイする Google Cloud リージョンを指定します。Cloud SQL インスタンスや VPC アクセス コネクタと同じリージョンにすることを強く推奨します。例: asia-northeast1 |
--source=SOURCE_PATH | デプロイするソースコードがあるディレクトリのパスです。通常、コマンドを実行している現在のディレクトリであれば . を指定します。 |
--entry-point=ENTRY_POINT_FUNCTION | Cloud Functions がリクエストを受け取った際に呼び出す、コード内の関数またはエクスポートされたオブジェクトの名前です。Express アプリケーションの場合、export { app } とエクスポートしているため、app を指定します。 |
--trigger-http | この関数が HTTP リクエストによってトリガーされることを指定します。これにより、関数にアクセスするための HTTP エンドポイントが提供されます。 |
--service-account=SERVICE_ACCOUNT_EMAIL | 関数が実行される際に使用されるサービスアカウントのメールアドレスを指定します。このサービスアカウントには、Cloud SQL への接続権限 (Cloud SQL クライアント ロールなど) や、Secret Manager を使用する場合はそのシークレットへのアクセス権限が必要です。例: my-function-sa@your-project-id.iam.gserviceaccount.com |
--vpc-connector=VPC_CONNECTOR_PATH | この関数が VPC アクセス コネクタを介して VPC ネットワークに接続することを指定します。Cloud SQL にプライベート IP で接続するために必須です。パスは projects/YOUR_PROJECT_ID/locations/YOUR_REGION/connectors/YOUR_VPC_CONNECTOR_NAME の形式で指定します。 |
--set-env-vars KEY1=VALUE1,KEY2=VALUE2 | 関数に環境変数を設定します。複数の変数をカンマで区切って指定できます。機密性の低い情報(例: DB_USER, DB_NAME, INSTANCE_CONNECTION_NAME)に使用します。 |
--update-secrets KEY3=SECRET_ID:VERSION | Secret Manager に保存されているシークレットを環境変数として関数に公開します。パスワードなどの機密性の高い情報の管理に推奨されます。:VERSION は通常 :latest を使用して最新バージョンを参照します。 |
--memory=MEMORY_SIZE | 関数に割り当てるメモリの量を指定します。デフォルト値があり、必要に応じて MiB または GiB 単位で指定できます(例: 512Mi、1Gi)。 |
--timeout=TIMEOUT_SECONDS | 関数の実行がタイムアウトするまでの時間を秒単位で指定します。デフォルトは60秒です。長い処理が必要な場合は長く設定します。例: 60s。 |
--allow-unauthenticated | このオプションを指定すると、認証なしでインターネットから関数を呼び出すことができます。公開APIとして利用する場合に設定します。セキュリティを強化する場合はこのオプションを省略し、IAM を使ってアクセスを制御します。 |
コマンドを実行するとデプロイが始まります。
有効にしなければならないAPIがあると対話形式で「y/N」を聞いてくるので「y」を入力します。私の場合は以下のようなやりとりがありました。
API [cloudfunctions.googleapis.com] not enabled on project [mydrive-442905]. Would you like to
enable and retry (this will take a few minutes)? (y/N)? y
Enabling service [cloudfunctions.googleapis.com] on project [mydrive-442905]...
Operation "operations/acf.p2-535253806525-g1j4a7f7-9999-8888-777-ce2ab895879b" finished successfully.
Created .gcloudignore file. See `gcloud topic gcloudignore` for details.
API [cloudbuild.googleapis.com] not enabled on project [mydrive-442905]. Would you like to enable
and retry (this will take a few minutes)? (y/N)? y
Enabling service [cloudbuild.googleapis.com] on project [mydrive-442905]...
Operation "operations/acf.p2-535253806525-5yy555ee-ff44-4242-8181-82f9af63d5d2" finished successfully.
Allow unauthenticated invocations of new function [cloud-run-functions-sample]? (y/N)? y
Preparing function...done.
X Deploying function...
- [Build] Build in progress... Logs are available at [https://console.cloud.google.com/cloud-bui
ld/builds;region=asia-northeast2/b6bde836-q999-444f-999a-19ec2002bfeb?project=535253806525]
. [Service]
. [ArtifactRegistry]
. [Healthcheck]
. [Triggercheck]
デプロイ設定の管理
このデプロイコマンドを毎回打つのはしんどいので、YAML / JSON で管理する方法があります。cloudbuild.yaml
というファイルに設定を書いてプロジェクトの直下に置いておくと、gcloud builds submit
というコマンドだけでデプロイができるようになります。
例えば以下のような yaml を作成します。
steps:
- name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
args:
- gcloud
- functions
- deploy
- cloud-run-functions-sample
- --gen2
- --runtime=nodejs20
- --region=asia-northeast2
- --source=.
- --entry-point=app
- --trigger-http
- --service-account=cloudrunfunctionstest@mydrive-442905.iam.gserviceaccount.com
- --vpc-connector=projects/mydrive-442905/locations/asia-northeast2/connectors/vpc-connector
- --set-env-vars
- DB_USER=cloud-run-functions,DB_NAME=sample,INSTANCE_CONNECTION_NAME=mydrive-442905:asia-northeast2:sandbox
- --update-secrets
- DB_PASSWORD=projects/535253806525/secrets/sql_cloud-run-functions_user-password:latest
- --memory=512Mi
- --timeout=60s
なお、gcloud builds
コマンドを使うためには事前に Cloud Resource Manager API
を有効にしておく必要があります。
デプロイしたくないファイルの除外
初回デプロイ時に .gcloudignore
ファイルが自動的に作られます。ここにデプロイしたくないファイルを定義しておくことで、デプロイ対象外とすることができます。例えば以下のように書きます。
# git
.git
.gitignore
# Google Cloud
.gcloudignore
cloudbuild.yaml
# Node.js
node_modules
# env
.env
.env.*
# docker
docker-compose.yaml
# other
README.md
デプロイが完了すると URL が発行されます。
4.3 Cloud Run Functions の権限を変える
このままだとデプロイされたアプリケーションに普通にアクセスすることはできません。権限を変えてあげる必要があります。
- Google Cloud Console にアクセスします。
- サービスから 「Cloud Run」 を選択します。
- すると先ほどデプロイしたサービスが一覧に表示されるので、サービス名を押下して詳細ページへ行きます。
- 「セキュリティ」タブ を押下します。
-
「認証」 の設定を
Allow unauthenticated invocations
に変えて「保存」します。 - これでサービスのエンドポイントに対して認証なしでアクセスできるようになります。
この変更をすると URL を知っている人が認証なしでアクセス可能になることに注意してください。普通にユーザがアクセスできる想定の API であれば問題ありません。
もしこの設定を変えたくない場合は、アクセスする際にヘッダーに IAM の認証情報をつけてリクエストすればアクセスすることは可能です。
この設定はデプロイ時のオプションで --allow-unauthenticated
を指定することでも設定可能です。ただ、その場合はサービスアカウントに run.services.setIamPolicy
権限をつけてあげる必要があると思います。
4.4 実際にサイトにアクセスしてみる
ここまで来るとようやく実際にサイトにアクセスできるようになります。
デプロイ時に払い出された以下のようなURLにアクセスすると
https://asia-northeast2-xxxx.cloudfunctions.net/cloud-run-functions-sample/hello
[{"now":"2025-07-05T14:45:09.276Z"}]
という簡素なJSONが返ってきましたが、正しく Cloud SQL にアクセスできていそうです!
まとめ
Cloud Run Functions に API を置き、API から Cloud SQL にアクセスする簡易的なアプリケーションを作ってみました。慣れればすぐ作れそうですが、初回だと地味に時間がかかりそうです。
しかしサーバレス、フルマネージドでこのような API が簡単に作れてしまうのは楽しいですね。
みなさんの参考になれば幸いです。