Certified Kubernetes Application Developer (CKAD) #7 Workloads 3: Job, CronJob (バックオフ、同時実行)

#5 で Deployment による無停止デプロイを身につけ、#6 で DaemonSet と StatefulSet によるノード単位・状態保存のワークロードを扱いました。今回は性格がまったく異なるワークロードを見ていきます。Deployment が 常に起動し続けるべきサービス のためのものだとすれば、Job は 一度実行して終わる処理 のためのものです。データマイグレーション・バックアップ・バッチ演算のように、完了すればそれで終わりの仕事がここに当たります。

サービス用の Pod が落ちたら再び起こさなければなりませんが、バッチ処理の Pod は正常に終わったらもう再起動してはいけません。この「終わり」を扱うのが Job の核心です。そしてその Job を cron 表記で定期実行するのが CronJob です。試験では backoffLimit と concurrencyPolicy がとくに頻出なので、今回の記事で YAML と kubectl の両方で手に馴染ませていきます。

Job: 一度実行して終わる処理 #

Job は 1 つ以上の Pod を作り、指定した回数だけ正常に完了 するまで保証するワークロードです。Deployment が常に N 個の Pod を起動し続けるのとは違い、Job は決められた処理が終わると Pod をそれ以上作らずに止まります。

もっとも単純な Job を generator で生成してみます。

k create job pi --image=perl:5.34 \
  $do -- perl -Mbignum=bpi -wle 'print bpi(2000)' > job.yaml

$do#1 で定義した --dry-run=client -o yaml です。上のコマンドが作った骨組みは次のとおりです。

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
        - name: pi
          image: perl:5.34
          command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

ここで Job は apiVersion: batch/v1 である点、そして Pod テンプレートの restartPolicyNever である点が Deployment と異なります。

restartPolicy: Job では Always が禁止される #

Job の Pod テンプレートには restartPolicy として OnFailure または Never だけが使えます。Deployment とは違い Always は許可されません。処理が終わったら再び回してはいけないからです。

動作
Never失敗した Pod を再起動せず、Job が 新しい Pod を作って再試行
OnFailure同じ Pod の中で コンテナを再起動して再試行

Never は失敗するたびに Pod が新たに積み重なってデバッグログが残るという利点があり、OnFailure は Pod 数を増やさないという利点があります。試験では問題の要求にそのまま従えば大丈夫です。

completions と parallelism #

Job の動作は 2 つのフィールドで決まります。

フィールド意味デフォルト値
completions成功とみなすには何回完了する必要があるか1
parallelism同時に何個の Pod を実行するか1

たとえば completions: 6parallelism: 2 なら、Job は一度に 2 個ずつ Pod を回し、合計 6 回の成功を満たすまで進みます。

backoffLimit: 再試行の上限 #

backoffLimit は Job が失敗を何回まで再試行するかを定める上限です。この回数を超えると Job は Failed と表示され、もう新しい Pod を作りません。デフォルト値は 6 です。

spec:
  backoffLimit: 4

再試行の間にはしだいに長くなる遅延 (exponential backoff) が適用されます。無限に失敗する処理がクラスターのリソースを食いつぶさないようにする安全装置です。試験で「最大 N 回まで再試行せよ」という要求が出たら、このフィールドを押さえれば大丈夫です。

activeDeadlineSeconds: 時間制限 #

activeDeadlineSeconds は Job が開始されたあとこの秒数を超えると強制的に終了させる時間制限です。backoffLimit が 回数 基準なら、こちらは 時間 基準です。2 つのうち先に到達した条件が適用されます。

spec:
  activeDeadlineSeconds: 100

時間を超えて終了すると、Job は DeadlineExceeded の理由で失敗扱いになります。

ttlSecondsAfterFinished: 自動クリーンアップ #

完了または失敗した Job は、デフォルトではクラスターにそのまま残ります。ttlSecondsAfterFinished を設定すると、終了したあと指定した秒数が経過したときに Job とその Pod が自動的に削除されます。

spec:
  ttlSecondsAfterFinished: 60

バッチ処理が終わったあとに古い Job が積み重なるのを防ぐ用途で、0 にすると完了と同時に削除されます。

並列実行パターン #

Job は completions と parallelism の組み合わせで 3 つの代表的なパターンを作ります。

パターン設定使いどころ
単一処理completions 未指定、parallelism 未指定一度回って終わる単発の処理
固定完了数completions: N (並列は任意)N 個の独立した項目を処理
ワークキューparallelism: Mcompletions 未指定ワーカーがキューから項目を取り出して処理し、キューが空になると終了

ワークキューパターンでは各 Pod が外部のキューを見て自分で仕事を取りに行くので、completions を空けたまま parallelism でワーカー数だけを決めます。

固定完了数 + 並列 Job の例 #

6 回の完了を一度に 3 個ずつ処理し、再試行は 2 回までしか許可しない Job です。

apiVersion: batch/v1
kind: Job
metadata:
  name: batch-import
spec:
  completions: 6
  parallelism: 3
  backoffLimit: 2
  activeDeadlineSeconds: 120
  ttlSecondsAfterFinished: 120
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: importer
          image: busybox:1.36
          command: ["sh", "-c", "echo importing && sleep 5"]

この Job は同時に 3 個の Pod を回して合計 6 回の成功を満たし、2 回を超えて失敗すると止まり、120 秒を超えると強制終了され、終わったあと 120 秒が経過すると自動的にクリーンアップされます。

CronJob: Job を定期的に回す #

CronJob は決められたスケジュールに従って Job を作り出すワークロードです。Job が一回限りなら、CronJob はそれを 繰り返し ます。バックアップを毎日早朝に回したり、レポートを 5 分ごとに生成したりする処理がここに当たります。

generator で骨組みを作ってみます。

k create cronjob report \
  --image=busybox:1.36 \
  --schedule="*/5 * * * *" \
  $do -- /bin/sh -c 'date; echo report' > cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: report
spec:
  schedule: "*/5 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: report
              image: busybox:1.36
              command: ["/bin/sh", "-c", "date; echo report"]

CronJob は spec.jobTemplate の下に Job の spec をそのまま抱え、その中にさらに Pod テンプレートが入る 3 段ネスト の構造です。試験でインデントをもっとも間違えやすいリソースなので、generator で生成するほうが安全です。

schedule: cron 表記 #

schedule は標準的な cron の 5 桁表記を使います。

┌── 分 (0〜59)
│ ┌── 時 (0〜23)
│ │ ┌── 日 (1〜31)
│ │ │ ┌── 月 (1〜12)
│ │ │ │ ┌── 曜日 (0〜6, 0=日曜日)
│ │ │ │ │
* * * * *
表記意味
*/5 * * * *5 分ごと
0 * * * *毎時 0 分
0 2 * * *毎日早朝 2 時
0 0 * * 0毎週日曜日の深夜 0 時

concurrencyPolicy: 同時実行ポリシー #

前回の実行がまだ終わっていないのに次のスケジュールが到来したらどうするかを定めるのが concurrencyPolicy です。試験の常連なので、3 つの値の違いを正確に覚えておきます。

動作
Allow (デフォルト)同時実行を許可。前の Job が回っていても新しい Job を生成
Forbid前の Job が終わっていなければ新しいスケジュールを スキップ
Replace前の Job を キャンセルして 新しい Job に置き換え

重なってはいけないバックアップ処理には Forbid、最新の実行だけが意味を持つ処理には Replace が適切です。

startingDeadlineSeconds: 開始の締め切り #

コントローラーが停止していたりノードの問題で予定の時刻に Job を開始できなかったとき、この秒数以内なら遅れてでも開始し、その締め切りを超えたら当該の実行をスキップさせるフィールドです。

spec:
  startingDeadlineSeconds: 30

suspend: 一時停止 #

suspend: true にすると CronJob は新しい Job を作らずに止まります。点検中に定期処理をしばらく止めておくときに使います。すでに回っている Job には影響を与えません。

# 一時停止
k patch cronjob report -p '{"spec":{"suspend":true}}'

# 再開
k patch cronjob report -p '{"spec":{"suspend":false}}'

履歴の上限 #

CronJob は終わった Job を一定の個数だけ保管します。

フィールド意味デフォルト値
successfulJobsHistoryLimit成功した Job を何個まで残すか3
failedJobsHistoryLimit失敗した Job を何個まで残すか1

古い Job が無限に積み重ならないようにする設定で、デバッグのためにより多くの履歴を残したければ値を上げます。

concurrencyPolicy を適用した CronJob の例 #

重なる実行を防ぎ、遅い開始を 30 秒までしか許可せず、履歴を減らしておいたバックアップ CronJob です。

apiVersion: batch/v1
kind: CronJob
metadata:
  name: db-backup
spec:
  schedule: "0 2 * * *"
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 30
  successfulJobsHistoryLimit: 5
  failedJobsHistoryLimit: 2
  jobTemplate:
    spec:
      backoffLimit: 3
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: backup
              image: busybox:1.36
              command: ["/bin/sh", "-c", "echo backing up && sleep 10"]

毎日早朝 2 時に回りますが、前回のバックアップが終わっていなければ今回の実行はスキップし、jobTemplate の中の backoffLimit で失敗時に 3 回まで再試行します。

実習: Job と CronJob を扱う #

Job を作ってログを確認 #

# Job 生成
k apply -f job.yaml

# 状態確認 (COMPLETIONS 列が 6/6 になれば完了)
k get job batch-import

# Job が作った Pod を見る
k get pods -l job-name=batch-import

# ログ確認 (Job 名で 1 つの Pod のログを見る)
k logs job/batch-import

# 詳しい進行状況とイベント
k describe job batch-import

k get jobCOMPLETIONS 列は 成功/目標 の形式で進行度を示してくれます。DURATIONAGE で所要時間も確認できます。

CronJob を扱う・手動でトリガー #

CronJob はスケジュールが到来しないと Job を作らないため、試験で動作をすぐに確認するには手動でトリガーします。

# CronJob 生成
k apply -f cronjob.yaml

# 登録確認 (SCHEDULE・LAST SCHEDULE・ACTIVE 列)
k get cronjob db-backup

# スケジュールを待たずに今すぐ一度実行 (CronJob から Job 生成)
k create job manual-run --from=cronjob/db-backup

# 手動実行した Job のログ
k logs job/manual-run

# CronJob が作った Job の一覧
k get jobs

k create job <名前> --from=cronjob/<CronJob 名> は CronJob の jobTemplate をそのまま複製して Job を即座に生成します。採点前に処理がきちんと回るかを確認するのにもっとも速い方法です。

試験のポイント #

  • Job は batch/v1。apiVersion を間違えるとリソースが作られません。generator で生成すれば自動的に合います。
  • restartPolicy は Never または OnFailure のみ。Job と CronJob の Pod テンプレートに Always を使うと拒否されます。
  • backoffLimit は常連。「最大 N 回再試行」の要求が出たらこのフィールドです。デフォルト値 6 を覚えておくと速いです。
  • concurrencyPolicy の 3 つの値Allow (デフォルト)・Forbid (スキップ)・Replace (置き換え) の違いを正確に区別する問題がよく出ます。
  • CronJob は 3 段ネストjobTemplate.spec.template.spec まで入る構造なので、インデントの事故が多くなります。generator で骨組みを作ってからフィールドだけ直すほうが安全です。
  • 手動トリガー。CronJob をすぐに検証するには k create job --from=cronjob/<名前> を使います。
  • completions と parallelism の組み合わせ で単一・固定・キューのパターンが分かれます。どちらもデフォルト値は 1 です。

まとめ #

この記事で押さえたこと:

  • Job は一度実行して終わるバッチワークロードcompletions で目標完了数を、parallelism で同時実行数を定めます。
  • 再試行と上限backoffLimit (回数)・activeDeadlineSeconds (時間) で失敗を制御し、ttlSecondsAfterFinished で終わった Job を自動的にクリーンアップします。
  • restartPolicy は Never または OnFailure。Job で Always は使えません。
  • CronJob は Job を cron スケジュールで繰り返すscheduleconcurrencyPolicystartingDeadlineSecondssuspend・履歴の上限で動作を整えます。
  • 実習。Job は k logs job/<名前> で、CronJob は k create job --from=cronjob/<名前> で手動トリガーして検証します。

バッチワークロードの概念をより広く押さえたければ、Kubernetes 中級シリーズ も合わせて読むとよいです。

次へ: Deployment 戦略 #

ワークロード 3 種 (Job・CronJob) まで整理し、#5〜#7 のワークロードのまとまりを終えました。次はそのワークロードを どう入れ替えるか の問題に戻ります。

#8 Deployment 戦略: Blue-green、canary では、1 つのバージョンを丸ごと移す blue-green デプロイと、一部のトラフィックだけを新しいバージョンに流す canary デプロイを扱います。Deployment と Service、そしてラベルセレクターを組み合わせて、試験で 2 つの戦略を直接実装しながら整理していきます。

X