CronJob 按计划创建 Job,再由 Job 管理真正执行工作的 Pod。完成后的对象可以保留有用状态,但保留量受到多种独立机制影响。尤其要注意,CronJob 本来就有默认历史上限;省略显式历史字段,并不意味着普通已完成运行会无限堆积。分析清理问题时,先分清对象类型与状态,再判断是哪项机制没有覆盖。

CronJob 创建 Job,Job 管理 Pod。已结束历史数量与 TTL 年龄是独立清理控制。归属影响级联删除;活动任务与外部日志保留需要独立策略。
当另一机制允许更早删除时,数量与 TTL 都不保证最低保留时长。清理是异步的,活动任务限制也不证明业务可安全重试。本轮未重跑保留诊断。

历史记录不等于日志归档

CronJob 默认最多保留三个成功 Job 和一个失败 Job,控制器分别协调两类记录。某类上限设为零,表示不保留该类历史,但清理是异步过程,不是任务结束那个瞬间必然消失。仍在运行的 Job 不属于完成历史,需要单独观察,不能期待这两个数量上限替它们设置运行期限。

三十天每小时一次意味着七百二十个计划机会,不一定保留七百二十个 Job,更不一定恰好产生相同数量的 Pod。错过调度、并发策略、重试与并行度都会改变关系,一个 Job 也可能拥有多个 Pod。用运行次数估算存储或控制面容量之前,应把这些假设说清楚,不能把理想时间表直接当成当前对象数量。

Pod 对象仍在,也不保证容器日志仍然可读。节点故障、日志轮转、容器清理和驱逐都会影响访问。真正需要按时长保留的证据,应交付到合适的日志系统。Kubernetes 对象便于调查状态,但不能替代归档保证;只延长 Job 历史,不会自动解决节点日志的生命周期问题。

大量历史仍有对象存储、列表大小和排查噪声等成本。Watch 接收相关变化,不会因为一个对象仍存在,就持续重新发送它所有未变化的状态。应测量真实对象数和控制面行为,避免把保留状态描述为永不停歇的控制器活动。调查困难可能值得优化,但应使用正确机制解释成本。

数量与年龄是独立策略

CronJob 历史限制的是已结束 Job 的数量,不是至少保留多久。密集运行可能很快替换最后一条失败记录,稀疏运行则可能保留很长时间。应根据查看间隔和失败频率选择数量,不能认为保留一次失败就足够覆盖所有工作负载,也不能把数量设置说成按时间执行的保留窗口。

TTL-after-finished 写在 Job 上。若希望应用到今后由 CronJob 创建的任务,应将 ttlSecondsAfterFinished 放在 Job 模板的 spec 中。计时从 Job 达到 Complete 或 Failed 开始,过期只代表具备级联删除资格。零表示完成后立即具备资格,省略则没有这项按年龄清理的规则,不能混淆省略与零的含义。

历史控制器与 TTL 都可能删除同一个 Job。因此,历史数量很大或 TTL 很长,都不保证最低保留时长,只要另一机制允许更早删除即可。Finalizer 和控制器进度还可能推迟实际消失。修改 CronJob 模板影响未来 Job,不会替所有旧 Job 自动重写现存 TTL;验证时应分别读取模板与既有对象。

所属关系决定级联行为

CronJob 创建的 Job 引用其所有者,Job 与 Pod 之间也形成从属关系。普通级联删除可以沿这条链清理。显式 orphan 删除则保留从属对象,并移除相关所有者链接,不是故意留下普通悬空引用再等待垃圾收集器追赶。两种删除方式具有不同结果,操作前应核对传播策略。

Job 被孤立后,原 CronJob 不再裁剪它们的历史,但已经配置的 TTL 仍可作用于各个完成任务。直接创建的 Job 同样需要明确保留决策。不同客户端的删除选项不能凭记忆统一推断,也不能认为孤儿马上就会消失。确认谁负责清理,比仅仅看到一个空 owner 字段更重要。

坏掉的样子

始终没有进入终止条件的 Job,不会因完成后 TTL 获得清理资格,历史上限也不会限制活动任务。合适的 activeDeadlineSeconds 可以约束运行时间,concurrencyPolicy 则控制计划任务重叠。它们解决不同问题,而且都不能证明底层业务重复执行安全;重试、副作用和幂等性仍然需要应用负责。

历史上限为零可能在调查前就移除失败记录。缩短保留前,应把必要日志与结果文件保存在其他地方。反过来,所有者关系不存在或 TTL 缺失,只是需要调查的信号,不能直接授权删除;其他控制器或业务保留要求可能有意承担清理责任,不能把所有未自动消失的记录都当成泄漏。

快速验证

以下保留的只读诊断本轮没有重新执行。应明确选择临时测试上下文和命名空间,并对照存储的 CronJob、它实际拥有的 Job、完成条件与创建时间。命名空间总数包含无关工作负载和活动 Job,因此不能拿它直接与单个 CronJob 的两项历史上限比较。

kubectl get cronjob hourly-rollup -o jsonpath='{.spec.successfulJobsHistoryLimit} {.spec.failedJobsHistoryLimit} {.spec.jobTemplate.spec.ttlSecondsAfterFinished}{"\n"}'
kubectl get jobs --sort-by=.metadata.creationTimestamp --no-headers | wc -l
kubectl get pods --field-selector=status.phase=Succeeded --no-headers | wc -l
kubectl get jobs -o jsonpath='{range .items[*]}{.metadata.name} ttl={.spec.ttlSecondsAfterFinished} owner={.metadata.ownerReferences[0].name}{"\n"}{end}'

即使源清单省略历史字段,第一行通常也会显示 API 默认值。空输出不证明无限保留,应检查查询错误和完整对象。最后一行只显示第一个所有者引用。TTL 与 owner 同时为空,只说明尚未证明这两项机制负责清理,不表示永远不可能删除。跨多个完成周期复查,期望所属完成历史在协调后受限,同时单独评估活动任务与无关对象。

来源