使长作业可恢复,并将驱逐视为预期事件。 这是唯一能真正与成本驱动的自动扩缩容完美结合的选项,但它要求工作内容是可以设置检查点(checkpointable)的。对于文档分析、批量摘要或任何工作单元可以从队列重新驱动的流水线,这是正确的答案:Worker 每隔 N 个 token 将中间状态写入持久存储,队列通过可见性超时将该工作标记为进行中,而替代 Pod 在抢占后从检查点恢复。批量推理平台多年来一直针对竞价实例(spot instances)这样做——工作队列追踪已完成和待处理的项目,中断只需将未完成的工作返回队列即可。
在这类事件发生后,一种常见的反射性反应是“我们要加个 PDB”。PDB 告知 eviction API 和节点排空(node drain)控制器,在它们发起主动干扰(voluntary disruptions)时要遵循最小可用性。对于滚动升级、节点维护和集群自动扩缩容(cluster autoscaler)回收,它是一个真实且有用的护栏。
但 PDB 无法保护你免受 HPA 或 KEDA 扩缩容决策的影响。在 PDB 的定义中,缩容(Scale-down)并不是一种逐出(eviction)——它是 Deployment 控制器确认副本集需要更少的副本,随后 kubelet 相应地终止 Pod。PodDisruptionBudget 协议对此保持沉默。KEDA 仓库中有一个长期讨论(#2899)正是关于这种意外:缩容决策会在繁忙的 Pod 仍在工作中途时将其终止,而 PDB 无法阻止这一点。
这里已经有所进展。KEDA 一直在开发动态调度和感知工作者的缩容(worker-aware scale-down)——其核心理念是缩容程序应知道哪些 Pod 正在处理工作,并优先终止空闲的 Pod。截至 2026 年,各缩容程序对此的支持仍参差不齐,且取决于工作负载是否暴露了真实的“我正忙碌”信号。在这一信号普及之前,你不能指望自动扩缩容程序会挑选正确的 Pod 来销毁;你必须让每个 Pod 都能安全被销毁,或者确保销毁行为根本不会发生。
更深层的教训是,“可能终止 Pod 的因素”其范围远大于遵循 PDB 的子集。竞价实例(Spot)回收、节点池合并、OOM 杀掉进程、引擎内部长时间 GC 停顿期间的存活探针(liveness-probe)失败,以及你自己的自动扩缩容程序,所有这些最终都指向同一个结果——工作消失了——而其中只有一部分是通过你控制的机制路由的。正确的心理模型应该是“任何 Pod 随时都可能挂掉”,就像对待“牛群(cattle)”基础设施一样,只是这些牛需要 28 分钟才能吃完它们的饭。
在事后分析中,最令人沮丧的细节不是 Pod 被回收了。而是这次失败几乎是不可见的。客户的 SDK 收到一个已关闭的连接,但没有显示有用的错误。应用程序日志中没有异常,因为应用程序进程是被干净地杀掉的。唯一持久的记录是网关访问日志中的一条 499 记录,而且没有人为此报警。
499 是一个非标准代码(HAProxy 和其他一些代理对相同情况使用不同的代码,Apigee 和其他 API 网关也有自己的变体),但在任何使用 NGINX、Envoy 或类似反向代理作为流式前端的推理架构中,它是流式响应在完成前被中断的关键信号。它综合了多种原因——真实的客户端断开连接、网络重置、中间代理超时以及 Pod 终止——但其中每一项都是流式推理运维人员应该关心的。