
Cloud Run ジョブの並列実行方法について考えてみた
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
最近Cloud Run ジョブと真摯に向き合っている、データアナリティクス事業本部の根本です。
皆さんはCloud Run ジョブで並列処理を行なってバッチ処理の所要時間を短縮したいと思ったことはありませんか?今回はCloud Run ジョブでの並列処理の実行方法について考えてみました。同じようなことを考えている人はぜひ読んでみてください。
この記事の対象者
- Cloud Run ジョブで並列処理を実装したいと考えているひと
Cloud Run ジョブでの並列処理のパターン
どのような並列処理のパターンがあるか
Cloud Run ジョブで並列処理を行う場合、以下の2パターンが考えられます(Workflowsからの呼び出しを例にしています)。
それぞれのパターンに関してメリットデメリットを考えてみました。
パターン | メリット | デメリット |
---|---|---|
1ジョブで複数Taskを起動 | 1つのジョブを起動すれば並列処理できる | 1回の呼び出しで複数Taskをそれぞれ実行する実装が必要。環境変数を分割するなど呼び出し元、ジョブ側での環境変数の受け取り方も考える必要がある |
1ジョブで1Taskを実行で複数起動 | 実装が容易 | 呼び出しもとで複数回呼び出す必要がある |
それぞれのパターンを深掘りしてみましょう。
1つのジョブで複数のTaskを並列処理
この場合、ジョブの実装に注意する必要があります。
まず、Cloud Run ジョブのTaskを制御するにはCLOUD_RUN_TASK_INDEX
という環境変数を用います。複数のTaskを並列処理する簡単な実装イメージとしては以下となります。
CLOUD_RUN_TASK_INDEX == 1:
処理①
CLOUD_RUN_TASK_INDEX == 2:
処理②
この場合の、向いているユースケースとしては以下が考えられます。
- 各Taskで行いたい処理が決まっている場合
- 呼び出しを一回で済ませたい場合(forループなどの反復処理を用いたくない)
1ジョブ1Taskで複数起動
この場合は、呼び出しもとの実装とジョブの環境変数に注意する必要があります。
Workflowsから並列Forループで起動すると仮定した場合、
1Taskで処理を行うので環境変数を用いて処理を可変にするようにしないと同一の処理が複数回起動されることになってしまいます。
例えば環境変数で$FILE_NAME
をスクリプトで用いていた場合、環境変数を実行時にオーバーライドして呼び出さないと同じ処理を複数回実行してしまいます。
環境変数のオーバーライドとはなんぞや、という方はこちらの記事も見てみてください。
実際の実装をもとに見てみましょう。
BUCKET_NAME="your-bucket-name"
FILE_NAME=$ENV_FILE_NAME
LOCAL_FILE_PATH="/tmp/$FILE_NAME"
COMPRESSED_FILE_PATH="/tmp/$FILE_NAME.gz"
# GCSからファイルをダウンロード
gsutil cp gs://$BUCKET_NAME/$FILE_NAME $LOCAL_FILE_PATH
# ファイルをgzipで圧縮
gzip -c $LOCAL_FILE_PATH > $COMPRESSED_FILE_PATH
# 圧縮ファイルをGCSにアップロード
gsutil cp $COMPRESSED_FILE_PATH gs://$BUCKET_NAME/$FILE_NAME.gz
上記スクリプトではENV_FILE_NAME
という環境変数をもとにCloud Storageからファイルをダウンロードしてgzip形式に圧縮して再度Cloud Storageにアップロードするという処理を行なっています。
このジョブを起動するWorkflowsを以下で実装します。
main:
steps:
- init:
assign:
- project_id: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
- job_name: hello-cloud-run
- job_location: asia-northeast1
- list: ["test1.txt", "test2.txt", "test3.txt"]
- loopFor:
parallel:
for:
value: file_name
in: ${list}
steps:
- run_job:
call: googleapis.run.v1.namespaces.jobs.run
args:
name: ${"namespaces/" + project_id + "/jobs/" + job_name}
location: ${job_location}
body:
overrides:
containerOverrides:
env:
- name: ENV_FILE_NAME
value: ${file_name}
result: job_execution
Workflowsの実装としては、parallel
ステップ内でファイル名の配列をもとにForループを回し、ループ内でCloud Run ジョブを実行します。
ジョブ実行の際にはoverrides
ステップにてコンテナ環境変数をオーバーライドしてそれぞれのCloud Run ジョブを実行します。
このような実装をすることで、1つのジョブ1つのタスクで並列処理を実行することが可能となります。
この場合の、向いているユースケースとしては以下が考えられます。
- 呼び出しもとで処理を出し分けたい場合
- Clou Run ジョブの実装を簡潔なものにしたい場合
ただし、Workflowsから呼び出す場合はWorkflowsの並列処理の上限も考慮しなくてはいけないです。極度に並列数の大きいワークロードの場合、
複数Taskを持ったジョブをWorkflowsから呼び出すというパターン1と2の合わせ技のような実装も良いかもしれません。
まとめ
Cloud Run ジョブで並列処理をするパターンを考えてみました。私は、パターン2の1つのジョブを複数回呼び出す方がマイクロサービスぽいですし実装もシンプルになるので好きだなと考えました。とはいえ処理が決まっているのであればシンプルなのでパターン1も全然ありだなと思います。例えば1日一回特定ファイル群を並列処理する場合とか。それぞれのTaskに決まりきったファイルを処理させる実装をすれば良いので1ジョブで複数Taskを呼び分けることも容易と思います。
Cloud Run は考えれば考えるほど色々なことが可能で、本当に奥ゆかしいサービスだなと思います。今後もどんどん深掘りしていきたいです!
この記事がどなたかのお役に立てば嬉しいです。それではまた。