☁️

Pub/SubでCloud Runをオーケストレーション

2024/01/21に公開

TL;DR

  • サーバレスで複数のCloud RunをPub/Sub経由で実行します

はじめに

どうも、アニメマスターです。
生きていたら1つのトリガーで複数のCloud FunctionsやCloud Runを実行したくなることってありますよね?
ということで、それらを実装していく記事になります。

概観

01-architecture

SchedulerからPub/Subのトピックを呼び出して、Pub/SubからCloud RunをPushで実行します。

1. Artifact Registryにイメージを登録

公式のガイドなどを参考に、Cloud Runで利用するDockerイメージをArtifact Registryに登録しておきます。

2. Terraformでのリソース作成

今回作成するディレクトリは以下の様になります。

.
├── main.tf
├── modules
│   ├── cr
│   │   ├── main.tf
│   │   └── output.tf
│   ├── global
│   │   └── output.tf
│   └── pubsub
│       └── main.tf

2-1. globalモジュールに変数の設定

まずglobalモジュールにプロジェクトで利用する変数の設定ファイルを作ります。
<>書きのところは個人のものに置き換えてください。

modules/global/output.tf
output "project" {
  description = "A name of a GCP project"
  value       = <YOUR_PROJECT>
}

output "region" {
  description = "A name of a GCP region"
  value       = <YOUR_REGION>
}

output "service1_repository_path" {
  description = "A path of a service1 container in GCP Artifact Registry repository"
  value       = <YOUR_REPOSITORY>
}

output "service2_repository_path" {
  description = "A path of a service2 container in GCP Artifact Registry repository"
  value       = <YOUR_REPOSITORY>
}

output "sa_email" {
  description = "A service account email"
  value       = <YOUR_SERVICE_ACCOUNT_EMAIL>
}

2-2. Cloud Runモジュールの作成

次にCloud Runのモジュールを作成していきます。

modules/cr/main.tf
module "global" {
  source = "../global"
}

resource "google_cloud_run_v2_service" "service1" {
  name     = "service1"
  project  = module.global.project
  location = module.global.region
  template {
    service_account = module.global.sa_email

    scaling {
      max_instance_count = 1
    }

    containers {
      image = module.global.service1_repository_path
      ports {
        container_port = 8080
      }
    }
  }
}

resource "google_cloud_run_v2_service" "service2" {
  name     = "service2"
  project  = module.global.project
  location = module.global.region
  template {
    service_account = module.global.sa_email

    scaling {
      max_instance_count = 1
    }

    containers {
      image = module.global.service2_repository_path
      ports {
        container_port = 8080
      }
    }
  }
}

Cloud RunのモジュールはPub/Subのリソース作成時に利用するのでoutput.tfで出力しておきます。

modules/cr/output.tf
output "service1_uri" {
  value = google_cloud_run_v2_service.service1.uri
}

output "service2_uri" {
  value = google_cloud_run_v2_service.service2.uri
}

2-3. Pub/Subモジュールの作成

最後にPub/Subのモジュールを作成していきます。

modules/pubsub/main.tf
module "global" {
  source = "../global"
}

module "cr" {
  source = "../cr"
}

resource "google_pubsub_topic" "default-topic" {
  name = "default-topic"
}

resource "google_pubsub_subscription" "service1-subscription" {
  name  = "service1-subscription"
  topic = google_pubsub_topic.default-topic.name
  push_config {
    push_endpoint = module.cr.service1_uri
    oidc_token {
      service_account_email = module.global.sa_email
    }
    attributes = {
      x-goog-version = "v1"
    }
  }
  retry_policy {
    minimum_backoff = "60s"
    maximum_backoff = "600s"
  }
  ack_deadline_seconds    = 60
  enable_message_ordering = true
  depends_on              = [module.cr.service1]
}

resource "google_pubsub_subscription" "service2-subscription" {
  name  = "service2-subscription"
  topic = google_pubsub_topic.default-topic.name
  push_config {
    push_endpoint = module.cr.service2_uri
    oidc_token {
      service_account_email = module.global.sa_email
    }
    attributes = {
      x-goog-version = "v1"
    }
  }
  retry_policy {
    minimum_backoff = "60s"
    maximum_backoff = "600s"
  }
  ack_deadline_seconds    = 60
  enable_message_ordering = true
  depends_on              = [module.cr.service2]
}

resource "google_cloud_scheduler_job" "default-scheduler" {
  project   = module.global.project
  region    = module.global.region
  name      = "default-scheduler"
  schedule  = "0 6 * * *" # minute hour day-of-month month day-of-week
  time_zone = "Asia/Tokyo"

  pubsub_target {
    topic_name = google_pubsub_topic.default-topic.id
    data       = base64encode("message")
  }

  retry_config {
    retry_count = 1
  }

  depends_on = [google_pubsub_topic.default-topic]
}

おわりに

今回Cloud Runで実行しましたが、バッチ処理ならCloud Functionsで良い気もしますね。Cloud Runでも十分無料枠で賄えはしますが。
AWSならサーバレスコンポーネントのオーケストレーションはStep Functionsで行う気がしますが、GCPではWorkflowsではなくPub/Subを使う人が多い気がしています。

それでは、良きアニメライフを!

References

Discussion