> For the complete documentation index, see [llms.txt](https://docs.wantedly.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.wantedly.dev/fields/the-system/cronjob.md).

# CronJob

ウォンテッドリーでは多くのマイクロサービスがバッチ処理を [CronJob](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/) として Kubernetes 上で動かしています。 CronJob は指定した時刻に Job を起動するだけのシンプルなリソースに見えますが、実際の運用では「同じ Job が複数回動く」「予定時刻に動かない」といった挙動に出くわします。 この章では、こうした挙動を踏まえて CronJob を書くときに気をつけることと、社内に用意されている検知・通知の仕組みについて説明します。

CronJob / Job という Object そのものの位置づけについては [プロダクト開発のための Kubernetes 入門](/fields/infrastructure/kubernetes-introduction.md) を参照してください。

## CronJob を書くときに気をつけること

### 冪等に書く

Job は同じスケジュールに対して複数回実行される可能性があります。 後述の二重実行に加え、`backoffLimit` に基づくリトライによっても同じ Job が複数回走ります。 同じ入力に対して何度実行しても結果が変わらないように書いてください。

### 長時間実行する処理は避ける

CronJob が起動した Pod はインフラ側の都合 (ノードの入れ替えなど) で中断される可能性があります。 長くても 3〜4 時間程度におさまるよう処理を分割してください。

### `startingDeadlineSeconds` を設定する

CronJob のスケジュールどおりに Job を起動するという挙動は厳密ではありません。 [Kubernetes 公式ドキュメント](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#cron-job-limitations) に書かれているとおり、コントローラの仕組み上、起動が予定時刻より遅延することがあります。 ウォンテッドリーでの実測でも最大で 70 秒程度遅れることが確認されています。

この遅延に対する許容範囲を制御するのが `startingDeadlineSeconds` で、「未実行のスケジュールを発見したときに何秒までは実行を試みるか」を指定します。 ウォンテッドリーでは原則として `startingDeadlineSeconds: 300` を設定することを推奨しています。 これより小さいと実行漏れが、極端に大きいと意図しない二重実行が起こりえます。 [kube generate](https://docs.wantedly.dev/fields/infrastructure/deploy-strategy-implement#kubernetes-manifest-no) で生成したマニフェストには自動で設定されるため、特別な理由がない限り上書きする必要はありません。

> \[!TIP] **スケジュール変更時の罠**
>
> `startingDeadlineSeconds` を設定していても、スケジュールを「より遅い時刻」に変更した直後は二重実行が発生することがあります。
>
> 例えば毎日 2:00 am に動いていた CronJob を毎日 3:00 am に変更し、その変更を 3:01 am に apply した場合を考えます。 このとき、コントローラは「3:00 am の今日のスケジュールがまだ実行されていない」と判断し、`startingDeadlineSeconds` の範囲内であれば追加で 1 回起動を試みます。 結果として、本来 1 日 1 回しか動かないはずの Job がその日だけ 2 回動いてしまいます。
>
> スケジュールをより遅い時刻に変更する際は、変更後の最初の実行時刻を過ぎてから apply するなど、適用タイミングに注意してください。

## 失敗とスケジュール漏れの検知

ウォンテッドリーでは CronJob 周りの異常を検知する仕組みを社内で運用しています。 検知できるのは下の 2 種類です。

* Job の実行が `backoffLimit` で指定された回数を超えて失敗したとき
* CronJob がスケジュールされた時刻に実行されなかった可能性があるとき

Job が失敗したとき、該当マイクロサービスのリポジトリには GitHub Issue が自動作成されます。 さらに `cronjob-failure.wantedly.com/slack_notification` Annotation を CronJob に書いておくと、上の 2 種類の異常がいずれも Slack に通知されます。 スケジュール漏れの検知には [Honeybadger Check-Ins](https://www.honeybadger.io/check-ins/) を利用しており、CronJob のスケジュールを変更すれば Check-Ins のスケジュールも自動で追従します。

Annotation の詳しい仕様や通知メッセージの構造は [CronJob 失敗通知基盤 (internal)](https://dev-docs.wantedly.com/k8s/cronjob-failure-notification) を参照してください。

## 話を聞きに行きたい

* Slack: [#infra](https://wantedly.slack.com/archives/C010V922570)

## もっと知りたい

* [プロダクト開発のための Kubernetes 入門](/fields/infrastructure/kubernetes-introduction.md) — CronJob / Job という Object そのものの位置づけ
* [Kube](/fields/dev-tools/kube.md) — `kube` コマンドで Job を確認する方法
* [CronJob 失敗通知基盤 (internal)](https://dev-docs.wantedly.com/k8s/cronjob-failure-notification) — Annotation の詳細仕様、関連リポジトリ、社内サービス用 DSL
* [スケジュールジョブ (CronJob) の実行失敗通知基盤における成功と失敗 - Speaker Deck](https://speakerdeck.com/unblee/success-and-failure-in-scheduled-job-execution-failure-notification-infrastructure) — 通知基盤の設計の経緯
