LabHub
学习 学习路径 课程

CKAD — Kubernetes 应用开发者

宽限期也是时间预算 — preStop与TERM之间

在 LabHub 中继续学习

一句话总结

preStop 不是终止宽限期之外附送的时间。必须把待终止 Pod 的 hook、应用的信号处理和剩余工作时间放在一起设计。

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

为什么需要它

设置 readiness,并在 preStop 中稍等片刻后,很容易以为一定安全。但如果 hook 结束后进程立刻退出,已经接收的请求仍可能被截断;即使进程会等待请求完成,如果终止宽限期先耗尽,也无法完成。“加入了配置”与“得到了期望结果”之间的空白,正是本实验要填补的部分。

配置究竟进入哪个 Pod 同样重要。新加入 Deployment template 的 preStop,是使用该 template 新建 Pod 的配置。若以为它会追溯应用到当前正被替换并终止的旧 Pod,实验比较就会出错。删除前必须读取并保留目标 Pod 的 UID,以及 spec.containers 中的 lifecycle。只捕获新 template 的报告,无法说明实际执行了什么。

它如何运作

在通常的 TERM 终止流程中,kubelet 先执行 preStop hook,hook 结束后再向容器进程发送终止信号。终止宽限期在执行 hook 之前就开始计时,所以 hook 与后续清理必须共用同一预算。hook 用掉 3 秒,并不表示应用额外得到 3 秒。盲目加入很长的 sleep,虽然能给新请求摘除留出时间,却也会消耗终止预算。

这里还容易错误地累加时间。已经开始的 HTTP 工作在 hook 执行期间仍可能继续。因此,完整工作耗时 10 秒、hook 耗时 3 秒,并不总是意味着还需要 13 秒。设计时要计算 hook 执行时间和 TERM 后实际剩余处理、清理时间的上限;观测时则分别记录接收、删除、hook、TERM、完成的时刻。如果工作没有上限,只增加宽限期也无法保证安全终止。

本服务器的 graceful 模式在收到 TERM 后会拒绝新的 work 请求,等待 active 归零后退出。immediate 模式用于对比,会以退出码 17 立即结束。代码 17 本身并不是 Kubernetes 的标准终止含义,而是实验服务器故意选择的观测标记。正常完成时会把响应正文发送到底,并观测到退出码 0。

即使采用 graceful,如果宽限期不足,容器仍可能被强制终止。此前实际 VM 探测中,宽限期不足时观测到了 137。但在现场不要只看到 137 就断定是终止宽限期导致,还必须调查 OOM 等其他原因。关键是把 Pod watch 的终止原因、实际宽限设置、信号前后时刻和请求结果连接起来。

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

执行 kubectl delete 的墙上时钟时刻,与 kubelet 开始处理终止的时刻并不完全相同。API 处理、观测传播和 runtime 操作也有延迟。因此,“宽限期 5 秒,所以删除命令后必须恰好 5 秒断开连接”这种评分,即使在正常环境也可能错误。本实验不要求匹配零点几秒,而是检查同一个已接收请求的结果和终止状态。

在评分时重新查询 Pod 也不够。已经删除的 Pod 不存在,终止容器也可能已被 runtime 回收。探针的首个实现试图在终止后检查,结果失去了证据。因此要在发送请求前启动 Pod watch,并保留最终容器状态和 DELETED 事件。读取观测文件进行重新评分时不会重建 Pod;如果偷偷再执行一次相同实验,检查的就不是先前结果。

后续实验要做什么

在 graceful termination 配置中缩短宽限期,让请求中断,再补足宽限期,确认相同长度的工作能否完成。比较阅读理论后的预期与实际观测,但不要把某一次响应扩展为所有生产流量的无中断保证。为请求失败添加自动重试时,还要像前一单元的 Job 幂等性一样考虑业务副作用。

官方文档:容器生命周期 hook, Pod 终止流程.