🧑‍💻

Cloud Run サービスから Cloud SQL にダイレクト VPC 下り (外向き) を使ってプライベート接続する

2023/12/14に公開

2023年は「Cloud Run を触って覚える」をテーマとした一人アドベントカレンダーを一人で開催しており、Cloud Run のさまざまな機能や、Cloud Run でよく使う構成などを実際の使い方と一緒にご紹介しています。

https://qiita.com/advent-calendar/2023/cloud-run

14日目は Cloud SQL にプライベート接続する方法についてご紹介します。Cloud Run から VPC 内のリソースにプライベート接続する方法は、今年 (2023年) にプレビューで提供を開始した「ダイレクト VPC 下り (外向き)」を使います。

https://cloud.google.com/blog/ja/products/serverless/announcing-direct-vpc-egress-for-cloud-run?hl=ja

Cloud Run の概要は技術評論社さまのブログ「gihyo.jp」に寄稿した記事で解説していますのでこちらもぜひご覧ください。

https://gihyo.jp/article/2023/10/modern-app-development-on-google-cloud-03

Cloud SQL にプライベート接続する方法

まず、Cloud Run サービスから Cloud SQL にプライベート接続するにはどのような構成になるか解説します。

Cloud SQL へのプライベート接続
Cloud SQL へのプライベート接続

まず Cloud SQL インスタンスのプライベート接続について見てきましょう。

Cloud SQL インスタンスは Google Cloud が管理する VPC ネットワーク内に作成されます。このインスタンスを自分の環境 (Google Cloud プロジェクト) の VPC からアクセスできるようにするには、Google が管理する VPC と自分の VPC を VPC ピアリングする形で実現しています。これを プライベート サービス アクセス と呼びます。

https://cloud.google.com/sql/docs/postgres/configure-private-services-access?hl=ja

プライベート サービス アクセスを使って自分の環境の VPC 内から Cloud SQL インスタンスに対してプライベート接続ができるようにした上で、Cloud Run サービスからは VPC 内のリソースに接続するための構成を用意する必要があります。具体的な方法は次の記事で紹介しています。

https://zenn.dev/google_cloud_jp/articles/cloudrun-vpc

「サーバーレス VPC アクセス」と「ダイレクト VPC 下り (外向き)」のいずれかの方法によって接続できます。この記事では「ダイレクト VPC 下り (外向き)」を使って接続する手順を紹介します。

Cloud SQL インスタンスの準備をする

Cloud SQL インスタンスを準備しましょう。この節では、Cloud SQL インスタンスの作成とデータベースの作成を行います。

Cloud SQL インスタンスを作成する

まずは Cloud SQL のコンソールを開きます。

https://console.cloud.google.com/sql/instances

[インスタンスを作成] をクリックします。

インスタンスの作成
インスタンスの作成

[データベース エンジン] は [PostgreSQL] を選択します。

PostgreSQL の選択
PostgreSQL の選択

[インスタンス ID] は quickstart にします。[パスワード] は [生成] をクリックすると、ランダムなパスワードが生成されます。

インスタンス ID とパスワード
インスタンス ID とパスワード

[Cloud SQL エディションの選択] では [Enterprise Plus] がデフォルトで選択されていますが [Enterprise] に変更します。

Cloud SQL エディションの選択
Cloud SQL エディションの選択

[エディションのプリセット] を使うと、CPU やメモリなどの設定について、ユースケースに合ったデフォルト値を一括で設定できます。Cloud SQL は様々な設定項目がありますが、ユースケースに合わせてざっくりクイックに設定することができ便利です。ここでは [サンドボックス] を選択します。

エディションのプリセット
エディションのプリセット

[リージョン] は asia-northeast1 (東京) を、[ゾーンの可用性] は [シングルゾーン] のままにします。

リージョンとゾーン
リージョンとゾーン

Cloud SQL はデフォルトでパブリック IP のみが設定されるようになっているため、プライベート IP のみを許可するように設定します。[インスタンスのカスタマイズ] の [構成オプションを表示] をクリックして展開し、[接続] の [インスタンス IP の割り当て] の [プライベート IP] にチェックを付けます。[ネットワーク] は default を選びます。

プライベート IP の設定
プライベート IP の設定

プライベート サービス アクセスを設定します。[接続を設定] をクリックします。

プライベート サービス アクセスの接続設定
プライベート サービス アクセスの接続設定

Service Networking API を有効化する必要があるので [API を有効化する] をクリックします。

Service Networking API の有効化
Service Networking API を有効化

Cloud SQL インスタンスに割り当てる IP 範囲を設定します。細かい要件が特になければ [自動的に割り当てられた IP 範囲を使用する] が簡単です。/20 の IP 範囲を自動的に割り振るようになります。選択し、[続行] をクリックします。

IP 範囲の設定
IP 範囲の設定

[接続] をクリックし、設定を完了します。接続を作成するまでには数分かかります。

プライベート サービス アクセスの接続設定の完了
プライベート サービス アクセスの接続設定の完了

接続の作成が完了すると、プライベート サービス アクセスの設定が正常に作成できていることが確認できます。

プライベート サービス アクセスの確認
プライベート サービス アクセスの確認

最後に最下部までスクロールし [インスタンスを作成] をクリックします。

インスタンスの作成
インスタンスの作成

Cloud SQL インスタンスが作成されるまでには数分かかります。数分後、インスタンスの作成が正常に完了していることを確認します。

インスタンスの確認
インスタンスの確認

データベースを作成する

次に、Cloud Run サービスで使用するデータベースを作成します。

Cloud SQL インスタンスの作成後に表示されている詳細画面の [データベース] メニューを開き [データベースの作成] をクリックします。

データベースの作成
データベースの作成

[データベース名] は db にします。

データベース名の設定
データベース名の設定

以上でデータベースの準備は完了です。

Cloud Run サービスを作成する

次に Cloud SQL に接続する Cloud Run サービスを作ります。

この記事では Node.js のサンプルアプリをデプロイします。Web アプリケーション フレームワークとして Express を、OR マッパーとして Prisma を使用します。

サンプル アプリケーションのコンテナ イメージを作成する

Prisma のサンプルとして公開されているアプリ テンプレートを使用します。テンプレートはたくさん用意されていますが、今回はその中からシンプルな Express アプリケーションである rest-express を使います。

https://github.com/prisma/prisma-examples/tree/latest/typescript/rest-express

try-prisma というパッケージを使うと、テンプレートを元にしたアプリ プロジェクトを生成できます。

まずは Cloud Shell を起動し、次のコマンドを実行します。

npx try-prisma@latest --template typescript/rest-express

作成するアプリのセッティングを対話形式で行います。ここでは quickstart という名前にしました。

? Should we automatically install packages for you? No
? What should the project be named? quickstart
? Where should the new folder be created? .

アプリが作成できたら、アプリ プロジェクトのディレクトリに移動します。

cd quickstart

prisma/schema.prisma ファイルを開き、データベースの接続先 URL を DATABASE_URL 環境変数から設定するように修正します。

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

package.json にマイグレーション用のコマンドと起動用のコマンドを追加します。下記は package.json の一部です。scriptsstartmigrate を加えます。

本来であればマイグレーションは Cloud Run サービスのデプロイ前に行ったほうが良いですが、今回は簡易的に行う予定のため Cloud Run のコンテナ インスタンスからマイグレーションが行えるようにしておきます。

package.json
  "scripts": {
    "start": "ts-node src/index.ts",
    "migrate": "prisma migrate deploy && yarn start",
    "dev": "ts-node src/index.ts"
  }

デプロイの前に、ローカルで一度マイグレーションを実行し、マイグレーションファイルを作成しておく必要があります。

ローカルで PostgreSQL のコンテナを起動します。

docker run --name postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres:15.4

DATABASE_URL の環境変数をローカルで起動した PostgreSQL のコンテナに向けるように設定した上でマイグレーションを実行します。正常に終了すると prisma/migrations 配下にマイグレーションごとのフォルダと sql ファイルが作られます。

export DATABASE_URL=postgresql://postgres:postgres@localhost:5432/db
npx prisma migrate dev --name init

次に、コンテナとしてデプロイできるよう Dockerfile を追加します。

Dockerfile
FROM node:20

WORKDIR /app

COPY . .
RUN npm install

EXPOSE 3000

CMD ["npm", "start"]

次のコマンドでコンテナ イメージをビルド・プッシュします。

gcloud builds submit --region=asia-northeast1 --tag gcr.io/PROJECT_ID/quickstart .

以上でコンテナ イメージの準備が終わりました。

Cloud Run サービスを作成する

次に Cloud Run サービスを作成します。Cloud Run のコンソールに移動し [サービスの作成] をクリックします。

サービスの作成
サービスの作成

Cloud Run サービスの基本的な設定は こちらの記事 で紹介していますので、この記事では割愛しつつ Cloud SQL への接続に必要な設定にフォーカスしてご説明します。

コンテナ イメージは先ほどプッシュしたものを選択します。

コンテナ イメージの設定
コンテナ イメージの設定

[コンテナ、ボリューム、ネットワーキング、セキュリティ] を展開します。[コンテナ ポート] を 3000 に変更します。

コンテナ ポートの変更
コンテナ ポートの変更

[コンテナ コマンド] に yarn migrate を設定します。この設定によって、Dockerfile に記載してある CMD が上書きされます。このリビジョンではマイグレーションとアプリケーションの起動が行われるようになります。

コンテナ コマンドの変更
コンテナ コマンドの変更

[変数とシークレット] タブに移動し、環境変数を設定します。[変数を追加] をクリックし DATABASE_URL を設定します。

環境変数の追加
環境変数の追加

CloudSQL に接続可能な URL のスキーマは次のとおりです。

postgresql://USER:PASSWORD@localhost/DB_NAME?host=/cloudsql/PROJECT_ID:REGION:INSTANCE_NAME

それぞれ次のように設定します。

項目
USER PostgreSQL のユーザー名
PASSWORD USER に指定したユーザーのパスワード
DB_NAME データベース名 (ここでは db になる)
PROJECT_ID Google Cloud プロジェクト ID
REGION Cloud SQL のリージョン
INSTANCE_NAME Cloud SQL のインスタンス名

ここでは簡易的に環境変数として設定していますが、パスワードが含まれるので Secret Manager を併用してシークレットとして管理した方が適切です。設定方法は割愛しますが、具体的な手順は こちら を参照してください。

次に [Cloud SQL 接続] の [接続を追加] をクリックし、接続する Cloud SQL インスタンスを設定します。

Cloud SQL 接続の追加
Cloud SQL 接続の追加

上記を設定する上で [Cloud SQL Admin API] を使うため、まだ有効化していなければ有効化します。

Cloud SQL Admin API の有効化
Cloud SQL Admin API の有効化

次に [ネットワーキング] のタブを開きます。

[アウトバウンド トラフィック用の VPC に接続する] で [VPC に直接トラフィックを送信] を選びます。[ネットワーク] と [サブネット] は Cloud SQL と同じ VPC、サブネットを設定します。

VPC 接続の設定

以上で設定は終わりです。[作成] をクリックするとデプロイされます。

試す

実際に Cloud Run サービスを実行して見ましょう。curl で呼び出してみると実際にデータが保存できていることが確認できます。サンプル アプリケーションの API は README から確認できます。

https://github.com/prisma/prisma-examples/tree/latest/typescript/rest-express#using-the-rest-api

POST /signup でユーザーを作成します。

curl \
  -X POST \
  -H "content-type: application/json" \
  -d '{"name": "Example User", "email":"example@example.com"}' \
  https://<RUN_URL>/signup

作成したユーザーが返却されます(確認しやすいように整形しています)。

{
  "id":1,
  "email":"example@example.com",
  "name":"Example User"
}

POST /post で投稿できます。

curl \
  -X POST \
  -H "content-type: application/json" \
  -d '{"title":"サンプル","content":"本文","authorEmail":"example@example.com"}' \
  https://<RUN_URL>/post

作成した投稿が返却されます(確認しやすいように整形しています)。

{
  "id":1,
  "createdAt":"2023-12-14T19:54:09.754Z",
  "updatedAt":"2023-12-14T19:54:09.754Z",
  "title":"サンプル",
  "content":"本文",
  "published":false,
  "viewCount":0,
  "authorId":1
}

まとめ

Cloud Run と Cloud SQL を組み合わせた構成は汎用性が高いので、利用する機会も多いと思います。

「Cloud Run から VPC 内のリソースへの接続」と「Cloud SQL へのプライベート接続」の両方を構成しなければいけませんが、この記事がそれらの構成の理解の一助になれば幸いです。

マイグレーションの部分など本題とはずれる箇所は省略していますが、それはまた別の機会に解説できればと思います。

Google Cloud Japan

Discussion