案例 容器 cron 9 点变 17 点:默认 UTC、DST 漂移与 K8s CronJob timeZone

2026-09-07 00:05:40

容器 cron 9 点变 17 点:默认 UTC、DST 漂移与 K8s CronJob timeZone

现象与根因:cron 读的是系统时区

cron 默认使用系统时区 /etc/localtime。容器镜像没有显式设置时,多数是 UTC,于是:开发机是 Asia/Shanghai,部署到容器后仍写 0 9 * * *,本意是北京时间 9:00,实际却在北京时间 17:00(UTC 09:00)才跑。

更隐蔽的是集群多台机器:有的设过 Asia/Shanghai,有的忘了,同一条 cron 在不同节点会错开 8 小时。

修复:显式设时区,或按 UTC 换算

Dockerfile 里固定时区并同步 /etc/localtime

ENV TZ=Asia/Shanghai
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

不想碰镜像,就把 cron 时刻直接按 UTC 换算:本意北京 9:00 就写 0 1 * * * UTC。

K8s CronJob:1.27 起才有 timeZone 字段

1.27 之前的 CronJob 没有 timeZone 字段,调度按 kube-controller-manager 所在节点时区走(多数镜像默认 UTC)。要指定时区:

  • spec.timeZone,1.25 进入 beta,1.27 GA。

升级前,只改 CronJob 里的 cron 表达式不解决集群内时区不一致问题;1.27+ 直接显式声明 timeZone 更干净。

夏令时:2:30 要么不存在,要么出现两次

以美国夏令时为例:

场景时刻cron 行为
春季拨快02:00 变 03:00,02:30 不存在多数 cron 实现不执行,或顺延到 03:00
秋季拨慢02:00 回拨到 01:00,02:30 出现两次默认可能执行两次

Vixie cron(Linux 默认 cron)文档对「跳过/重复」有明确定义;Quartz、Spring、K8s 的语义各有差异。Asia/Shanghai 全年不实行 DST,不受影响,但容器和调度器一旦碰到其他时区就要留意。

CRON_TZ:任务级时区

不想改系统 /etc/localtime,可以在 crontab 顶部为单条任务指定时区:

CRON_TZ=Asia/Shanghai
0 9 * * * /path/to/script

关键任务务必避开 02:00–03:00 窗口,放到 04:00 之后最安全。

anacron:关机后的补跑

anacron 用于系统在计划时间没运行(比如关机)时,下次启动补跑错过的任务。配合 DST 的重复/跳过,补跑机制可能让同一任务出现多跑或晚跑,业务侧需要幂等保护和看门狗:按时间窗口或任务标记去重,再辅以流水线水位告警,不能只依赖 cron 语义兜底。

复制全文 生成海报 cron 定时任务 时区 DST Kubernetes

推荐文章

程序员茄子在线接单