Cloud Run Worker Pools で Redash をサーバーレス化する
はじめに
新しくCloud Run ワーカープール がリリースされましたね。pull ベースのワークロードを実行が目的の Cloud Run とのことです。
以前からできないこともなかったですが、より実装しやすくなったという印象です。
今回はこの Cloud Run ワーカープール を使って Redash を動かしてみます。Redash はクエリを発行するとそれをキューイングして、バックグラウンドする仕組みになってます。このバックグラウンドで処理するアプリケーションに Cloud Run ワーカープール を利用します。
構築
作成するリソースのざっくりした関係図です
1. データベースの作成
以下を作成します
- Cloud MemoryStore - Valkey v8.0
- Cloud SQL - PostgreSQL 17
Redis がなぜ必要なのかはこちらの記事がわかりやすかったです。
Redis 互換の Valkey を立てます。事前に Private Service Connect のコネクションポリシー[1]を作成しておく必要があります。ちなみに Redis でも問題ないです。
terraform コード
data "google_project" "project" {}
# Valkey (for Redis)
resource "google_memorystore_instance" "redash_db" {
authorization_mode = "AUTH_DISABLED"
deletion_protection_enabled = false
engine_version = "VALKEY_8_0"
instance_id = "redash-db"
location = "asia-northeast1"
mode = "CLUSTER_DISABLED"
node_type = "SHARED_CORE_NANO"
replica_count = 0
shard_count = 1
transit_encryption_mode = "TRANSIT_ENCRYPTION_DISABLED"
desired_auto_created_endpoints {
network = "projects/${data.google_project.project.project_id}/global/networks/default"
project_id = data.google_project.project.project_id
}
persistence_config {
mode = "DISABLED"
}
zone_distribution_config {
mode = "SINGLE_ZONE"
zone = "asia-northeast1-a"
}
}
Cloud SQL で PostgreSQL を立てます
terraform コード
resource "google_sql_database_instance" "redash_db" {
database_version = "POSTGRES_17"
deletion_protection = true
instance_type = "CLOUD_SQL_INSTANCE"
name = "redash-db"
region = "asia-northeast1"
settings {
availability_type = "ZONAL"
disk_size = 10
disk_type = "PD_SSD"
disk_autoresize = false
edition = "ENTERPRISE"
tier = "db-g1-small"
backup_configuration {
enabled = true
location = "asia-northeast2"
point_in_time_recovery_enabled = true
start_time = "12:00"
transaction_log_retention_days = 1
backup_retention_settings {
retained_backups = 7
retention_unit = "COUNT"
}
}
database_flags {
name = "cloudsql.iam_authentication"
value = "on"
}
insights_config {
query_insights_enabled = true
query_plans_per_minute = 5
query_string_length = 1024
record_application_tags = false
record_client_address = false
}
maintenance_window {
day = 1
hour = 0
update_track = "stable"
}
}
}
PostgreSQL に Redash からアクセスできるようにするため、アプリケーション接続用に redash
というユーザを built-in で作成します
2. マイグレーション
Redis に接続できないとマイグレーション実行に失敗してしまうため、Valkey にも接続するようにします
Valkey に接続するための SSH トンネルを構築する方法は公式ドキュメント[2]に記載されているのでこちらを参照してください
PROJECT_ID=YOUR_PROJECT_ID ;\
VALKEY_IP=YOUR_VALKEY_PRIVATE_IP ;\
gcloud compute ssh bastion-server \
--project ${PROJECT_ID} \
--zone=asia-northeast1-a \
--tunnel-through-iap \
-- -N -L 6379:${VALKEY_IP}:6379
マイグレーション実行先の PostgreSQL に接続できるようにするため、Cloud SQL Proxy を起動します[3]
./cloud-sql-proxy "${PROJECT_ID}:asia-northeast1:redash-db?port=5432"
いよいよマイグレーションを実行します。REDASH_COOKIE_SECRET
も設定しないと失敗するので注意してください。docker から localhost へ接続したいので host.docker.internal
を指定します。
REDASH_COOKIE_SECRET='MASKED'
DB_PASSWORD='MASKED' # redash user's database password
docker run --rm -it --add-host host.docker.internal:host-gateway \
-e REDASH_COOKIE_SECRET=${REDASH_COOKIE_SECRET} \
-e REDASH_DATABASE_URL="postgresql://redash:${DB_PASSWORD}@host.docker.internal/redash" \
-e REDASH_REDIS_URL='redis://host.docker.internal:6379/0' \
redash/redash:25.1.0 create_db
psql を使って、postgres ユーザでログインしてマイグレーションの結果を確認しましょう
PGPASSWORD=$DB_PASSWORD psql -h 127.0.0.1 -U postgres -d redash -c "\dt"
3. Cloud Run で Redash を動かす
サービスアカウント, シークレットマネージャ を作成します
terraform コード
resource "google_service_account" "service_account" {
account_id = "cloud-run-redash"
display_name = "Cloud Run Redash Service Account"
}
resource "google_secret_manager_secret" "secret" {
for_each = toset([
"redash-cookie-secret",
"redash-database-url",
"redash-google-client-id",
"redash-google-client-secret",
"redash-sendgrid-api-key"
])
secret_id = each.value
replication {
auto {}
}
}
resource "google_secret_manager_secret_iam_member" "secret_iam_member" {
for_each = google_secret_manager_secret.secret
secret_id = each.value.id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_service_account.service_account.email}"
}
redash-cookie-secret
にはランダムな文字列をいれておきます。redash-database-url
は postgresql://redash:${DB_PASSWORD}@${DB_PRIVATE_IP}/redash
を入れておきます。
DB_PASSWORD
は redash ユーザのパスワードへ、DB_PRIVATE_IP
は Cloud SQL のプライベート IP アドレスに置換してください。
Cloud Build をつかって、docker イメージをビルドします。イメージの push 先は cloud-run-source-deploy
[4] を間借りします。
FROM redash/redash:25.1.0
steps:
- name: 'gcr.io/cloud-builders/docker'
args:
- build
- redash
- -t
- asia-northeast1-docker.pkg.dev/$PROJECT_ID/cloud-run-source-deploy/redash:${_DOCKER_IMAGE_TAG}
- name: 'gcr.io/cloud-builders/docker'
args:
- push
- asia-northeast1-docker.pkg.dev/$PROJECT_ID/cloud-run-source-deploy/redash:${_DOCKER_IMAGE_TAG}
substitutions:
_DOCKER_IMAGE_TAG: latest
gcloud builds submit --config ./redash/cloudbuild.yaml --project=$PROJECT_ID
Redash を動かす Cloud Run をデプロイします。今回使う YAML はここにアップロードしてあります。
Redash は Server, Worker, Scheduler の3種類のサービスを動かす必要があります。
Worker はさらに 6 つの機能にわかれ、QUEUES
で指定されたキューを処理します。
まずは Server をデプロイします。これは Cloud Run Service としてデプロイします。アクセスを制限したいので IAP を有効化します。PostgreSQL と Redis に接続できるように、Direct VPC Egress を VPC に設定します。
gcloud run services replace redash-server.yaml --project $PROJECT_ID --async
次に Scheduler と Worker を動かす Cloud Run をデプロイします。これは Cloud Run Worker Pools としてデプロイします。今回は Worker を 3つの役割にわけて作成します。役割の分割には contrib-helm-chart[5] を参考にしました。
files=(
"redash-scheduler.yaml"
"redash-worker-generic.yaml"
"redash-worker-adhoc.yaml"
"redash-worker-scheduled.yaml"
)
for file in "${files[@]}"; do
gcloud beta run worker-pools replace "$file" --project "$PROJECT_ID" --async
done
それぞれ環境変数に REDASH_HOST
を追加します。これは redash-server の Cloud Run の URL にします。これを設定しておかないとメールのリンクなどもホスト名が空っぽになってしまいます。
gcloud run services describe redash-server --format='value(status.url)' --project $PROJECT_ID --region=asia-northeast1
# または
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(project_number)")
echo "https://redash-server-${PROJECT_NUMBER}.asia-northeast1.run.app"
また、redash-worker-adhoc はクエリを処理するので、これだけ WORKERS_COUNT
を 2
にしておきます。あわせてメモリも 2 GiB
に増量しておきます。
4. 初回セットアップ
redash-server の Cloud Run のURLにアクセスすると初回セットアップの画面が表示されます
必要な項目を入力して管理者ユーザを作成します
データソースを追加する
Redash の動作を確認するために BigQuery, Cloud SQL for MySQL への接続を試してみます
1. BigQuery
Cloud Run のサービスアカウントに IAM ロールを与えます。roles/bigquery.dataViewer
はプロジェクトに付与すると対象が広いので、本来は対象のリソースを絞ったほうがいいと思います。
terraform コード
resource "google_project_iam_member" "bigquery_job_user_iam_member" {
for_each = toset([
"TARGET_PROJECT_ID_1",
"TARGET_PROJECT_ID_2",
])
project = each.value
role = "roles/bigquery.jobUser"
member = "serviceAccount:${google_service_account.service_account.email}"
}
resource "google_project_iam_member" "bigquery_data_viewer_iam_member" {
for_each = toset([
"TARGET_PROJECT_ID_1",
"TARGET_PROJECT_ID_2",
])
project = each.value
role = "roles/bigquery.dataViewer"
member = "serviceAccount:${google_service_account.service_account.email}"
}
Redash の画面から以下のようにデータソースを追加します
- Name: 任意
- Project ID: 対象の Google Cloud プロジェクトID
- Load Schema: チェック
- Use Standard SQL: チェック
IAM ロールを振っているので、JSON Key File は不要です
2. Cloud SQL
Cloud Run のサービスアカウントに IAM ロールを与えます
terraform コード
resource "google_project_iam_member" "cloudsql_client_iam_member" {
for_each = toset([
"TARGET_PROJECT_ID_1",
"TARGET_PROJECT_ID_2",
])
project = each.value
role = "roles/cloudsql.client"
member = "serviceAccount:${google_service_account.service_account.email}"
}
resource "google_project_iam_member" "cloudsql_instance_user_iam_member" {
for_each = toset([
"TARGET_PROJECT_ID_1",
"TARGET_PROJECT_ID_2",
])
project = each.value
role = "roles/cloudsql.instanceUser"
member = "serviceAccount:${google_service_account.service_account.email}"
}
アクセス先のデータベースにユーザを追加します。もしも レプリカを作成している場合は、DB_INSTANCE_NAME は Cloud SQL のレプリカではないプライマリを指定してください。
terraform コード
resource "google_sql_user" "iam_user" {
for_each = {
# Cannot create users on replicas
"TARGET_PROJECT_ID_1" = "DB_INSTANCE_NAME_1",
"TARGET_PROJECT_ID_2" = "DB_INSTANCE_NAME_2",
}
name = google_service_account.service_account.email
instance = each.value
project = each.key
type = "CLOUD_IAM_SERVICE_ACCOUNT"
}
ユーザに権限[6]を付与します。横着して cloudsqlsuperuser
ロールを振ってますが、これは強すぎるので必要に応じて権限を絞ったほうがいいです。
-- ロールを付与
GRANT 'cloudsqlsuperuser'@'%' TO 'cloud-run-redash'@'%';
-- デフォルトロールとして設定
SET DEFAULT ROLE 'cloudsqlsuperuser'@'%' TO 'cloud-run-redash'@'%';
-- 確認
SHOW GRANTS FOR 'cloud-run-redash'@'%';
Scheduler と Worker サーバのサイドカーに Cloud SQL Proxy[7] を追加します。読み取りだけでいいので、ここで指定する接続先は Cloud SQL のレプリカを指定します。レプリカがない場合はプライマリを指定します。
Redash の画面から以下のようにデータソースを追加します
- Name: 任意
- Host: 127.0.0.1
- Port: 13306
- User: cloud-run-redash
- Database name: 接続先のデータベース
--auto-iam-authn
を指定しているので IAM 認証で接続します。そのため、Password は不要です。今後接続先を増やす場合は Cloud SQL Proxy に追加した上でデータソースを追加してください。
インテグレーション
1. Google OAuth
初期ユーザ以外は Google アカウントでログインできるようにします。公式ドキュメント[8]のまま設定できます。
なお、OAuth 同意画面は事前に設定済みです。
- Create credentials > OAuth client ID
- Application type は Web application
- Name は任意でよい
- Authorized JavaScript origins は redash-server の URL
- Authorized redirect URIs は redash-server の URL (
https://redash-server-<PROJECT_NUMBER>.asia-northeast1.run.app
) +/oauth/google_callback
発行した Client ID と Client Secret をシークレットマネージャの redash-google-client-id
と redash-google-client-secret
に保存します。
そして、redash-server の環境変数に以下を追加します。
最後に、Redash の 「Settings > General > Authentication > Google Login」 で、Allowed Google Apps Domains に許可したいドメイン(今回だと組織のドメイン)を指定します。
2. メール設定
アラート通知や招待など何かとメールを使うことがあるのでメールサーバを設定します。
今回は Sendgrid を使うようにします。接続情報[9]をもとに Sendgrid のアカウントで API キーを発行して、シークレットマネージャの redash-sendgrid-api-key
に保存します。
redash-server と redash-worker-generic の環境変数[10]に以下を追加します。
メール通知を検証してみましょう。任意のデータソースに対して、以下クエリを保存します。
select 0 as cnt;
1 以上になったときにメール通知がいくようにアラートを設定します。
クエリを変更してアラートを発火させます。
select 1 as cnt;
メールが届いていることを確認します。ちゃんと届いていれば設定はOKです。
おわりに
Cloud Run を使って、Redash をサーバーレス化しました。Google Cloud では Redash のようなバックグラウンドジョブを多用するアプリケーションは、GKE か GCE を使わないと難しかった印象です。Worker Pools の登場により Cloud Run を選択できるようになるのは非常にうれしいですね👌
この記事がCloud Run Worker Pools の活用や Redash の構築を検討されている方の参考になれば幸いです。
最終的なコード
Redash - Server
Redash - Scheduler
Redash - Worker
with 'queries'
with 'periodic,emails,default'
with 'scheduled_queries,schemas'
Discussion