Kubernetes と Linux crontab の cron:タイムゾーンの落とし穴
同じ 5 フィールドの cron 文字列が GitHub Actions、K8s CronJob、サーバー crontab で異なる時刻に発火する理由と、安全なプレビュー方法を解説します。
0 9 * * 1-5 は明確に見えます。分 0、9 時、平日。3 つのプラットフォームにデプロイするまで。同じ文字列から 09:00 ローカル、09:00 UTC、コントロールプレーンのゾーンで 09:00 にバックアップが走ることがあります。
この比較は、Linux サーバー、Kubernetes、CI の間で cron 行をタイムゾーンルールを読み直さずにコピーするエンジニア向けです。
同じ文法、異なる時計
クラシック Unix cron は 5 フィールド(分、時、日、月、曜日)を使います。文法は馴染み深いですが、時計 は共有されません。
| プラットフォーム | 典型的なタイムゾーン動作 |
|---|---|
Linux ユーザー crontab | サーバーローカル(timedatectl) |
Kubernetes CronJob | timeZone 未設定ならコントローラーデフォルト(1.25+) |
GitHub Actions schedule | 常に UTC |
| Quartz / 一部クラウド | 多くは 6 フィールド(秒が先) |
上海 VM の /etc/crontab から GitHub workflow にコピーした行は、変換しないと 8 時間ずれます。
Linux crontab:サーバーを知る
VM 上の crontab -e は マシンのローカルゾーン で式を評価します。DST 変更は 30 2 * * * のような壁時計スケジュールで 1 時間スキップまたは重複させることがあります。
ランブックには次を記録してください。
- 式
- サーバーの
timedatectl出力 - ログ相関用の期待 UTC 相当
CloudWatch や Loki のタイムスタンプと相関するときは、インシデント中の暗算ではなく Timestamp Converter で変換してください。
Kubernetes:timeZone を明示する
Kubernetes 1.25 以降、CronJob は次をサポートします。
spec:
timeZone: "Asia/Shanghai"
schedule: "0 9 * * 1-5"
timeZone がなければ、動作はコントローラー設定次第です。ノート PC と同じと仮定しない でください。YAML に timezone がないのはレビューブロッカーとして扱ってください。
クラスターアップグレードとマルチリージョンコントロールプレーンのため、明示的な IANA ゾーンは「いつもローカル」より優れます。
GitHub Actions:UTC のみ
on:
schedule:
- cron: "0 9 * * 1-5"
これは月曜から金曜の 09:00 UTC です。上海の平日 09:00 なら、標準オフセット時は UTC で 0 1 * * 1-5 が必要です。または workflow README にオフセット表を文書化してください。
「なぜ CI が夜中に走った?」というチケットの多くはここから始まります。
マージ前にプレビュー
プラットフォームに関係なく次を行います。
- Cron Expression Parser で式をパース
- 人間向け説明を読む(日 または 曜日の OR セマンティクスに注意)
- UTC と対象ゾーンで次回実行をプレビュー
- ランブック行に timezone + platform を追加
よくあるコピペ失敗
| ミス | 結果 |
|---|---|
| Actions cron をローカル 9 時のつもり | 壁時計とずれる |
K8s で timeZone なし | クラスター移行後にドリフト |
| 6 フィールド Quartz を 5 フィールドとして貼付 | 無効または誤ったスケジュール |
| DOM + DOW を AND 期待で両方設定 | 予期しない追加実行 |
関連学習
フィールド構文、プラットフォーム表、完全デバッグチェックリストは Cron Expressions course にあります。ゾーンをまたぐログ相関は Timestamps in logs across timezones と組み合わせてください。
まとめ
cron 文字列はタイムゾーン非依存。スケジューラーはそうではない。 各行の横に platform + zone を文書化し、UTC とローカルでプレビューし、Kubernetes の timeZone は偶然ではなく意図的に設定してください。