KV 缓存预热 Cron 任务只在蓝环境运行而从未进入绿环境,原因竟是主机绑定从未迁移
事故复盘将十二天前的一次部署确定为支出增加 3.6 倍的原因,而当时在场的参与者中,没有一个人在变更发布时参与其中。部署过程非常常规:蓝绿切换,流量按计划转移到绿色环境,蓝色环境停用,流水线变绿,发布工程师关闭了工单。生产环境的 SLO 都没有触发。应用层的告警也没有响起。系统运行得完全符合设计。
原本的设计是一个每五分钟运行一次的 Cron 任务,它每五分钟针对稳定的系统提示词前缀 (system-prompt prefix) 预热提供商的 Prompt 缓存。这种预热为团队在冷启动时带来了 91% 的缓存命中率,并在每个会话的第一次请求中获得了大约 4 倍的成本优势。该 Cron 任务是一年前首次引入蓝绿模式时编写的,其主机选择器 (host selector) 被固定在蓝色池 (blue pool),以避免在重叠窗口期间运行两次预热。当绿色环境变成活跃环境而蓝色环境消失时,该 Cron 任务失去了它的主机,并从“每五分钟运行一次”悄无声息地转变为“永不运行”。随着提供商缓存的 TTL 使预热的前缀过期,缓存命中率在接下来的 36 小时内逐渐下降。成本仪表盘计算的是每日窗口内的平均单次请求成本,平滑了趋势,直到下一个计费周期让问题变得显而易见。
这是一种处于两个互不感知的系统接缝处的失效模式:拥有滚动更新权限的部署流水线,以及拥有定期作业权限的调度器。每一方都完全按照预期行事。Cron 调度器遵守了其主机选择器。部署系统轮换了资源池。结果是出现了一个无人负责的隐蔽回归,因为回归存在于两者之间的关系中,而不是存在于任何一个系统内部。
缓存预热:部署系统无法察觉的部署依赖
现在 Prompt 缓存是首屈一指的成本杠杆。对于几千个 Token 的稳定系统提示词前缀,缓存命中的成本大约只有缓存写入成本的十分之一,且延迟仅为后者的一小部分。运行生产级 LLM 工作负载的团队通常报告称,当缓存工作正常时,支出可减少 60% 至 90%,且长前缀工作负载的延迟也相应得到改善。经济因素驱动着每一个严肃的应用程序在用户会话之间维持缓存热度。
Anthropic 的 Prompt 缓存默认 TTL 为五分钟,高吞吐量工作负载可选一小时的扩展 TTL。无论哪种方式,热度都是易逝的。如果在 TTL 窗口内没有请求触及缓存前缀,该前缀就会被逐出。下一个请求将支付缓存创建的溢价——大约比基础输入费率高出 25%——以及冷读取的延迟惩罚。一个希望在低有机流量期间保持稳定缓存预热的团队,最终会以短于 TTL 的间隔对前缀运行模拟探测 (Synthetic Ping)。
那个 Ping 就是预热作业。它不是请求路径的一部分。它不是应用程序的一部分。除了成本仪表盘之外,它在操作上对一切都是不可见的。而成本仪表盘在一段窗口内取平均值,是检测缓存预热回归最慢的机制。当每日平均值波动到足以跨越告警阈值时,缓存已经冷了一天。当每周平均值波动时,账单已经膨胀了十二天。
部署系统没有原生的缓存预热概念。它的正确性模型是“新代码是否成功启动并正在提供流量”。它对 Cron 任务的模型充其量是“清单中是否定义了 Cron”。这两者都无法检查 Cron 是否真的在针对正确的目标执行。固定在一个已不存在的部署颜色的 Cron 任务满足了这两个检查,并悄无声息地什么都不做。
失效模式:基础设施绑定到瞬态标识符
这里的深层模式比 Cron 任务和 Prompt 缓存更广泛。它适用于任何基础设施固定在周围系统视为瞬态的标识符上的情况:部署颜色、主机名、Pod 模板哈希、节点标签、被轮换的服务账号。被固定的基础设施在锚点变更后以“空操作”而非“故障”的形式存续,因为调度层的契约是“寻找匹配此选择器的目标并在那里运行”。一个无法匹配任何目标的选择器在契约中并不是错误;它只是一个空结果集。
同样的模式在多个地方重复出现。固定在一个工作负载不再携带的标签上的 Prometheus 抓取作业,会悄无声息地停止收集该 工作负载的指标。固定在节点刷新期间被替换的主机名上的备份作业,会悄无声息地停止备份。固定在重命名后发生漂移的 Pod 选择器上的日志转发器,会悄无声息地丢弃日志。在每种情况下,可见的信号都是“一切正常”,而不可见的信号则是该作业所产生的任何产出都在缓慢衰减。
蓝绿固定的情况尤为隐蔽,因为“蓝”和“绿”是“活跃池”和“备用池”的概念别名,但名称却是物理存在的。固定到“蓝色”的 Cron 任务是固定在一个名称上,而不是固定在一个角色上。当发布版本切换角色时,该名称变成了坟墓,而该 Cron 任务现在正针对一个不再对应任何有意义事物的目标运行。发布工程师没有理由去思考它,因为 Cron 任务不是部署清单的一部分;而 Cron 任务的所有者也没有理由去思考它,因为部署过程从未询问过。
探测鸿沟:为什么缓存命中率作为告警信号具有滞后性
- https://platform.claude.com/docs/en/build-with-claude/prompt-caching
- https://code.claude.com/docs/en/prompt-caching
- https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/
- https://cronitor.io/guides/kubernetes-cron-jobs
- https://dev.to/krissv/kubernetes-cronjobs-silently-fail-more-than-you-think-2nb9
- https://dev.to/deadping/5-ways-your-cron-jobs-are-failing-silently-and-how-to-catch-them-2njp
- https://introl.com/blog/prompt-caching-infrastructure-llm-cost-latency-reduction-guide-2025
- https://startdebugging.net/2026/04/how-to-add-prompt-caching-to-an-anthropic-sdk-app-and-measure-the-hit-rate/
- https://apxml.com/courses/langchain-production-llm/chapter-7-deployment-strategies-production/blue-green-canary-deployments
- https://www.truefoundry.com/blog/provider-agnostic-prompt-caching-llm-gateway
