Cloud Run Jobs で Cloud SQL にマイグレーション実行
はじめに
便利だろうと思いつつ、これまであまり触れる機会のなかったCloud Run Jobsですが、DBのマイグレーション(up/down)の自動化を試みる際に使ってみることにしました。
今回は、CloudSQL(PostgreSQL)に対して、Cloud Run Jobs上でマイグレーションを実行する実装を行いました。本記事では、そのための環境構築方法をTerraformコードを交えて説明します。サンプルコードはこちら(github)にあります。
なお、PostgreSQLを使用していますが、MySQLを使用する際にもドライバを変えるだけで同様に実装できるかと思います。また、AlloyDBに関しても、接続情報やプロキシを変更することで同様に実装できることを試しました[1]。
0. 前提
ジョブを実装する前に、Google Cloud上に構築されている環境の前提条件を記載します。この環境に追加して、ジョブの実装を進めていきます。
Cloud SQLインスタンスが存在し、VPCネットワークからプライベートIPアドレスでアクセス可能な状態であるとします。Terraformを使用して構成した場合、次のようなリソースが作成されている状態です。
data "google_project" "current" {}
resource "google_compute_network" "db_network" {
name = "db-network"
auto_create_subnetworks = false
}
resource "google_compute_global_address" "db_private_ip" {
name = "db-private-ip"
purpose = "VPC_PEERING"
address_type = "INTERNAL"
address = "10.0.0.0"
prefix_length = 16
network = google_compute_network.db_network.id
}
resource "google_service_networking_connection" "db_conn" {
network = google_compute_network.db_network.id
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [google_compute_global_address.db_private_ip.name]
}
resource "google_sql_database_instance" "db" {
name = "db"
database_version = "POSTGRES_16"
region = "asia-northeast1"
settings {
tier = "db-f1-micro"
ip_configuration {
private_network = google_compute_network.db_network.id
ipv4_enabled = false
enable_private_path_for_google_cloud_services = true
}
}
depends_on = [google_service_networking_connection.db_conn]
}
resource "google_sql_user" "db_admin" {
project = data.google_project.current.project_id
instance = google_sql_database_instance.db.name
name = "db-admin"
# 本来はシークレットマネージャなどを使用するべき
password = var.password
}
resource "google_sql_database" "db" {
project = data.google_project.current.project_id
instance = google_sql_database_instance.db.name
name = "db"
}
1. Artifact Registry の作成
まず、マイグレーションジョブ用のDockerイメージを格納するためのレジストリを作成します。
resource "google_artifact_registry_repository" "migration" {
location = "asia-northeast1"
repository_id = "migration"
description = "migration docker repository"
format = "DOCKER"
}
2. マイグレーション関連ファイル作成
Cloud Run Jobs上で実行するマイグレーション関連ファイルを作成します。
ここではサンプルとしてalembicを使用していますが、Knexなど他のマイグレーションツールでも同様にマイグレーションを実行できると思います。alembicを使用しない場合、このセクションをスキップしてください。また、以降のセクションでもalembicに関するコードや説明については、適宜読み換えご利用ください。
まず、依存パッケージを準備し、マイグレーションの初期化を実行します。Ryeを使用する場合は、以下のコマンドを実行します(必要に応じて pip install
などに読み換えてください)。
rye add sqlalchemy alembic psycopg2-binary
alembic init alembic
次に、マイグレーション関連ファイルのDB接続情報を設定します。この際、必要に応じて環境変数[2]を使用すると良いでしょう。alembicを使用する場合、前述のinit
コマンドで自動生成される alembic.ini
の sqlalchemy.url
を編集します。リビジョンファイルも作成し、以下のコマンドを実行して、自動生成されたファイルの upgrade/downgrade 関数を実装します。
alembic revision -m "message"
3. Dockerfile作成
マイグレーションを実行するコンテナのDockerfileを作成します。このDockerfileには、マイグレーション関連ファイル一式とCloudSQL Proxyをインストールします。そして、コンテナ起動時にプロキシを介してマイグレーションを実行するように設定します。
FROM python:3.11-slim
WORKDIR /app
RUN apt-get update && apt-get install -y wget gnupg && rm -rf /var/lib/apt/lists/*
RUN wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 -O cloud_sql_proxy && \
chmod +x cloud_sql_proxy
COPY alembic.ini /app/
COPY alembic /app/alembic
COPY pyproject.toml .
COPY requirements.lock .
RUN python -m venv /venv
RUN /venv/bin/pip install --no-cache-dir -r requirements.lock
CMD ./cloud_sql_proxy -dir=/cloudsql -instances=[PROJECT_ID]:asia-northeast1:[DB_INSTANCE_NAME]=tcp:5432 & \
sleep 10 && \
/venv/bin/alembic upgrade head
このCMDを編集することで、downマイグレーション用のコンテナも作成できます。
4. イメージのビルド&プッシュ
3で作成したDockerfileをビルドし、1で作成したArtifact Registryにプッシュします。
まず、Google Cloudのレジストリに認証を設定します。
gcloud auth configure-docker asia-northeast1-docker.pkg.dev
次に、イメージのビルドを行います。ビルド環境がMacである場合、CloudRunの実行環境とCPUアーキテクチャが異なることに注意し(参考)、以下のようにビルドします。
(docker buildx create --name mybuilder --use)
docker buildx build --platform linux/amd64 -t asia-northeast1-docker.pkg.dev/[PROJECT_ID]/[REPOSITORY_NAME]/[IMAGE_NAME]:latest --push .
5. ジョブのデプロイ
4でプッシュされたイメージを使用してジョブをデプロイします。このジョブはCloudSQLに対してVPCアクセスできるように設定します。また、このジョブにアタッチするサービスアカウントはArtifact Registryからの読み込み権限も必要です。VPCアクセスコネクタを含む、Terraformのサンプルコードを以下に書きます。
resource "google_vpc_access_connector" "connector" {
name = "vpc-access-con"
ip_cidr_range = "10.10.0.0/28"
network = google_compute_network.db_network.id
}
resource "google_service_account" "migration_job" {
account_id = "migration-job"
description = "for migration job"
}
resource "google_project_iam_member" "migration_job" {
for_each = toset([
"roles/artifactregistry.reader",
"roles/cloudsql.admin",
])
project = data.google_project.current.project_id
role = each.value
member = "serviceAccount:${google_service_account.migration_job.email}"
}
resource "google_cloud_run_v2_job" "migration" {
name = "migration"
location = "asia-northeast1"
template {
template {
service_account = google_service_account.migration_job.email
containers {
image = "asia-northeast1-docker.pkg.dev/${google_artifact_registry_repository.migration.project}/${google_artifact_registry_repository.migration.repository_id}/migration:latest"
}
vpc_access {
connector = google_vpc_access_connector.connector.id
}
}
}
}
最小権限を付与したい場合は、google_artifact_registry_repository_iam_member
などのリソースを使用するのが良いでしょう。
これで、一通り構築は完了です。ジョブはコンソールから実行するか、以下のコマンドで実行すれば良いでしょう。
gcloud run jobs execute
マイグレーションが正常に実行できているかどうかは、CloudSQL studioを使えば簡単に確認[3]できます。
(Appendix)GitHub Actionsによる自動化
以上で構築は完了ですが、GitHub Actionsなどで自動化しておくと、CI/CDパイプラインに組み込めむことができ、より便利です。例えば、以下のようにビルドからジョブの実行までを自動化することができます。
name: migration
on:
push:
branches:
- "main"
jobs:
migration:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
env:
WORKLOAD_IDENTITY_PROVIDER: 'projects/[PROJECT_ID]/locations/global/workloadIdentityPools/[POOL_ID]/providers/[PROVIDER_ID]'
GITHUB_SERVICE_ACCOUNT: '[SERVICE_ACCOUNT_EMAIL]'
IMAGE_PATH: 'asia-northeast1-docker.pkg.dev/[PROJECT_ID]/migration/migration:latest'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ env.GITHUB_SERVICE_ACCOUNT }}
- name: Set up gcloud CLI
uses: google-github-actions/setup-gcloud@v2
with:
version: 'latest'
- name: Init docker for gcloud
run: gcloud auth configure-docker asia-northeast1-docker.pkg.dev
- name: Build docker image
run: docker build -t ${{ env.IMAGE_PATH }} .
- name: Push docker image for container registory
run: docker push ${{ env.IMAGE_PATH }}
- name: Execute Cloud Run Job
run: |
gcloud run jobs deploy migration \
--image ${{ env.IMAGE_PATH }} \
--region asia-northeast1
gcloud run jobs execute migration \
--region asia-northeast1
上記はWorkload Identity連携を使用しています。
これに加えて、3で記載したようにdownマイグレーションのジョブも簡単に作成できるので、こちらもワークフローに自動化しておくと良いでしょう。
まとめ
Cloud Run Jobs を使用して、Cloud SQLに対してDBのup/downマイグレーションを実行するための環境構築方法を記載しました。また、これを使用してマイグレーションを自動化する例についても紹介しました。
本記事の内容が、読者の皆様にとってお役に立てれば幸いです。
-
踏み台サーバを立てるなどの準備なしに、コンソールから簡単にクエリを実行できます。GAになったのは最近かと思いますが、非常に便利です。 ↩︎
世界のラストワンマイルを最適化する、OPTIMINDのテックブログです。「どの車両が、どの訪問先を、どの順に、どういうルートで回ると最適か」というラストワンマイルの配車最適化サービス、Loogiaを展開しています。recruit.optimind.tech/
Discussion