背景
弊社ではfirebaseで動く自社プロダクトの開発をいくつか行っているのですが、 その中でモデルを利用した重い処理はcloud functionを使うことができず動かす場所に頭を悩ませていました。 (一旦回避策としてGKEを利用して構築自体はできたのですが、ほぼ利用しないテスト環境でも毎月6~7千円コストがかかる状態。。。) LT登壇などで様々な方に意見を伺ったところCloud Runが良さそうという話をお聞きし、 動作確認までたどり着けたのでそこまでの内容をまとめておこうと思います。
環境は全てterraformで管理しているのでterraformを使った構築方法を記載しています。
一旦動かすだけのところ
Cloud Runを一旦動かしてみるのは以下コードを使うだけですぐ行うことができました。 URLリクエストを送った際にメッセージをおうむ返しするだけのものですが、 これで構築するだけでCloud Runの使い方の雰囲気とその応答スピードの速さを実感できると思います。
resource google_cloud_run_service default {
name = "test"
location = "${var.region}"
template {
spec {
containers {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
}
traffic {
percent = 100
latest_revision = true
}
}
既存システムとの接続
実際に利用する際はシステムとの繋ぎ込みが必要なため、 以下の構成でfirebaseのアプリとの繋ぎ込みを行いました。
firebaseのアプリでstorageに画像を保存するとpubsubにメッセージが飛び、 そこからpushリクエストでCloud Runを呼び出しています。
Cloud Run周りのterraform
resource google_container_registry registry {
project = "cloud_run_test_sakurai"
location = "ASIA"
}
# デプロイ時は先にgcrへイメージをアップロードする必要がある
resource google_cloud_run_service default {
name = "cloud_run_test_sakurai"
location = "asia-southeast1"
template {
spec {
containers {
image = "gcr.io/{{PROJECT_ID}}/cloud_run_test_sakurai:latest"
}
}
}
traffic {
percent = 100
latest_revision = true
}
}
Cloud Runへの接続周りのterraform
resource google_storage_notification notification {
bucket = "cloud_run_test_sakurai.appspot.com"
payload_format = "JSON_API_V1"
topic = google_pubsub_topic.image_upload.id
event_types = ["OBJECT_FINALIZE"]
depends_on = [google_pubsub_topic_iam_binding.binding]
}
resource google_pubsub_topic_iam_binding binding {
topic = google_pubsub_topic.image_upload.id
role = "roles/pubsub.publisher"
members = ["serviceAccount:${data.google_storage_project_service_account.gcs_account.email_address}"]
}
data "google_storage_project_service_account" "gcs_account" {}
resource google_pubsub_topic image_upload {
name = "cloud_run_test_sakurai-topic"
}
resource google_service_account pubsub_kicker {
account_id = "cloud_run_test_sakurai-pubsub-kicker"
display_name = "Pub/Sub service account for cloud run kicker"
}
resource google_project_iam_member pubsub_kicker {
member = "serviceAccount:service-{{ACCOUNT_ID}}@gcp-sa-pubsub.iam.gserviceaccount.com"
role = "roles/iam.serviceAccountTokenCreator"
}
resource google_cloud_run_service_iam_member pubsub_kicker {
service = google_cloud_run_service.default.name
location = google_cloud_run_service.default.location
role = "roles/run.invoker"
member = "serviceAccount:${google_service_account.pubsub_kicker.email}"
}
resource google_pubsub_subscription kick_cloud_run {
name = "cloud_run_test_sakurai-subscription"
topic = google_pubsub_topic.image_upload.name
ack_deadline_seconds = 600
message_retention_duration = "1200s"
retry_policy {
minimum_backoff = "60s"
}
push_config {
push_endpoint = "${google_cloud_run_service.default.status[0].url}"
oidc_token {
service_account_email = google_service_account.pubsub_kicker.email
}
}
}
Cloud Buildのyaml
※terraform_apply.shはterraform applyを実行するshell scriptになります。
substitutions:
_TERRAFORM_VERSION_: 0.12.10
steps:
- id: 'build_gkr_image'
name: 'gcr.io/cloud-builders/docker'
entrypoint: 'docker'
args: ['build','-t','gcr.io/${PROJECT_ID}/cloud_run_test_sakurai:latest','{{PATH/TO/DOCKERFILE}}']
allow_failure: <strong>true</strong>
- id: 'push_gkr'
name: 'gcr.io/cloud-builders/docker'
entrypoint: 'docker'
args: ['push','gcr.io/${PROJECT_ID}/cloud_run_test_sakurai:latest']
allow_failure: <strong>true</strong>
- id: 'tf apply'
name: 'hashicorp/terraform:${_TERRAFORM_VERSION_}'
entrypoint: 'sh'
args: ['./ci/terraform_apply.sh','dev']
timeout: 3600s
感想
pubsub周りの権限で少しわかりにくいですが割と簡潔にバッチシステムを組むことができました。 元々AWS環境で似たようなモデル実行環境を作っていた身としては、 裏で動かすインスタンスタイプの指定がないためフルマネージド感が強く、 またAWS Batchのようにインスタンス立ち上げを待たなくて良いので爆速だった点がとてもメリットとして感じられました。 今後運用していく上で感じた点などもまとめていければと思います。