Pub/Subに届いたメッセージをトリガーにしてCloud Functionsを呼び出す方法について
Terraformを使うケースを想定する
TL;DR
- Dead Letter Queue(DLQ)を使いたい場合はサブスクリプションで設定する
- HTTPトリガーで認証付きのCloud Functions gen2を呼び出す場合は、呼び出し側に
roles/run.invoker
のロールが必要- gen1で必要となる
roles/cloudfunctions.invoker
と異なる
- gen1で必要となる
Pub/SubトリガーでCloud Functionを呼び出す場合
Pub/SubとCloud Functionsを接続する際はPub/Subトリガーを使って呼び出すのがよくあるパターンである。 次のようなTerraformをapplyすると、Pub/SubトピックとCloud Functionに加え、自動でサブスクリプションとEventarcのトリガーが作成される。
resource "google_pubsub_topic" "tftest" { name = "tftest-topic" } resource "google_cloudfunctions2_function" "image_transform" { name = "image-copy-function-2" description = "Copies image from source bucket to destination bucket" location = "asia-northeast1" build_config { runtime = "nodejs16" entry_point = "handleImageUpload" source { storage_source { bucket = google_storage_bucket.image_transform_function.name object = google_storage_bucket_object.function_source.name } } } service_config { max_instance_count = 3 min_instance_count = 1 available_memory = "4Gi" timeout_seconds = 60 max_instance_request_concurrency = 80 available_cpu = "4" environment_variables = { DESTINATION_BUCKET = google_storage_bucket.tftest.name } ingress_settings = "ALLOW_INTERNAL_ONLY" all_traffic_on_latest_revision = true service_account_email = google_service_account.image_transform_function.email } event_trigger { event_type = "google.cloud.pubsub.topic.v1.messagePublished" trigger_region = "asia-northeast1" pubsub_topic = google_pubsub_topic.tftest.id retry_policy = "RETRY_POLICY_RETRY" } }
しかしこの場合だと、Cloud Functionsが失敗した際にメッセージを転送するDLQを設定することができない。
理由としては、Pub/SubのDLQはサブスクリプションに対して設定するため、自動生成されるサブスクリプションはTerraformのコードで扱えないから。
回避方法
Pub/SubのPushサブスクリプションを使い、Cloud FunctionのHTTPエンドポイントを呼び出す。このHTTPトリガーはgoogle_cloudfunctions2_function
を作成すると自動で作られ、デフォルトでは呼び出しに認証が必要になる。
この呼び出し方法だと、google_pubsub_subscription
をTerraformで記述する必要があるので、その中でDLQが指定できる。
resource "google_pubsub_subscription" "tftest_http" { name = "tftest-http" topic = google_pubsub_topic.tftest.name ack_deadline_seconds = 10 push_config { push_endpoint = google_cloudfunctions2_function.image_transform.service_config[0].uri attributes = { x-goog-version = "v1" } oidc_token { service_account_email = google_service_account.invoke_function_2.email } } dead_letter_policy { dead_letter_topic = google_pubsub_topic.dead_letter_topic.id max_delivery_attempts = 5 } retry_policy { minimum_backoff = "5s" maximum_backoff = "30s" } } resource "google_pubsub_topic" "dead_letter_topic" { name = "dead-letter-topic" message_retention_duration = "600s" }
権限まわり
サブスクリプションに紐づけるSAには次の権限が必要になる
- Cloud Functionsを呼び出す権限
roles/run.invoker
ロールが必要(Cloud Functions gen2の場合)- なぜかCloud Runを呼び出す権限が必要になる(gen1の場合は素直に
roles/cloudfunctions.invoker
を付与すれば良い)
GCPが提供するpubsubのSAには次の権限が必要になる
- SAにトークンを発行する権限
- DLQのトピックへメッセージを発行する権限
- メッセージに対して確認応答する権限(DLQに転送する際、元のキューから除去するために必要)
Terraformでは次のようになる
resource "google_service_account" "invoke_function_2" { account_id = "invoke-function-2" display_name = "invoke-function-2" } resource "google_cloud_run_service_iam_member" "invoke" { location = "asia-northeast1" service = google_cloudfunctions2_function.image_transform.name role = "roles/run.invoker" member = google_service_account.invoke_function_2.member } resource "google_project_iam_member" "project" { project = data.google_project.self.id role = "roles/iam.serviceAccountTokenCreator" member = "serviceAccount:service-${data.google_project.self.number}@gcp-sa-pubsub.iam.gserviceaccount.com" } resource "google_pubsub_topic_iam_member" "publish_dlq" { topic = google_pubsub_topic.dead_letter_topic.id role = "roles/pubsub.publisher" member = "serviceAccount:service-${data.google_project.self.number}@gcp-sa-pubsub.iam.gserviceaccount.com" } resource "google_pubsub_subscription_iam_member" "dequeue" { subscription = google_pubsub_subscription.tftest_http.id role = "roles/pubsub.subscriber" member = "serviceAccount:service-${data.google_project.self.number}@gcp-sa-pubsub.iam.gserviceaccount.com" }
おわりに
Pub/SubトリガーでCloud Functionsを呼び出す場合、TerraformでDLQの設定ができないという問題はProvider PluginのIssueにも上がっていた