Karpenter 的整合行为有违直觉
摘要
一篇技术博客文章,探讨为什么 Karpenter 的节点整合看似有违直觉——集群容量保持不变,而节点不断更换——并解释其底层机制。
<p><a href="https://lobste.rs/s/6gfucn/karpenter_s_consolidation_behaviour_is">评论</a></p>
查看缓存全文
缓存时间: 2026/08/04 17:48
# Karpenter 的节点整合行为违反直觉
Source: https://blog.appliedcomputing.io/p/karpenters-consolidation-behaviour-5a6
OK,这个故事始于同事在一个周二早上 9 点给我发消息:“嘿,drmorr,Karpenter 在这个集群里表现得有点奇怪,你能快速看一下吗?”
“当然可以,”我在早晨的咖啡因下肚之前就答应了。我心想,*这可能大概要花 15 分钟,然后我就能继续我的一天了*[1](https://blog.appliedcomputing.io/p/karpenters-consolidation-behaviour-5a6#footnote-1)。五个小时后,我终于明白了到底发生了什么事。又过了两个小时,我才理解得足够透彻,可以向同事解释清楚[2](https://blog.appliedcomputing.io/p/karpenters-consolidation-behaviour-5a6#footnote-2)。大约一周后,我理解得足够深入,于是写了这篇博文。
剧透警告:Karpenter 并没有 bug,整个过程每一步都逻辑衔接,但(至少对我来说)结果仍然令人意外,以至于我觉得值得写一篇文章。
先说明一下,我已经抹掉了这篇文章里的所有序列号,以保护…… 我也不知道,大概保护不了任何人,我觉得这里没有什么特别敏感的内容,但无论如何。问题始于一张看起来像这样的图:

图 1:集群中未使用 CPU 资源随时间变化的堆叠时间序列图(假设数据);所有数据均已虚构,以保护无辜者。
这张图展示的是集群中*未使用*的 CPU 数量,按集群中的节点聚合。换句话说,每条线代表一个不同的节点,任何时间点上的值就是该节点上可用(可调度)的 CPU 数量。这是一个堆叠图,这样我们既能了解未使用 CPU 的分布情况,也能了解集群中未使用 CPU 的总量。
这张图其实很难读!但是,如果你眯着眼睛看足够久,会看出两件事:第一,集群中的“可用 CPU 容量”相当稳定:我们或多或少总是有 5 个可用的 CPU。第二,尽管如此,集群中的节点却在*不停*地更替。这种模式在这张图中持续了超过 12 个小时。什么???为什么 Karpenter 会这样做?这甚至是 Karpenter 的错吗???
然而,在检查 Karpenter 的日志后,我确认 Karpenter*确实*因为节点整合而终止了这些节点。但我仍然非常困惑:Karpenter 本应通过节点整合来更好地装箱集群——换句话说,经过一次节点整合事件后,集群中的“可用容量”应该会减少!但事实并非如此:在一次节点整合事件之后,可用容量保持不变。
要理解这里发生了什么,我们需要知道两件事:这个集群上运行着什么,以及 Karpenter 的节点整合是如何工作的。我们先来谈谈 Karpenter 节点整合,因为我听到过很多关于它如何工作的*不精确的传言*[3](https://blog.appliedcomputing.io/p/karpenters-consolidation-behaviour-5a6#footnote-3)。
Karpenter 的标准推销话术[4](https://blog.appliedcomputing.io/p/karpenters-consolidation-behaviour-5a6#footnote-4)是这样的:“安装这个神奇的自动扩缩器,你所有的问题都会消失!”
抱歉,这有点讽刺。再试一次。Karpenter 的标准推销话术是这样的:“安装这个神奇的自动扩缩器,它会让你的集群便宜得多,多得多!” 这仍然有点讽刺[5](https://blog.appliedcomputing.io/p/karpenters-consolidation-behaviour-5a6#footnote-5),但更接近事实了。问题是,“Karpenter 如何让事情变得更便宜?”答案是节点整合。
在高层次上,Karpenter 有两个控制循环:一个是“扩容”循环,另一个是“整合”循环(早在 SimKube 1.0 时代,我就写过关于[分析这两个控制循环性能](https://blog.appliedcomputing.io/p/using-simkube-10-comparing-kubernetes)的文章)。扩容循环设计得很快:它会快速响应任何 Pending 的 Pod,并启动新节点以尽快调度这些 Pod。整合循环较慢;它不断寻找 Pod 和节点的“最优”配置,这里的“最优”指的是“最便宜”。实际上,这意味着它不断地试图通过将 Pod 从“轻度使用”的节点上移走来缩小集群(即装箱),以便可以终止这些节点[6](https://blog.appliedcomputing.io/p/karpenters-consolidation-behaviour-5a6#footnote-6)。理论上,这会降低成本,因为你不必为同样多的计算资源向 AWS 付费。
让我们更详细地研究一下整合行为;如果你阅读了 Karpenter 关于这个主题的[文档](https://karpenter.sh/docs/concepts/disruption/#consolidation),你可能会惊讶地发现这里几乎没有什么旋钮可以调节!你只能设置两个参数(按节点池设置):`consolidationPolicy` 和 `consolidateAfter`。第一个参数控制 Karpenter 如何执行整合,你可以将其设置为 `WhenEmpty` 或 `WhenEmptyOrUnderutilized`[7](https://blog.appliedcomputing.io/p/karpenters-consolidation-behaviour-5a6#footnote-7);第二个参数控制一个节点必须闲置多久,整合机制才会启动。
一般来说,如果你在运行 Karpenter,你“可能”希望使用 `WhenEmptyOrUnderutilized`;否则,你只是在等待 Pod 自然地从集群中循环出去,然后才会有某种装箱开始生效,这通常会导致大量几乎为空的节点闲置在你的集群中。所以*实际上*唯一有意义的参数就是 `consolidateAfter`。这个旋钮有助于减少更替:设置得低,Karpenter 会极其迅速地踢出集群中的节点[8](https://blog.appliedcomputing.io/p/karpenters-consolidation-behaviour-5a6#footnote-8);但如果你有一个本来可以使用该节点的新工作负载进入,这可能是不可取的。然后你必须启动一个新节点,而节点供给可能需要比在集群中保留一个“温暖”节点更长时间(并且成本更高)。如果你预期这类情况会定期发生,你可以将 `consolidateAfter` 设置为更高的值,告诉 Karpenter 把节点多保留一段时间[9](https://blog.appliedcomputing.io/p/karpenters-consolidation-behaviour-5a6#footnote-9)。
到目前为止,我已经把这页 Karpenter 文档读了几十遍,每次读的时候我都会想,“奇怪,竟然没有办法控制 Karpenter 整合行为的利用率阈值!”换句话说,我们总是说 Karpenter 整合“未充分利用”的节点,这意味着存在某个阈值,Karpenter 在这个阈值之上认为节点“并非未充分利用”,而我总是有点惊讶这个阈值不可调。但事实证明,这个阈值不可调是有充分理由的:*不存在这样的阈值*。
具体来说:Karpenter 会考虑整合一个节点,*无论*它有多满。这并不重要,不管节点上运行着一个 Pod 还是一百个 Pod,只要我们不在节点的 `consolidateAfter` 窗口内,该节点就有资格被整合。从 Karpenter 的角度来看,唯一[10](https://blog.appliedcomputing.io/p/karpenters-consolidation-behaviour-5a6#footnote-10)重要的是,“所有这些 Pod 能放到其他任何地方吗?”如果答案是肯定的,那就拜拜了,你该走了!
这里的含义是,Karpenter 的整合行为与所讨论节点的状态完全无关,而*完全*取决于其周围集群的状态。一个满载集群中有一百个 Pod 的节点?不会被整合。同样的百 Pod 节点,如果周围有很多其他空节点的集群中?*可能*会被整合。或者用更直白的话说:Karpenter 正在使用*全局状态*(整个集群看起来什么样?)来做*本地*决策(我该怎么处理这个节点?)。通常来说,这与我们在分布式系统中想要的正好相反。
现在,我们(或者至少是我)对 Karpenter 的整合行为有了更好的理解,让我们回头看看这个集群上运行的工作负载,特别是这篇文章开头分享的图表。这张图有什么奇怪的地方吗?我直到看了它的反向图才发现。与其看集群中剩余可用容量,不如看看集群中已承诺(已调度)的 CPU 容量:

图 2:集群中请求的 CPU 资源随时间变化的堆叠时间序列图(假设数据);这基本上是图 1 的逆图。
这张图*仍然*有点难读,但稍微简单一点。我们可以看到,集群中*已使用*的容量现在大致保持不变,即使节点在不断更替,但是——嗯,看那些尖峰[11](https://blog.appliedcomputing.io/p/karpenters-consolidation-behaviour-5a
相似文章
@che_shr_cat: 1/ 多年来我们一直通过头部共享(GQA/MQA)来优化KV缓存,但我们忽略了一个基本假设:为什么……
这条推文挑战了关于Transformer需要独立的Q、K和V投影的基本假设,提出合并它们可以为KV缓存带来巨大的内存节省。
@songhan_mit:探索我们在KV缓存压缩方面的持续努力:
来自Song Han的一条推文强调了在KV缓存压缩方面的持续工作,其中介绍了Weian Mao的一篇博客,讨论了论文中常常被忽视的系统级方面。
我是如何解决持续运行的Anthropic智能体循环中上下文窗口膨胀问题的(Opus + Sonnet架构)
一位开发者分享了一种架构模式,用于管理持续运行的Anthropic智能体循环中的上下文窗口膨胀问题,采用KV缓存、动态工具模式加载,以及通过Claude 3.5 Sonnet和Claude 3 Opus解耦执行器与顾问角色。
@SemiAnalysis_: 与对DeepSeek R1的恐慌类似,一些不明真相的人认为Kimi K3使用线性注意力(KDA)对……不利
SemiAnalysis认为,与无知的恐慌相反,Kimi K3的线性注意力(KDA)对NVIDIA、HBM、DRAM和网络并非不利,并解释了为什么降低KV-cache需求实际上是有益的。
@yukangchen_: 我们很高兴分享一篇新的技术文章《KV缓存压缩及其基础设施问题》。https://research.nvidia.…
NVIDIA Research发布了一篇技术博客,探讨KV缓存压缩技术及其基础设施问题,包括FlashAttention和paged attention如何为长上下文LLM的生产部署带来实际障碍,并提出了一个使用RoPE的几何解决方案。