Cloud Run Jobsを使ってバッチ処理を定期実行する


こんにちは、オールアバウトSRE所属の @kyohei_tsuno です。

この記事は、All About Group（株式会社オールアバウト） Advent Calendar 2022 17日目の記事になります。

Google Cloudを活用してバッチ処理を実行させる時、これまではGKEのCronJobを使って実行していました。GKEからCloud Runへ移行するプロジェクトがあり、CronJobの代わりにCloud Run Jobsを導入しています。

今回はCloud Run Jobsに関する簡単な紹介と弊社での活用事例をご説明します。



## Cloud Run Jobsとは?

サーバレスのコンテナ実行環境でバッチ処理などの実行に適したサービスです。2022年5月頃にプレビュー版がリリースされました。
(2022年12月時点でもプレビュー版となっております)

Google CloudのサーバレスサービスにはCloud Run・Cloud Functions等あります。Cloud Run Jobsはバッチ処理などのジョブを使う際に便利な機能として登場しました。

Cloud RunやCloud Functionsと同じくサーバレスのコンテナ実行環境ではありますが、以下の点で違いがあります。

### 主な違い

||Cloud Run|Cloud Functions|Cloud Run Jobs|
|---|---|---|---|
|起動方法|HTTPリクエスト|イベントドリブン・HTTPリクエスト・手動実行|イベントドリブン・手動実行|
|言語|制限なし|制限あり ※URL参照[^1]|制限なし|
|実行時間(MAX)|60分|60分(第二世代・HTTP関数)、10分(イベントドリブン関数)|60分|
|CPU|選択可能(リクエスト処理中のみ/常に割り当て)|メモリのみ設定可能|実行中は常に割り当て|

[^1]:Cloud Functionsの実行環境
https://cloud.google.com/functions/docs/concepts/execution-environment?hl=ja

### リソースモデル

Job、Task、Executionの3つがあります。

* Job

リージョンに紐付くCloud Run Jobsのルートリソース
コンテナイメージの指定や、Task 数、並列数の設定を行う
例：各TaskのCPU/Memory サイズ、環境変数、等の設定
1Jobにつき10,000 Task まで実行可能
1Jobのタイムアウトはなく、全Taskが完了するまでExecutionが継続する

* Task

Jobで指定された数だけ実行されるコンテナインスタンス
1 つの Job に対して、複数の Task が実行されるように設定することが可能
並列で処理される Task の数を制限することも可能
1Taskのタイムアウトはデフォルトで10分、最大1時間

* Execution

Jobの実行を表す。全Task が完了するまで継続されて、
1度作成したJobは何度も実行可能

参考記事：
https://medium.com/google-cloud-jp/cloud-run-jobs-c963a7143367

## 採用した背景

モダナイゼーションとコストダウン・運用簡素化を目的に、各所で必要に応じてGKE→Cloud Run等のサーバレスリソースへの移行を進めております。

各サービスのバッチ処理は基本的にGKEを使ってCronJobで実行していましたが、今回はCloud Run Jobsが上手く活用できそうだったので、動作テスト等で検証後に導入する流れとなりました。

こちらが変更前(GKE)と変更後(Cloud Run Jobs)のバッチ処理の流れになります
![スクリーンショット 2022-12-11 21.58.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985033/1781b8ed-e665-ff16-6ed1-9198b7753ee7.png)



## Cloud Run Jobsの作成方法

以下、作成の流れを説明します
(弊社のアプリケーションではPHP/Laravel, Golang等が使われており、本記事ではPHP/LaravelのCommandsでのバッチ処理を例に記載します)


①事前準備

バッチ処理のテストをする準備として、php artisanコマンドで
app/Console/Commandsディレクトリに新しいコマンドクラスを作成します

```
php artisan make:command SyncTestBatchHoge
php artisan make:command SyncTestBatchFuga
```

```php
<?php

namespace App\Console\Commands;

/**
 * テスト用バッチ
 *
 * @author K.Tsuno
 */
class SyncTestBatchHoge extends BatchBase
{

    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'sync:testBatchHoge';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Test用にHogeを出力する';

    /**
     * 
     * {@inheritDoc}
     * @see \App\Console\Commands\BatchBase::getBatchName()
     */
    protected function getBatchName()
    {
        return 'テスト用のバッチ';
    }

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        echo ('Hoge!');
    }
}
```
※Fugaバージョンは割愛

①Cloud Run Jobsの作成

作成方法は、
1.コンソールから作成
2.gcloudコマンドを用いて構築
という2種類あり、今回はgcloudコマンドから作成していきます

※弊社ではTerraformを使ってインフラリソースの管理・構築を行なっておりますが、Cloud Run JobsはまだTerraformではリリースされていません。

※Github Issueの情報によると、近日中にprovider google v4.46.0でgoogle_cloud_run_v2_jobがリリースされるとの話題が出ていました。[^2]

[^2]:Cloud Run JobsのTerraformサポートに関して
https://github.com/hashicorp/terraform-provider-google/issues/13194
https://github.com/hashicorp/terraform-provider-google/issues/11743

コマンドの詳細：[^3]

[^3]:Cloud Run Jobsのオプション
https://cloud.google.com/sdk/gcloud/reference/beta/run/jobs/create?hl=ja

```
gcloud beta run jobs create Cloud Run Jobs名 \
--image=container image名 \
--command="/bin/sh" \
--command="-c" \
--args="php artisan sync:testBatchHoge && php artisan sync:testBatchFuga" \
--max-retries=0 \
--service-account=service-account名 \
--set-secrets=KEY=SECRET_NAME：SECRET_VERSION \
--vpc-connector=vpc connector名 \
--vpc-egress=all-traffic \
--project=project名 \
--region=region名
```

※set-secretsではSercret Managerのパスワードが利用できます
(SECRET_NAME：SECRET_VERSIONにセットする)

指定したいバッチが1つの場合は、以下のような形で作成できます。
※単語ブロックごとに分けて記載する必要があります

```
--command=php \
--command=artisan \
--command=sync:testBatchHogeHoge \
```

②実行権限の付与

実行者・グループを指定して、コンソールやコマンドの実行権限を付与したい場合、`gcloud beta run jobs add-iam-policy-binding`で作成します

```
gcloud beta run jobs add-iam-policy-binding Cloud Run Jobs名 \
  --member="group:sample_invoker@test.co.jp" \
  --role="roles/run.invoker" \
  --region region名 \
  --project project名

bindings:
- members:
  - group:sample_invoker@test.co.jp
  role: roles/run.invoker
etag: ********
version: 1
```

権限付与の実行結果は以下のコマンドで確認できます

```
gcloud beta run jobs get-iam-policy Cloud Run Jobs名 --project projectID --region region名

bindings:
- members:
  - group:sample_invoker@test.co.jp
  role: roles/run.invoker
etag: ********
version: 1
```

③Cloud Schedulerの作成

Terraformを使って構築しています。
http_target.uriにCloud Run JobsのURIを指定すると、API経由でジョブを呼び出して、scheduleに指定した時間帯で実行してくれるようになります。[^4]

```terra
resource "google_cloud_scheduler_job" "sample_batch_scheduler" {
  name             = "Sample Cloud Run Jobs Schedular"
  region           = "SCHEDULER_REGION"
  description      = "sample cloud run jobs batch schedular"
  schedule         = "*/10 * * * *"
  time_zone        = "Asia/Tokyo"

  http_target {
    http_method = "POST"
    // Google CloudのAPI経由でJobの実行を呼び出す
    uri         = "https://CLOUD_RUN_REGION-run.googleapis.com/apis/run.googleapis.com/v1/namespaces/PROJECT-ID/jobs/JOB-NAME:run"

    oauth_token {
      service_account_email = module.scheduler_service_account.email
    }
  }
}
```

[^4]:スケジュールに従ってジョブを実行する
https://cloud.google.com/run/docs/execute/jobs-on-schedule?hl=ja

④Schedular用サービスアカウントの作成

```
module "scheduler_service_account" {
  source  = "terraform-google-modules/service-accounts/google"
  version = "~> 3.0"

  display_name = "batch-schedular-service-account"
  description  = "sample cloud run jobs invoker"
  project_id   = project_id
  names        = ["batch-schedular-invoker"]
  project_roles = [
    "${var.project_id}=>roles/run.invoker",
    "${var.project_id}=>roles/secretmanager.secretAccessor",
  ]
}
```

roles/run.invoker権限が必要なので付与しています。Secret Managerのパスワード取得用にroles/secretmanager.secretAccessorもつけています。

これで起動準備が出来ました!
あとはスケジュールの間隔に沿って定期実行してくれます



## コンソール画面

Cloud Run Jobsのコンソール画面を少しだけ紹介します
※前述の作成手順のものとは異なります

構成画面：

作成したジョブの詳細設定を確認・更新ができます

![スクリーンショット 2022-12-10 16.21.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985033/beca970c-41d0-9ed5-6322-e41b65e81d24.png)

履歴：

ジョブの実行履歴が確認できます
失敗した時のデバッグ時に確認しやすい点が便利です

![スクリーンショット 2022-12-10 16.13.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985033/116d90ba-5700-f12d-08f9-f6c3cb2a7936.png)

ジョブ 成功時：

![スクリーンショット 2022-12-10 16.14.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985033/f1363f84-b847-dd96-3b9b-5ea531edc780.png)

ジョブ 失敗時：

![スクリーンショット 2022-12-10 16.13.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985033/ba90561f-da23-d048-6176-74d4aa5ff52b.png)

## 所感

今回ご紹介した内容はシンプルな1Taskの直列処理のみでした。複数のタスクがあり同時実行させたい場合、並列処理を活用する事でよりスピーディーなジョブの実行が可能となります。

個人的には、ジョブの実行時間、実行者、成功・失敗時のログ等がGKEのCronJobよりもコンソールから視認しやすく感じました。ジョブの実行時以外はコンテナが起動しない為、使い方次第でコストも抑えられます。

GKEの場合はKubernetesに関する基礎知識がないと、CronJobの挙動把握・失敗時の原因調査等が難しい面もあります。Cloud Run Jobsの方がアプリケーション開発者も仕様理解が比較的容易で、権限譲渡した運用をお任せしやすいかと思いました。

以上、Cloud Run Jobsを使ったバッチ処理の簡単な紹介記事でした。
今後の正式リリース版やTerrafrom対応が楽しみです。