0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Cloud Scheduler + Cloud WorkflowsでCloud Runの設定を定期的に調整する

0
Posted at

はじめに

最近、TROCCOのSelf-Hosted Runner(自己管理の環境下でデータ転送を行える機能)の技術検証をしているのですが、そのなかでCloud Runの設定を調整する方法が実装できたので、参考までに記事にまとめておきます。

なお、今回はGoogle Cloud環境でCloud SchedulerとCloud Workflowsを利用していますが、AWSやAzureでも同様の考え方で処理できるのではないかと考えています。

こんな方におすすめ

  • TROCCO Self-Hosted Runnerで利用するCloud Runの環境をいい感じに調整したい方
  • Cloud Runの設定を定期的に調整する方法について知りたい方

今回できたこと

仕組みとしては共通ですが、

  • ①Cloud Runのインスタンス数を調整する
  • ②Cloud Runの課金モデルを変更する

の2つの方法をまとめています。

Self-Hosted Runnerでこれらが有用そうなのは、セキュアなデータ転送のためのアウトバウンド通信のみを許可する仕様のため、インバウンドアクセスに応じて自動スケーリングさせるWebアプリケーションとは挙動が異なるためです。

以下に詳細のコードを紹介します。TerraformのHCL形式ですが、設定項目がコード化されているだけなので、GUIでも同様の設定をしていただければ利用可能です。

事前準備

まず、各種の実行のために必要な権限をサービスアカウントに付与します。(他の部分で付与しているものもあるので、漏れがあるかもしれません)

resource "google_project_iam_member" "self_hosted_runner__controller" {
  for_each = toset([
    "roles/workflows.invoker",     # Cloud SchedulerからCloud Workflowsを実行するために必要
    "roles/logging.logWriter",     # Cloud Workflowsのログ出力に必要
    "roles/run.developer",         # Cloud Runの更新に必要
    "roles/iam.serviceAccountUser" # Cloud Runの更新時にサービスアカウントを利用するために必要
  ])
  project = var.project_id
  role    = each.value
  member  = google_service_account.trocco_self_hosted_runner.member
}

では、ここからが実際のコードです。

①Cloud Runのインスタンス数を調整する

処理としては、

  • Cloud Runの設定を取得する
  • 変更したい設定項目を上書きする
  • 変更した設定でCloud Runをデプロイする

という流れになっています。

インスタンス数の調整にあたっては、Cloud Scheduler側で最小/最大インスタンス数を指定し、それをもとにCloud Workflowsを実行することで実現しています。

resource "google_cloud_scheduler_job" "instance_scaler" {
  name      = "shr-test-${var.tested_by}-instance-scaler"
  schedule  = "0 9 * * *" # これは適当な時間
  time_zone = "Asia/Tokyo"
  http_target {
    uri         = "https://workflowexecutions.googleapis.com/v1/projects/${var.project_id}/locations/${var.default_location}/workflows/${google_workflows_workflow.cloud_run_instance_scaler.name}/executions"
    http_method = "POST"
    headers = {
      "Content-Type" = "application/json"
    }
    body = base64encode(jsonencode({
      argument = jsonencode({
        min_instance_count = 1
        max_instance_count = 1
      })
    }))
    oauth_token {
      service_account_email = google_service_account.trocco_self_hosted_runner.email
    }
  }
}

resource "google_workflows_workflow" "cloud_run_instance_scaler" {
  name            = "shr-test-${var.tested_by}-instance-scaler"
  region          = var.default_location
  description     = "Cloud Run Instance Scaler for TROCCO Self-Hosted Runner"
  service_account = google_service_account.trocco_self_hosted_runner.email
  # ref: cloud.google.com/run/docs/reference/rest/v2/projects.locations.services
  source_contents     = <<-EOT
    main:
      params: [args]
      steps:
        - log_start:
            call: sys.log
            args:
              severity: "INFO"
              text: '$${"スケーリング開始: " + ${google_cloud_run_v2_service.trocco_self_hosted_runner.name}'

        - get_current_service:
            call: googleapis.run.v2.projects.locations.services.get
            args:
              name: ${google_cloud_run_v2_service.trocco_self_hosted_runner.id}
            result: current_service

        - update_scaling:
            assign:
              - updated_service: $${current_service}
              - updated_service.template.containers[0].resources.cpuIdle: false # これが課金モデルの設定
              - updated_service.template.scaling.minInstanceCount: $${args.min_instance_count}
              - updated_service.template.scaling.maxInstanceCount: $${args.max_instance_count}

        - update_service:
            call: googleapis.run.v2.projects.locations.services.patch
            args:
              name: ${google_cloud_run_v2_service.trocco_self_hosted_runner.id}
              body: $${updated_service}
            result: updated_result

        - log_success:
            call: sys.log
            args:
              severity: "INFO"
              text: '$${"スケーリング完了: " + ${google_cloud_run_v2_service.trocco_self_hosted_runner.name}'

        - return_result:
            return:
              status: "success"
              service: ${google_cloud_run_v2_service.trocco_self_hosted_runner.name}
              min_instance_count: $${args.min_instance_count}
              max_instance_count: $${args.max_instance_count}
              updated_result: $${updated_result}
  EOT
  deletion_protection = false
}

②Cloud Runの課金モデルを変更する

Cloud Runにはリクエストベースの課金モデルとインスタンスベースの課金モデルがありますが、前者でSelf-Hosted Runnerを動かすと、異様に処理が遅くなります。

これは、外部からのリクエストが全く発生しないSelf-Hosted Runnerでは、リクエストベースの課金モデルではリクエストがないことでCPUがアイドル状態になってしまうからだと思われます。

そこで、

  • ジョブ実行をする時間帯:インスタンスベースの課金モデルとする
  • ジョブ実行をしない時間帯:リクエストベースの課金モデルとする

ことで、費用の最適化ができると想定されます。(リクエストベースの課金モデルで、実際料金が減っているかまでは検証できていないですが、おそらく大丈夫なはず)

resource "google_cloud_scheduler_job" "pricing_controller" {
  name      = "shr-test-${var.tested_by}-pricing-controller"
  schedule  = "0 19 * * *" # これは適当な時間
  time_zone = "Asia/Tokyo"
  http_target {
    uri         = "https://workflowexecutions.googleapis.com/v1/projects/${var.project_id}/locations/${var.default_location}/workflows/${google_workflows_workflow.cloud_run_pricing_controller.name}/executions"
    http_method = "POST"
    headers = {
      "Content-Type" = "application/json"
    }
    body = base64encode(jsonencode({
      argument = jsonencode({
        min_instance_count = 0
        max_instance_count = 1
      })
    }))
    oauth_token {
      service_account_email = google_service_account.trocco_self_hosted_runner.email
    }
  }
}

resource "google_workflows_workflow" "cloud_run_pricing_controller" {
  name                = "shr-test-${var.tested_by}-pricing-controller"
  region              = var.default_location
  description         = "Cloud Run Pricing Controller for TROCCO Self-Hosted Runner"
  service_account     = google_service_account.trocco_self_hosted_runner.email
  source_contents     = <<-EOT
    main:
      params: [args]
      steps:
        - log_start:
            call: sys.log
            args:
              severity: "INFO"
              text: '$${"課金モデル変更開始: " + ${google_cloud_run_v2_service.trocco_self_hosted_runner.name}'

        - get_current_service:
            call: googleapis.run.v2.projects.locations.services.get
            args:
              name: ${google_cloud_run_v2_service.trocco_self_hosted_runner.id}
            result: current_service

        - update_pricing_model:
            assign:
              - updated_service: $${current_service}
              - updated_service.template.containers[0].resources.cpuIdle: true
              - updated_service.template.scaling.minInstanceCount: $${args.min_instance_count}
              - updated_service.template.scaling.maxInstanceCount: $${args.max_instance_count}

        - update_service:
            call: googleapis.run.v2.projects.locations.services.patch
            args:
              name: ${google_cloud_run_v2_service.trocco_self_hosted_runner.id}
              body: $${updated_service}
            result: updated_result

        - log_success:
            call: sys.log
            args:
              severity: "INFO"
              text: '$${"課金モデル変更完了: " + ${google_cloud_run_v2_service.trocco_self_hosted_runner.name}'

        - return_result:
            return:
              status: "success"
              service: ${google_cloud_run_v2_service.trocco_self_hosted_runner.name}
              updated_result: $${updated_result}
  EOT
  deletion_protection = false
}

おわりに

今回初めてCloud Workflowsを利用しました。今回の処理はCloud Run FunctionsでPythonなどのコードを書いてもできますが、yamlだけでサクッとできるのは便利に思いました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?