Google Cloud Run Servicesを土日と夜間に縮小運用しよう #googlecloud #opentofu #terraform

はじめに
以前は SlackボットをAzure App Serviceで動かしていました が、本稿執筆時点では Google Cloud Run Services 上に OpenTofu (乃至 Terraform)で構築して運用しています。本稿では、このSlackボットを土日と夜間に縮小運用するための仕組みについて見ていきます。
なお、TFコードやSlackボットの詳細については省略させていただきます。
Slackボットの通常運用時の設定
Slackボットは google_cloud_run_v2_service リソースを用いて構築しています。一部省略していますが次のようになっています。
resource "google_cloud_run_v2_service" "this" { name = var.project_name location = var.region scaling { min_instance_count = 1 } template { service_account = google_service_account.cloudrun.email scaling { min_instance_count = 0 max_instance_count = 5 } containers { image = "<snip>" resources { limits = { cpu = "1000m" memory = "1Gi" } cpu_idle = false } ... } ... } ... }
ここでは次の2点に注目してください。
scaling.min_instance_count
scaling
は template
の中にも外にもあってややこしいですが、この scaling.min_instance_count
は外側のほうで、「サービスレベルのスケーリング設定」です。
ここでは 1
を設定しているので、リクエストを処理していない(アイドル状態)のインスタンスを最低でも1つを維持します(ウォーム状態)。つまりリクエストがあった場合、起動したままのインスタンスを利用するので、すぐに応答があることが期待できます。ただし、リクエストを処理していない間も料金がかかっています。もし 0
の場合はアイドル状態のインスタンスが削除されるので、その間の料金はかかりませんが、リクエストがあったらインスタンスを起動する必要があるため応答に時間がかかります。
次も参照してください。
template.containers.resources.cpu_idle
template.containers.resources.cpu_idle
は、リクエストベース課金(true
)か、インスタンスベース課金(false
)かを設定します。
リクエストベース課金では、リクエストの処理中のみにCPUが割り当てられ、そのときのみ料金がかかります。一方、インスタンスベース課金では、リクエストがない期間にもCPUが割り当てられ、言ってみればずっと料金がかかります。ここでは false
なのでインスタンスベース課金です。
次も参照してください。
まとめると、このボットの設定は、
- リクエストを処理していない(アイドル状態)のインスタンスを最低でも1つを維持
- リクエストがない期間にもCPUが割り当てられるインスタンスベース課金
となっています。つまり、いつでもリクエストを即時リクエストを受け付けられるようにしているが、その分料金がかかっている状態です。
コスト削減するなら、
- リクエストを処理していない(アイドル状態)のインスタンスはゼロ
- リクエストがあったときのみCPUを割り当てるリクエストベース課金
とするのがよさそうです。しかし実際にこの設定にしてみたところ、思ったより応答が遅く、ユーザ体験があまりよくありませんでした。また、応答待ちでクルクルの代用に絵文字リアクションをつける 仕組みもうまく動きませんでした。
しかし、利用のない夜間や土日にも即時応答可能な待機状態はコストの無駄ではないか…?ということで考える必要がありました。
Cloud Scheduler と Cloud Run Job で定期設定変更
そこでGoogle Cloud Scheduler で Google Cloud Run Job を定期実行し、その中で gcloud CLI による設定変更を行うことにしました。
cronとコマンドラインを書くなら次のようになります。
- 平日の夜20時に運転を縮小する
"0 20 * * 1-5"
gcloud run services update [PROJECT_NAME] --region [REGION] --min 0 --cpu-throttling
- 平日の朝9時に通常運転に戻す
"0 9 * * 1-5"
gcloud run services update [PROJECT_NAME] --region [REGION] --min 1 --cpu-no-throttling
縮小運転のTFコードを見てみましょう。
resource "google_cloud_run_v2_job" "cold" { name = "${var.project_name}-cold" location = var.region deletion_protection = false template { template { service_account = google_service_account.hotcoldswitch.email containers { image = "google/cloud-sdk:512.0.0-alpine" resources { limits = { cpu = "1000m" memory = "512Mi" } } args = [ "gcloud", "run", "services", "update", var.project_name, "--region", var.region, "--min", "0", "--cpu-throttling" ] } } } } resource "google_cloud_run_v2_job_iam_member" "cold" { project = google_cloud_run_v2_job.cold.project location = google_cloud_run_v2_job.cold.location name = google_cloud_run_v2_job.cold.name role = "roles/run.invoker" member = google_service_account.hotcoldswitch.member } resource "google_cloud_scheduler_job" "cold" { name = google_cloud_run_v2_job.cold.name description = "Genmaicha Cold Switch Cloud Run Job" time_zone = "Asia/Tokyo" schedule = "0 20 * * 1-5" attempt_deadline = "360s" retry_config { retry_count = 3 } http_target { http_method = "POST" uri = "https://${google_cloud_run_v2_job.cold.location}-run.googleapis.com/apis/run.googleapis.com/v1/namespaces/${data.google_project.project.number}/jobs/${google_cloud_run_v2_job.cold.name}:run" oauth_token { service_account_email = google_service_account.hotcoldswitch.email } } }
通常運転に戻すTFコードは省略します。google_cloud_run_v2_job と google_cloud_scheduler_job も参照してください。
また、Cloud Run Servicesを起動しているサービスアカウント google_service_account.cloudrun
に、Cloud Run Jobを実行しているサービスアカウント google_service_account.hotcoldswitch
による実行を許すロールを与える必要があります。
resource "google_service_account_iam_member" "by_hcs" { service_account_id = google_service_account.cloudrun.name role = "roles/iam.serviceAccountUser" member = google_service_account.hotcoldswitch.member }
サービス アカウント ユーザーのロール も参照してください。Services を実行するのに必要なロールがあれば追加してください。
実行例

指標を確認したところ、夜20時に縮小し、翌朝9時に通常に復帰していることがわかります。これにより、Cloud Run Services の料金も半分に削減できました。
まとめ
Google Cloud Run Servicesの「サービスレベルのスケーリング設定」と「リクエストベース課金」を使うと料金を節約できますが、これらの機能を常時利用しているとユーザ体験があまりよくないため、Google Goolge Cloud Scheduler と Google Cloud Run Job を使うことで閑散期のみ利用するように切り替えてみました。
少し脱線しますが、最初 Google Cloud はとっつきづらい、わかりにくい…と思っていたのですが、Terraform / OpenTofu のおかげでだいぶ親しめるようになってきました。引き続き、現在運用中の Slack ボットに関する Google Cloud の機能や Terraform / OpenTofu のコードを紹介できたらと思います。