LoginSignup
7
3

More than 1 year has passed since last update.

[Kubernetes] CronJobで .spec.suspend を TRUE に指定するときに注意すべきこと

Posted at

はじめに

CronJobを一時的に止めておきたかったので .spec.suspend を使ってみたのですが、自分的に予想外の動きをしてしまったので、対処方法をまとめておきます。

この .spec.suspend の注意点を見逃すと、CronJobが一時的に停止した状態からJobを生成できなくなります。

対処方法までまとめましたので、是非最後まで読んでいただければと思います。

本記事は、 1.22.12-gke.300 で動作確認しました。

やりたいこと

1分ごとに Hello! と言ってくれるCronJobを作成しました。社交的なCronJobですね。

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

このCronJobの実行を、ある期間だけ止めておきたい。
深夜の寝ている時間はうるさいから止めておきたい、といった感じです。

なので、次のコマンドのように .spec.suspendTRUE にして止めておいて、

kubectl patch cronjob hello  -p '{ "spec": { "suspend": true } }'

動かしたいタイミングで、FALSE にしてまた動かそうと考えました。

kubectl patch cronjob hello  -p '{ "spec": { "suspend": false } }'

.spec.suspend を TRUE に指定したことで何が起きたか

冒頭にお話した通り、CronJobがJobを生成してくれなくなりました。

流れ

  1. 18:00, CronJobを作成。コマンドが実行される。
  2. 21:00, 就寝するため、.spec.suspendTRUE に設定。Jobが作成されなくなる。
  3. 5:00, 起床。 .spec.suspendFALSE に設定。しかしCronJobがJobを生成してくれない。

期待して起きたのに、Hello!と言ってもらえないのは悲しいですね。

なぜ問題が起きたか

.spec.suspendTRUE にすると内部で何が起きているのか、公式ドキュメントに記載されていました。

注意: スケジュールされた時間中にサスペンドされた実行は、見逃されたJob(missed job)としてカウントされます。starting deadlineが設定されていない既存のCronJob.spec.suspendがtrueからfalseに変更されると、見逃されたJobは即座にスケジュールされます。
https://kubernetes.io/ja/docs/tasks/job/automated-tasks-with-cron-jobs/#starting-deadline

つまり、停止中に実行されるはずだったJobは、失敗としてカウントされるようです。そして、CronJobは、生成するJobが100回連続して失敗すると、Jobを作成できなくなります。

公式ドキュメントに記載されていました。

最後にスケジュールされた時刻から現在までの間に、CronJobコントローラーはどれだけスケジュールが間に合わなかったのかをCronJobごとにチェックします。もし、100回以上スケジュールが失敗していると、ジョブは開始されずに、ログにエラーが記録されます。
https://kubernetes.io/ja/docs/concepts/workloads/controllers/cron-jobs/#cron-job-limitations

なぜ100回なのか、これはソースコードのコメントに記載されていました。
https://github.com/kubernetes/kubernetes/blob/release-1.25/pkg/controller/cronjob/utils.go#L96-L112

訳してみると、

金曜日の午後5時1分、皆が帰宅した時にコントローラが動かなくなった場合、火曜日の午前中に誰かが来て問題を発見し、コントローラを再起動すると、1時間毎のcronJobの80以上のジョブが、それ以上の介入なしに全て動き出すはずです。80個よりは多く、「たくさん」よりは少ないということで、多少恣意的に100個を選んでみた。

例えば30分に1回のように、毎時より高頻度で実行する場合や、3連休以上の休暇があった場合は、CronJobはJobを作成できなくなります。 100回 というのはハードコーディングされているため、「設定で変更しよう」といったことは出来なさそうです。

今回の場合は、

停止時間(8h) * 実行間隔(60/h) = 480回

なので、480回失敗としてカウントされていたことになります。

対応したこと

素直に、CronJobを削除、再度作成するほうがよさそうです。

しかし、その他の制約上どうしても .spec.suspend を使いたい(CronJobを削除したくない)場合もあります。
そんなときは、 .spec.schedule を編集することでこの問題を回避することができます。実行頻度を減らして、停止中の失敗回数を減らす作戦です。

例えば、 毎分実行 の場合100分で上限に達してしまいますが、毎時実行 に変更すれば100時間未満まで停止できることになります。

なので、次のコマンドのように .spec.suspendTRUE にする前に、 .spec.schedule を毎時0分実行に編集します。

kubectl patch cronjob hello -p '{ "spec": { "schedule": "0 * * * *" } }'
kubectl patch cronjob hello  -p '{ "spec": { "suspend": true } }'

そして、FALSE に戻す前に、次のコマンドで.spec.schedule を戻してまた動かします。

kubectl patch cronjob hello -p '{ "spec": { "schedule": "* * * * *" } }'
kubectl patch cronjob hello  -p '{ "spec": { "suspend": false } }'

これで、ひとまずCronJobがJobを作成してくれるようになりました。

さいごに

停止中に実行できなかったJobを生成する方法もあります。 .spec.startingDeadlineSeconds を指定するやり方です。

こちらの記事が参考になると思います。

参考

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3