🌊

Cloud Run Worker Pools で Redash をサーバーレス化する

に公開

はじめに

新しくCloud Run ワーカープール がリリースされましたね。pull ベースのワークロードを実行が目的の Cloud Run とのことです。

https://cloud.google.com/blog/products/serverless/exploring-cloud-run-worker-pools-and-kafka-autoscaler?e=48754805&hl=en

以前からできないこともなかったですが、より実装しやすくなったという印象です。

https://medium.com/google-cloud-jp/cloudrun-always-on-cpu-pubsub-pull-2664b9730781

今回はこの Cloud Run ワーカープール を使って Redash を動かしてみます。Redash はクエリを発行するとそれをキューイングして、バックグラウンドする仕組みになってます。このバックグラウンドで処理するアプリケーションに Cloud Run ワーカープール を利用します。

構築

作成するリソースのざっくりした関係図です

1. データベースの作成

以下を作成します

  • Cloud MemoryStore - Valkey v8.0
  • Cloud SQL - PostgreSQL 17

Redis がなぜ必要なのかはこちらの記事がわかりやすかったです。

https://kakakakakku.hatenablog.com/entry/2020/08/11/112554

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-urlpostgresql://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] を間借りします。

Dockerfile
FROM redash/redash:25.1.0
cloudbuild.yaml
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 はここにアップロードしてあります。
https://github.com/sikeda107/tech-blog/tree/main/redash-cloudrun/base

Redash は Server, Worker, Scheduler の3種類のサービスを動かす必要があります。

https://github.com/getredash/redash/blob/00a97d9266c2805c10f02f88fbddbfead46007d3/bin/docker-entrypoint#L62-L65

Worker はさらに 6 つの機能にわかれ、QUEUES で指定されたキューを処理します。

https://github.com/getredash/redash/blob/00a97d9266c2805c10f02f88fbddbfead46007d3/redash/worker.py#L10-L13

https://github.com/getredash/redash/blob/00a97d9266c2805c10f02f88fbddbfead46007d3/worker.conf#L13

まずは 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] を参考にしました。

https://github.com/getredash/contrib-helm-chart/blob/master/charts/redash/values.yaml#L397-L412

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_COUNT2 にしておきます。あわせてメモリも 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 ロールを振ってますが、これは強すぎるので必要に応じて権限を絞ったほうがいいです。

https://zenn.dev/pharmax/articles/93c0ec00fd722c

-- ロールを付与
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 のレプリカを指定します。レプリカがない場合はプライマリを指定します。

https://github.com/sikeda107/tech-blog/blob/34affce1288f2062dc6573a293eb5d8205ca97f0/redash-cloudrun/redash-scheduler.yaml#L46-L56

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 同意画面は事前に設定済みです。

  1. Create credentials > OAuth client ID
  2. Application type は Web application
  3. Name は任意でよい
  4. Authorized JavaScript origins は redash-server の URL
  5. 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-idredash-google-client-secret に保存します。

そして、redash-server の環境変数に以下を追加します。

https://github.com/sikeda107/tech-blog/blob/34affce1288f2062dc6573a293eb5d8205ca97f0/redash-cloudrun/redash-server.yaml#L58-L67

最後に、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]に以下を追加します。

https://github.com/sikeda107/tech-blog/blob/34affce1288f2062dc6573a293eb5d8205ca97f0/redash-cloudrun/redash-server.yaml#L34-L45

https://github.com/sikeda107/tech-blog/blob/34affce1288f2062dc6573a293eb5d8205ca97f0/redash-cloudrun/redash-server.yaml#L68-L72

メール通知を検証してみましょう。任意のデータソースに対して、以下クエリを保存します。

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
https://github.com/sikeda107/tech-blog/blob/main/redash-cloudrun/redash-server.yaml

Redash - Scheduler
https://github.com/sikeda107/tech-blog/blob/main/redash-cloudrun/redash-scheduler.yaml

Redash - Worker
with 'queries'
https://github.com/sikeda107/tech-blog/blob/main/redash-cloudrun/redash-worker-adhoc.yaml

with 'periodic,emails,default'
https://github.com/sikeda107/tech-blog/blob/main/redash-cloudrun/redash-worker-generic.yaml

with 'scheduled_queries,schemas'
https://github.com/sikeda107/tech-blog/blob/main/redash-cloudrun/redash-worker-scheduled.yaml

脚注
  1. ネットワーキング - サービス接続ポリシーはなぜ必要なのですか? ↩︎

  2. Redis インスタンスに接続する - ポート転送によるローカルマシンからの接続 ↩︎

  3. Cloud SQL Proxy を使用して接続する ↩︎

  4. ソースコードからデプロイする ↩︎

  5. getredash/contrib-helm-chart: Community maintained Redash Helm Chart ↩︎

  6. MySQL ユーザーについて ↩︎

  7. サイドカー パターンで Cloud SQL Auth Proxy を実行する ↩︎

  8. Setting up a Redash Instance - Google-OAuth-Setup ↩︎

  9. SMTPの接続情報を教えてください。 – サポート | SendGrid ↩︎

  10. Setting up a Redash Instance - Mail Configuration ↩︎

コミューン株式会社

Discussion