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 テンプレートの restartPolicy が Never である点が 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: 6、parallelism: 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: M、completions 未指定 | ワーカーがキューから項目を取り出して処理し、キューが空になると終了 |
ワークキューパターンでは各 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.yamlapiVersion: 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: 30suspend: 一時停止 #
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-importk get job の COMPLETIONS 列は 成功/目標 の形式で進行度を示してくれます。DURATION と AGE で所要時間も確認できます。
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 jobsk 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 スケジュールで繰り返す。
schedule・concurrencyPolicy・startingDeadlineSeconds・suspend・履歴の上限で動作を整えます。 - 実習。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 つの戦略を直接実装しながら整理していきます。