(2020年10月の検証記事です)
KubernetesのCronJobは、Job同様にオプション設定によってPodの扱いが変わってくるようなので試してみた。実行環境はMacOS上のminikube。
気になっていた設定は以下。
successfulJobsHistoryLimit(成功時のPod履歴数。デフォルト3)
failedJobsHistoryLimit(失敗時のPod履歴数。デフォルト1)
restartPolicy (NeverかOnFailureか)
backoffLimit (失敗時のリトライ数。デフォルト6だが0にする必要があるか)
上の2つは履歴数といっても実際にPodがその数だけ残るので、リソースが逼迫気味なワーカーノードでは気にしたいところ。
以下サンプルマニフェスト。dateコマンド結果を吐き出すだけの無意味なタスク。
cron_test_never.yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: cron-test
spec:
schedule: "*/1 * * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 2
failedJobsHistoryLimit: 2
jobTemplate:
spec:
template:
spec:
containers:
- name: cron-test
image: busybox
command: ["/bin/sh", "-c", "echo $(date -u) > /tmp/date.log"]
restartPolicy: Never
今回は深追いしていないが、concurrencyPolicy: Forbid は同時実行を抑止するため設定。backofflimit:0 はこの段階では設定しない。
実行結果
$ kubectl get cronjob
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
cron-test */1 * * * * False 0 <none> 7s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603592640-58wdq 0/1 Completed 0 54s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603592640-58wdq 0/1 Completed 0 84s
cron-test-1603592700-8wlrx 0/1 Completed 0 24s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603592700-8wlrx 0/1 Completed 0 77s
cron-test-1603592760-jdbcn 0/1 Completed 0 17s
指定通り、Podが2世代まで残る。次にsuccessfulJobsHistoryLimit: 0 にしてapply。
$ kubectl get po
No resources found in default namespace.
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603593300-vv7zd 0/1 ContainerCreating 0 1s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603593300-vv7zd 0/1 ContainerCreating 0 4s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603593300-vv7zd 0/1 Completed 0 7s
$ kubectl get po
No resources found in default namespace.
上記の繰り返しとなる。Podは実行中のみ存在し、Pod生成後10秒まではステータス取得可能らしい。restartPolicy: OnFailureでも同様のパターンでapplyしてみたが、正常時の動作はNeverと変わらなかった。
CronJobではsuccessfulJobsHistoryLimit: 0にしておけば、backofflimit:0 設定は必要ない?と考えたが、失敗時の動作も見ておく必要があるのでそれも試してみた。
エラーパターンではcommandの命令を以下のようにして、存在しないパスを指定した。
command: ["/usr/sh", "-c", "echo $(date -u) > /tmp/date.log"]
restartPolicy: Never エラーケース
restartPolicy: Never
failedJobsHistoryLimit: 2
backokfflimit指定なし
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603596240-srrjj 0/1 ContainerCreating 0 4s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603596240-kwk4q 0/1 ContainerCannotRun 0 6s
cron-test-1603596240-srrjj 0/1 ContainerCannotRun 0 11s
(snip)
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603596240-dxvt2 0/1 ContainerCannotRun 0 58s
cron-test-1603596240-kwk4q 0/1 ContainerCannotRun 0 68s
cron-test-1603596240-m69th 0/1 ContainerCannotRun 0 38s
cron-test-1603596240-srrjj 0/1 ContainerCannotRun 0 73s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603596240-7b42n 0/1 ContainerCannotRun 0 7s
cron-test-1603596240-dxvt2 0/1 ContainerCannotRun 0 107s
cron-test-1603596240-kwk4q 0/1 ContainerCannotRun 0 117s
cron-test-1603596240-m69th 0/1 ContainerCannotRun 0 87s
cron-test-1603596240-srrjj 0/1 ContainerCannotRun 0 2m2s
failedJobsHistoryLimit: 2に関わらず、backofflimitのデフォルト値6(実際は5)の分だけPodが残ってしまう!
ここで、backokfflimit:0をセットしてapply。
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: cron-test
spec:
schedule: "*/1 * * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 2
failedJobsHistoryLimit: 2
jobTemplate:
spec:
backoffLimit: 0
template:
spec:
containers:
- name: cron-test
image: busybox
command: ["/usr/sh", "-c", "echo $(date -u) > /tmp/date.log"]
restartPolicy: Never
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603596900-2fgfp 0/1 ContainerCannotRun 0 2m10s
cron-test-1603596960-rtvj2 0/1 ContainerCannotRun 0 69s
cron-test-1603597020-mhgm8 0/1 ContainerCannotRun 0 9s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603597020-mhgm8 0/1 ContainerCannotRun 0 96s
cron-test-1603597080-548zl 0/1 ContainerCannotRun 0 36s
backokfflimit:0により、failedJobsHistoryLimit: 2の指定通りに世代が残った。
次にfailedJobsHistoryLimit: 0にしてapply。すると、Podが存在するのはタスク実行時のみとなった。
$ kubectl get po
No resources found in default namespace.
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603597320-8kk2g 0/1 ContainerCannotRun 0 5s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603597320-8kk2g 0/1 ContainerCannotRun 0 8s
$ kubectl get po
No resources found in default namespace.
確かに余分なリソースは食わないが、エラー調査がまったくできない。そこを考慮するならば、failedJobsHistoryLimit: 1が理想的かもしれない。
restartPolicy: OnFailure エラーケース
restartPolicy: OnFailure
failedJobsHistoryLimit: 2
backokfflimit指定なし
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603597980-swr2d 0/1 RunContainerError 0 19s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603597980-swr2d 0/1 RunContainerError 1 32s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603597980-swr2d 0/1 RunContainerError 2 42s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603597980-swr2d 0/1 RunContainerError 3 66s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603597980-swr2d 0/1 CrashLoopBackOff 4 2m15s
failedJobsHistoryLimit: 2は影響せず、同一podでリスタートを繰り返す。Neverパターンではステータスが"ContainerCannotRun"だったが、OnFailureでは"RunContainerError"となる。途中からCrashLoopBackOffになった。
次にbackokfflimit:0をセットしてapply。
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603598820-vnrfr 0/1 ContainerCreating 0 2s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603598820-vnrfr 0/1 RunContainerError 0 6s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603598820-vnrfr 0/1 RunContainerError 0 22s
$ kubectl get po
No resources found in default namespace.
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603598880-l2jmx 0/1 RunContainerError 0 12s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603598880-l2jmx 0/1 Terminating 1 23s
$ kubectl get po
No resources found in default namespace.
起動されるPodは常に1つだが、異なるPodが起動された。次にfailedJobsHistoryLimit: 0にしてapplyしたところ、動作としては上記と変わらず、起動の都度Podが変化した。Podがわずかな時間しか存在しないため調査が困難と想定する。
結局どうすればいいか
成功時も失敗時も余分なPodを起動させないようにしたい。ただしエラー発生時は調査可能にしておきたい。であれば、以下の設定が妥当かと思われる。
successfulJobsHistoryLimit: 0
failedJobsHistoryLimit: 1
backoffLimit: 0
restartPolicy: Never
上記ステータスで改めてapply。
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603600380-t8jgq 0/1 ContainerCreating 0 3s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603600380-t8jgq 0/1 ContainerCannotRun 0 57s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
cron-test-1603600440-pzf5r 0/1 ContainerCannotRun 0 24s #1分後は次のPodが起動
PodはCron実行時間単位で新たに起動されるので、1分毎に実行の場合はその間にログを取得する必要がある…といっても次に起動したPodで取得してもいいんだから、まぁどうにかなるか。