LabHub
学习 学习路径 课程

CKAD — Kubernetes 应用开发者

批处理成功了,包裹却发了两次

在 LabHub 中继续学习

一句话总结

Job 显示 Complete,只表示满足了指定的执行完成条件,并不能证明外部系统中的业务恰好处理了一次。

概念图: 一句话总结 · 为什么需要它 · 它如何运作 · 在实际工作中会遇到的情况

为什么需要它

有一个接收订单并登记配送的批处理。开发者把 completions 和 parallelism 都设为 1,restartPolicy 选择 Never。执行画面显示一个成功 Pod 和 Complete,账本中却有同一订单的两笔配送。三个地方都写了数字 1,为什么仍会如此?

程序最后一行与外部系统的保存时刻不是同一个事件。第一个 Pod 向配送 API 发送请求,服务器保存新配送;程序收到响应后,在留下退出码 0 之前因错误结束。Job 控制器看到失败执行后创建新 Pod。新 Pod 再次处理同一订单,这次正常退出。对控制器而言工作已经完成,外部账本却可能留下两次保存。

本实验的配送是向 SQLite 添加行的合成服务,不使用真实地址、个人信息或配送商 API。使用小而可控的副作用,才能专注练习阅读失败原因。

它如何运作

首先区分执行主体。Job 是管理所需完成数量的控制器对象;Pod 是承载工作程序的执行单元;容器内进程结束会留下退出码;外部业务系统会在自身存储中留下请求结果。把任一层成功误读为另一层成功,就会错过原因。

parallelism 与期望同时运行的 Pod 数相关,completions 与所需成功次数相关,两者都不统计外部 API 的事务次数。restartPolicy: Never 只表示 kubelet 不在同一 Pod 中重启失败容器,并不是禁止 Job 控制器创建替代 Pod 的开关。

观测时不要只看名称,还要确认 UID 和所有权。若两个 Pod 的 ownerReferences 指向同一 Job UID,各自 restartCount 均为 0,其中一个 Failed、另一个 Succeeded,就有依据区分 Pod 内重启与 Job 重试。还必须保留失败 Pod 的退出码与日志,以判断失败发生在保存前还是保存后。

kubectl get jobs,pods -n ckad-job-effects
kubectl get pod -n ckad-job-effects <파드이름> -o json
kubectl logs -n ckad-job-effects <파드이름>

但不能只凭日志中的一句成功消息统计配送。应分别从服务器查询请求尝试列表与配送行列表。请求尝试会记录哪个 Pod 发送了哪个业务键,配送行则记录真实副作用。必须能够比较两次请求是否产生两条配送,或是否返回相同配送编号。

在实际工作中会遇到的情况

重试不是用来隐藏错误的功能。它能承受短暂失败,却不会消除判断操作能否安全重跑的责任。发送邮件、登记文件转换结果、扣减库存、向外部系统提交订单,也会遇到相同边界问题,尤其要注意难以确认对方是否已经保存的区间。

把 backoffLimit 改为 0 可以减少重试,却不会撤销已经记录的配送。第一个 Pod 若保存后失败,Job 会失败,但一笔配送仍可能存在。运维人员看到失败 Job 后手动创建新 Job,也仍可能重复副作用。因此,在看到失败状态后无条件重跑之前,要先按业务键查询账本。

activeDeadlineSeconds 同样只是允许执行的时间,不是外部保存的有效期。程序保存后等待,随后因时限而终止,已创建的行也不会自动消失。取消执行、取消业务和补偿处理是不同设计。本实验只观测这种差异,不执行任意补偿删除。

观测时机也很重要。超过期限后控制器若删除工作 Pod,之后只运行 get pods 可能找不到该次执行。应把实验期间收集的 Pod UID、所属 Job、容器执行信息与账本请求关联,并另行确认当前 Job 的失败原因。不要把删除前的 Running 观测改写成退出码,也不要因为看不到 Pod 就判断没有请求。重新评分需要区分采集时点的保留材料。

下一份教材要做什么

后续教材不会阻止再次执行,而会设计让相同业务安全重复的契约。先区分执行标识符与业务标识符。之后实验将保留正常 Job 作为对照,运行保存后立即失败的 Job,同时记录 Job condition、真实 Pod UID、退出码、请求次数与配送行数。目标是分别解释 Complete 却有两条记录,以及 Failed 却有一条记录的情况。

官方文档与适用范围

本教材的账本结构与故障注入是独立学习示例。不假定所有外部系统都采用相同保存模型,也不能代替考试题目或 CKAD 全部范围。