⚒️

Batch と Cloud Run Jobs ってどっち使えばいいの? - Cloud Run Jobs 編 -

2023/05/14に公開

AWS と Google Cloud で構築したデータ基盤の開発・運用に携わっているデータエンジニアです。5 年くらい携わっていて、この業務がきっかけで Google Cloud が好きになりました。

この記事では Google Cloud での定期実行のマネージド化について検証 したいと思います。実装方法について、Batch 編に続き Cloud Run Jobs 編を書きたいと思います。

記事の最後に現状の定期実行を実装している構成(Cloud Functions + Cloud Pub/Sub + Cloud Scheduler)の移行について検討したいと思います。

Overview

Cloud Run Jobs を活用した定期実行方法について検証

  • 最終的には Cloud Run Jobs + Cloud Scheduler で実現できました
  • 現在は Cloud Functions + Cloud Pub/Sub + Cloud Scheduler という構成で実装してあり、もっとこうなると良いという点は下記です


    ◼️ VPC 内で実行したい
    VPC Service Controls を利用しているため Functions でコネクタの利用が必須となっている、VPC SC 適用サービスにアクセスできる代わりにインターネットに出れない
    ⇒ コネクタ利用については Functions と同様に VPC SC 適用サービスへのアクセスには必須、コネクタの設定によっては検討の余地がある


    ◼️ IaC のコード量を減らしたい
    Terraform で記述しているがコード量が多くなってしまっている、より完結に記述できたら似たような構成で定期実行のデプロイをスケールしやすい
    ⇒ Cloud Run Jobs のジョブ定義に加えてコネクタの記述も必須(共通化するかジョブ固有にするかは検討の余地がある)、コードの記述量は多少増加しそう


    ◼️ 新しいサービスを使いたい
    現状の構成は上記のサービスが出る前の構成であるため、よりモダンなものとしたい
    ⇒ Cloud Run はコンテナサービスの中心であり続けるだろうから問題なし

今回要件としているネットワーク周りに不安が残る結果となりました。ただ、コンテナベースで考えるなら設定自体はかなり直感的でわかりやすいですし、デプロイは容易に感じました。

キーワード

Cloud Run Jobs による定期実行

基本的な使い方に関しては参考記事を見ていただければと思います。

1.コンテナの作成

Python で実行するコードを用意しました。サービスアカウントキーが払い出されて、そのキーが作成されてから特定の日数経過していたら削除するというものです。(コードは別の機会に共有します)

Artifact Registry にコンテナイメージを格納します。今回は手っ取り早く Python3 のイメージにコードと必要なライブラリを内包させます。

terminal
# Artifact Registry に image-for-batch というレジストリを作成します
gcloud artifacts repositories create image-for-batch \
--repository-format=docker --location=asia-northeast1

# python3 というコンテナに必要な資材をセットアップしている前提です
docker commit python3 \
asia-northeast1-docker.pkg.dev/image-for-batch/observe-sa-key:latest

# Artifact Registry に認証します
gcloud auth configure-docker asia-northeast1-docker.pkg.dev

# コンテナを格納します
docker push \
asia-northeast1-docker.pkg.dev/image-for-batch/observe-sa-key:latest

2.ジョブの作成および実行

こちらも東京リージョンで利用可能ですね。

こちらはネットワーク設定含めてコンソール画面から各種設定ができそうです。

ネットワーク設定の違い

Batch との違いとして単純に VPC と Subnet を指定するのではなく、Cloud Run Jobs では Serverless VPC Access Connector(以下、サーバーレスコネクタ) を指定する必要がありました。

Cloud Functions などのサーバーレスサービスで特定の VPC を経由したアクセスを実現する機能として Serverless VPC Access (公式ドキュメント)があります。これを実装する方法としてサーバーレスコネクタを作成する必要があります。

今回は下記のコマンドで作成します。

terminal
gcloud beta compute networks vpc-access connectors create connector-for-batch 
  --region asia-northeast1 \
  --subnet batch-subnet --subnet-project [PROJECT_ID]

gcloud コマンドでジョブの作成および実行

参考までに gcloud コマンドでのジョブ作成は下記になります。

terminal
gcloud beta run jobs create observe-serviceaccount-key \
  --image=asia-northeast1-docker.pkg.dev/[PROJECT_ID]/observe-serviceaccount-key/for-batch:latest \
  --command="/bin/bash"
  --args="-c","python3 /root/observe-sa-key.py" \
  --service-account=[SERVICE_ACCOUNT] \
  --region=asia-northeast1 \
  --vpc-connector=connector-for-batch \
  --set-env-vars=GCP_PROJECT=[PROJECT_ID]

ジョブ実行は下記になります。

terminal
gcloud beta run jobs execute observe-serviceaccount-key \
  --region asia-northeast1

3. Cloud Scheduler で定期実行

こちらもコンソールの TRIGGERS タブから簡単に作成可能です。

gcloud コマンドでジョブの作成

コンソールから実施しない場合は gcloud コマンドで Cloud Sheduler を作成します。(Jobs の方のオプションでいけるかと思いましたが、こちらは別で作成が必要みたいですね。)

terminal
gcloud scheduler jobs create http observe-serviceaccount-key-scheduler-trigger \
  --location asia-northeast1 \
  --schedule="*/5 * * * *" \
  --uri="https://asia-northeast1-run.googleapis.com/apis/run.googleapis.com/v1/namespaces/[PROJECT_ID]/jobs/observe-serviceaccount-key:run" \
  --http-method POST \
  --oauth-service-account-email [PROJECT_NUMBER]-compute@developer.gserviceaccount.com

これで Cloud Run Jobs に関しても定期実行を実装できました!
Batch に比べたらすんなり実装できたような気がします。

現構成の移行検討

実行したいジョブ & 比較ポイント

複数ありますが、処理自体は重いものはありません。
冒頭に記載した通り、下記の 3 点は要比較ポイントです。
◼️ VPC 内で実行したい
◼️ IaC のコード量を減らしたい
◼️ 新しいサービスを使いたい

VPC 内で実行したい

今回の構成で一番重要なポイントとして、 VPC Service Controls が適用されているサービス(BigQuery)にアクセスできる&インターネットにアクセスできるという点です。

例えば、BigQuery に関する独自のメトリクスを作成して外部の監視ツールに投げたいといったケースを想定しています。BigQuery はセキュアに運用したいので VPC SC の適用が必須です、一方で監視/可視化の SaaS に投げるとパブリックの経路も確保したいといったケースです。

Batch

VPC を指定してジョブを実行できるため、Google Private Access とともに併用することで VPC SC 適用サービスにもアクセス可能ですしインターネットにもアクセス可能でした

Cloud Run Jobs

こちらはサーバーレスコネクタの設定によってインターネットアクセスの可否が決まるみたいです。
・ Route all traffic through the VPC connector
⇒ 全トラフィックがサーバーレスコネクタ経由となるためインターネットアクセスは不可
・ Route only requests to private IPs through the VPC Connector
⇒ ジョブ内で API を叩く際に public 経由になっている場合には VPC SC 適用サービスにアクセス不可

ジョブ内で API を叩く際に private 経由にできれば後者のエラーにひっかかることなく要件を実現できそうですが、、工夫が必要そうです。

IaC のコード量を減らしたい

Terraform で記述することを想定しています。ネットワークリソースや Cloud Scheduler はどちらも共通なので言及しません。Terraform のサンプルコードを見つつ、新たに定期実行ジョブを作成したいときにすぐに実装できるかどうかを考えたいと思います。

ジョブのプログラムやコンテナ周りも共通なので、ここでは同じく言及しません。

Batch

Workflows の記述がメインになりそうです。記述の大部分は Workflows のジョブ定義になるかと思います。

workflows.tf
resource "google_workflows_workflow" "example" {
  name          = "workflow"
  region        = "us-central1"
  description   = "Magic"
  service_account = google_service_account.test_account.id
  source_contents = <<-EOF
  - getCurrentTime:
      call: http.get
      args:
          url: https://us-central1-workflowsample.cloudfunctions.net/datetime
      result: CurrentDateTime
  - readWikipedia:
      call: http.get
      args:
          url: https://en.wikipedia.org/w/api.php
          query:
              action: opensearch
              search: $${CurrentDateTime.body.dayOfTheWeek}
      result: WikiResult
  - returnOutput:
      return: $${WikiResult.body[1]}
EOF
}

このジョブ定義部分が長くなるとファイル自体も長くなってしまうので、ジョブ定義部分を切り出したくなります。(下記はイメージです。)

workflows.tf
resource "google_workflows_workflow" "example" {
  name          = "workflow"
  region        = "us-central1"
  description   = "Magic"
  service_account = google_service_account.test_account.id
  source_contents = "${file("./jobconfig.yaml")}"
}
jobconfig.yaml
- getCurrentTime:
      call: http.get
      args:
          url: https://us-central1-workflowsample.cloudfunctions.net/datetime
      result: CurrentDateTime
  - readWikipedia:
      call: http.get
      args:
          url: https://en.wikipedia.org/w/api.php
          query:
              action: opensearch
              search: $${CurrentDateTime.body.dayOfTheWeek}
      result: WikiResult
  - returnOutput:
      return: $${WikiResult.body[1]}

こういった形であれば、 Worflows の記述をテンプレ化しておいてジョブ定義ファイルを個々に記述する感じにできるとスケールしやすそうです。

Cloud Run Jobs

ジョブ定義に加えて、サーバーレスコネクタの記述が必要です。サーバーレスコネクタは共通リソースにしてもいいですし、ジョブ固有に作成しても良さそうです。

run-jobs-v2.tf
resource "google_cloud_run_v2_job" "default" {
  name     = "cloudrun-job"
  location = "us-central1"

  template {
    template{
      containers {
        image = "us-docker.pkg.dev/cloudrun/container/hello"
      }
      vpc_access{
        connector = google_vpc_access_connector.connector.id
        egress = "ALL_TRAFFIC"
      }
    }
  }

  lifecycle {
    ignore_changes = [
      launch_stage,
    ]
  }
}

resource "google_vpc_access_connector" "connector" {
  name          = "run-vpc"
  subnet {
    name = google_compute_subnetwork.custom_test.name
  }
  machine_type = "e2-standard-4"
  min_instances = 2
  max_instances = 3
  region        = "us-central1"
}

Cloud Run Jobs のリソースは設定項目が多い分、どの設定項目がジョブによって異なるかを見極めて変数にうまく置き換える必要がありそうです。

新しいサービスを使いたい

こちらに関してはどちらの構成でも現構成よりはモダンな形になるためどちらも良さそうです。

結論

今回のケースでは移行するなら Batch での仕組みを選択したいと思います。

VPC SC 適用サービスにアクセスしつつインターネットへのアクセス経路も確保しようとすると、これでまでの検証結果から Batch の方が実現性が高そうです。

Iac のコード的にもファイル構成を工夫すればスケールもしそうです。一方で、Workflows のジョブ定義は初見では難しく感じました。今回は Batch の定期実行としてサンプルコードがあり、ほぼコピペでいけましたが細部をチューニングするとなると学習コストは多少ありそうです。(※個人の意見です。)

また、現状 Cloud Functions でコードのままで実行しているので初期移行もコンテナを用意する必要がなく容易に実施できそうな点も嬉しいポイントです。

まとめ

マネージドでの定期実行方法として BatchCloud Run Jobs を見てきました。

使い方をまとめつつ、現状の定期実行方法として実装している Cloud Functions + Cloud Pub/Sub + Cloud Scheduler の移行についても検討しました。

Batch と Cloud Run Jobs 自体の定期実行の差分という観点ではなく、環境的な観点で Batch を中心とした仕組みを選択することとしました。

類似ケースで悩んでいる方の助けになれば幸いです。

参考記事

Discussion