<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>定时 · XB 现场笔记</title><link>https://www.xbcatm.com/zh/tags/%E5%AE%9A%E6%97%B6/</link><description>基于 HTTP、DNS、TLS、Caddy、SSH、发布与恢复的可复现运维现场笔记。</description><generator>Hugo</generator><language>zh-CN</language><atom:link href="https://www.xbcatm.com/zh/tags/%E5%AE%9A%E6%97%B6/index.xml" rel="self" type="application/rss+xml"/><item><title>CronJob 把时间表变成 Job</title><link>https://www.xbcatm.com/zh/posts/k8s-cronjob-schedules-batch-work/</link><pubDate>Fri, 17 Dec 2027 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/zh/posts/k8s-cronjob-schedules-batch-work/</guid><description>CronJob 近似按时间表创建 Job；并发策略、启动截止时间与业务幂等性共同决定批处理是否有用。</description><content:encoded>&lt;p&gt;CronJob 把一份 Job 模板与循环时间表组合起来。CronJob 控制器负责创建 Job，再由 Job 管理执行工作的 Pod。一次运行可以包含重试、替换 Pod，或按照 Job 配置启动多个并行工作者，不应把它简化为每次只启动一个短命容器。两次运行之间不需要专门的应用进程等候触发，但已经完成的 Job 和 Pod 可能继续保留，供排查与审计使用。&lt;/p&gt;
&lt;p&gt;时间表只是近似兑现。某个时刻可能生成重复 Job，也可能完全漏掉，因此不能无条件承诺每个计划时刻都至少执行一次。应用既要能安全重复执行，也要记录哪些业务周期真正完成。幂等性解决重复效果，独立的完整性检查解决根本没有运行的工作，这两个问题不能互相替代。&lt;/p&gt;
&lt;h2 id="时间表产生的是-job"&gt;时间表产生的是 Job&lt;/h2&gt;
&lt;p&gt;五个字段依次表示分、时、日、月与星期，支持区间、步长和 &lt;code&gt;@daily&lt;/code&gt; 等宏。&lt;code&gt;*/10 * * * *&lt;/code&gt; 表示每十分钟的计划时刻，并不保证 Pod 恰好在那个瞬间开始执行。即使 Job 已经建立，节点资源不足、镜像拉取或调度过程仍可能让实际工作晚于计划。&lt;/p&gt;
&lt;p&gt;如果业务依赖当地钟表时间，应明确设置 &lt;code&gt;spec.timeZone&lt;/code&gt;。省略时使用控制器管理器的本地时区；换一台机器本身不会必然改变行为，只有相关时区配置变化才会影响解释。不要把 TZ 或 CRON_TZ 塞进 schedule 字符串，应使用专门字段。夏令时切换以及报表所覆盖的业务日期，仍需要应用自己的明确规则，不能靠时区名称代替业务定义。&lt;/p&gt;
&lt;p&gt;CronJob 名称应控制在五十二个字符以内，为生成 Job 名称时追加的后缀留空间。从 Kubernetes 1.32 起，生成的 Job 带有 &lt;code&gt;batch.kubernetes.io/cronjob-scheduled-timestamp&lt;/code&gt;，可以把原定时间与创建、启动时间比较，判断延迟发生在哪一层。这个注解是诊断依据之一，并非唯一审计来源，更不能证明备份、报表或其他业务结果已经成功完成。&lt;/p&gt;
&lt;h2 id="并发策略只约束同一个-cronjob"&gt;并发策略只约束同一个 CronJob&lt;/h2&gt;
&lt;p&gt;默认的 &lt;code&gt;Allow&lt;/code&gt; 允许 Job 重叠。&lt;code&gt;Forbid&lt;/code&gt; 在前一次仍活跃时拒绝新的启动；跳过的时间计入漏跑，之后是否还能启动仍取决于截止时间。&lt;code&gt;Replace&lt;/code&gt; 则用新的 Job 替换当前 Job。这些策略不会协调另一个 CronJob，也不会阻止集群外的工作者同时修改相同数据。&lt;/p&gt;
&lt;p&gt;如果任务每次都比调度间隔长，Replace 可能不断打断尚未完成的工作。Forbid 用漏掉启动机会换取减少重叠，并没有建立一个持久任务队列。对于重复写入有害的操作，应设计业务级去重或协调，同时考虑替换期间已经提交了一半的结果。持有一把锁本身不代表中途失败之后就能安全继续，更不代表漏掉的业务周期会自动补齐。&lt;/p&gt;
&lt;h2 id="启动期限挂起与历史保留"&gt;启动期限、挂起与历史保留&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;startingDeadlineSeconds&lt;/code&gt; 限制的是允许多晚开始，而不是任务最多运行多久。不设置时，每次计划没有单独的启动截止时间。概念文档描述超过一百次漏跑时拒绝补启动，但此次核对的 1.34.0 调度辅助函数会产生警告并返回最近的合格时刻，并不单凭这个计数拒绝启动。应核对服务端版本，不要看到警告就推断永久停摆。设置启动期限后，统计窗口受该期限限制；小于十秒的值可能错过控制器检查，不能当作精确实时调度器。&lt;/p&gt;
&lt;p&gt;挂起会停止创建新 Job，但不会终止已存在的 Job。挂起期间的计划时间也算漏跑，所以恢复时可能立即发生补调度，仍受其他规则限制。不要把这理解成每个历史时刻都必然对应一个补建 Job。若每一期报表都必须处理，应使用持久的业务工作清单记录完成情况，而不是把 CronJob 历史当作可靠队列。&lt;/p&gt;
&lt;p&gt;历史保留默认留下三个成功 Job 和一个失败 Job。它控制的是已完成对象的保留数量，不是同时运行数，也不替代执行期限。修改模板只影响将来创建的 Job；已经启动的 Job 继续使用自己的规格。调度配置、单次任务配置和结果保留策略需要分别审阅，才能知道一次变更真正影响哪批工作。&lt;/p&gt;
&lt;h2 id="坏掉的样子"&gt;坏掉的样子&lt;/h2&gt;
&lt;p&gt;一个运行二十五分钟的任务放在十分钟时间表上，使用 Allow 时可能出现重叠和重复写入。应查看真实所属关系、活跃 Pod 与完成记录，再判断业务重复是否由调度造成。启动期限应该反映结果迟到多久仍然有用，而不是随便选一个数字，只为了让控制器不再产生警告。&lt;/p&gt;
&lt;p&gt;lastScheduleTime 长时间不更新也可能是预期行为：挂起、Forbid、稀疏的日历表达式或时区改变，都可能解释这个现象。事件会过期，成功创建 Job 也不等于备份可恢复。监控应同时关注最后一次经过验证的可用结果，以及调度本身的健康，避免每天都有绿色 Job，却从未检查产物是否真的能用。&lt;/p&gt;
&lt;h2 id="快速验证"&gt;快速验证&lt;/h2&gt;
&lt;p&gt;在指定的只读集群上下文与命名空间中，检查已有的 &lt;code&gt;nightly-report&lt;/code&gt;。下列命令保留自原文，本轮没有在真实集群重新执行。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;kubectl get cronjob nightly-report -o jsonpath=&amp;#39;{.spec.schedule} {.spec.concurrencyPolicy} {.spec.startingDeadlineSeconds}{&amp;#34;\n&amp;#34;}&amp;#39;
kubectl get cronjob nightly-report -o jsonpath=&amp;#39;{.status.lastScheduleTime}{&amp;#34;\n&amp;#34;}&amp;#39;
kubectl get jobs --sort-by=.metadata.creationTimestamp -o custom-columns=NAME:.metadata.name,START:.status.startTime,SUCCEEDED:.status.succeeded
kubectl get events --field-selector involvedObject.kind=CronJob --sort-by=.lastTimestamp | tail -5
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Job 列表还会包含同一命名空间里的其他任务，判断之前必须通过 ownerReferences 核对所属 CronJob。只有 START 时间不能证明运行重叠，SUCCEEDED 为空也不能证明任务仍在执行；还要结合 active、failed 与完成条件，以及对应的原定时间。时间间隔要按实际日历和策略解释，保留的事件没有说明时，再寻找控制器日志，不能根据一条缺失记录直接认定整个控制面停过。&lt;/p&gt;
&lt;h2 id="来源"&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/"&gt;Kubernetes CronJob&lt;/a&gt;，调度语法、并发策略、漏跑一百次的规则与历史上限，已核验 2026-10-15。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://kubernetes.io/docs/reference/kubernetes-api/batch/cron-job-v1/"&gt;CronJob v1 API 参考&lt;/a&gt;，CronJob spec 的字段级定义，已核验 2026-10-15。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://raw.githubusercontent.com/kubernetes/kubernetes/v1.34.0/pkg/controller/cronjob/utils.go"&gt;Kubernetes 1.34.0 CronJob scheduling helper&lt;/a&gt;, mostRecentScheduleTime and nextScheduleTime select the latest missed time and warn above 100，已核验 2026-10-15。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>