☰
Kubernetes自动扩展器实践:HPA、VPA与CA协同指南
2026/10/21 19:55:42 网站建设 项目流程

1. 三种自动扩展器,到底分别解决什么问题

1.1 从一次真实故障说起:为什么只靠固定资源远远不够

先讲一个我自己踩过的坑。之前某公司在跑一个促销活动,流量是平时的四五倍,我们当时用的还是最原始的方式:给每个服务把 request 和 limit 压到很大的值,副本数固定十几个,想着"多配点资源肯定扛得住"。结果活动一到,某个中间件服务的 Pod 被频繁 OOMKilled,另外一个无状态服务 CPU 飙升到接近 100%,整个链路开始超时。原因很简单:流量分布不是均匀的,你不可能给所有服务都预留峰值资源,那是把成本放到天上去。

后来我们把 Kubernetes 的三种自动扩展器都上了——HPA(Horizontal Pod Autoscaler)、VPA(Vertical Pod Autoscaler)、CA(Cluster Autoscaler),才把这事彻底理顺。简单来说,这三兄弟管的是不同的维度:HPA 管"Pod 数量多了还是少了",VPA 管"单个 Pod 的资源定大了还是定小了",CA 管"整个集群的节点够不够"。三者配合,才能既保住稳定性,又把资源浪费压到最低。

这篇文章我尽量把三者的原理、配置、踩坑经验一次讲清楚,适合正在做 Kubernetes 生产环境优化的平台工程师、SRE,以及被资源问题折腾过的后端开发同学。没有太多晦涩的数学公式,但我会把 HPA 的扩缩容算法、VPA 的推荐原理、CA 的扩容判定这些核心逻辑都拆开说明白。

1.2 HPA 的定位:横向加副本,应对流量波动

HPA 是大多数人最先接触的自动扩展器,它解决的是"并发量波动"的问题。当你的服务是水平可扩展的——换句话说,可以靠增加副本数来分摊压力——HPA 就会持续观察指标(比如 CPU 使用率、QPS、请求延迟),当指标高于目标值时增加副本,低于目标值时减少副本。

这里有一个关键前提:HPA 只对无状态或者能优雅处理缩容的服务有意义。如果某个服务是单体应用、内部有会话状态、或者依赖本地缓存且不能快速重建,那 HPA 横向扩出来的副本不一定能真正帮上忙,甚至会在缩容时把用户连接掐断。所以真正生产环境里,HPA 通常配合 Service、Ingress 和你自己的会话保持策略一起使用。

HPA 的配置本身并不复杂,一个最小的配置长这样:

apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: web-server-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: web-server minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60

但我建议你先别急着抄这个配置,因为真正决定 HPA 好不好的,是后面的算法细节和 behavior 参数,这部分我在第 2 章详细讲。

1.3 VPA 的定位:纵向调规格,治理资源碎片

VPA 解决的问题和 HPA 完全不同。很多服务的负载特征是"单个请求就吃不少 CPU/内存,但总并发量不高",这时候你加副本没用,因为每个副本都在做重活,倒不如把单个 Pod 的规格调大。VPA 干的就是这件事:它自动调整 Pod 的 request 和 limit,甚至可以直接帮你更新 Deployment 里的资源定义。

我见过很多团队浪费资源的方式就是:开发同学不知道自己的服务实际需要多少资源,于是随便写个 request"大一点比较稳",limit 干脆不设。结果集群里一堆 Pod 占着远超实际使用的资源额度,节点被这些"虚胖"的 Pod 占满,真正需要资源的新 Pod 反而调度不上去。VPA 的价值就是把这些"拍脑袋定资源"的问题纠正过来。

当然,VPA 有一个必须注意的限制:在大多数场景下,VPA 不能和 HPA 同时基于 CPU/内存指标工作,否则两个自动扩展器会互相打架——一个想加副本,一个想调规格,最终谁都不听谁的。这个兼容性问题我在第 3 章会专门展开。

1.4 CA 的定位:横向加节点,兜住集群容量

CA 是最后一个兜底的角色,它管的是"集群里有没有足够的节点来容纳 Pod"。HPA 和 VPA 再怎么调,也都是在一个固定的节点池里做文章;如果节点本身不够,新扩出来的 Pod 永远处于 Pending 状态,那前面两个自动扩展器等于白干活。CA 做的事情就是监控那些因为节点资源不足而 Pending 的 Pod,发现一个就触发扩容,往集群里加节点;反过来,当节点利用率长期偏低,它就先把 Pod 调度走,再把这个节点缩掉。

用一句话总结三者的协作:HPA 决定"我要几个 Pod",VPA 决定"每个 Pod 要多大",CA 决定"我要多少台节点来装下这些 Pod"。三者缺一不可,但很多人会犯的错误是把它们割裂开来单独研究,结果 HPA 配置得很漂亮,集群节点池却根本没有余量。

1.5 三者协作逻辑与误区

先说最常见的误区。

第一个误区是"有 HPA 就不需要预留资源了"。实际上 HPA 扩容是有一个过程的:指标采集、周期计算、副本更新、Pod 启动、就绪检查,整个链条走完往往需要几十秒甚至几分钟。如果你的集群没有任何缓冲容量,流量尖峰来临时扩容还没完成,服务已经被打垮了。正确的做法是 Pod 的 request 要设得合理,节点池要保留一定的扩容余量,给 HPA 留出反应时间。

第二个误区是"VPA 可以完全代替人工调参"。VPA 确实能给出推荐值,但它的工作模式是"建议、自动、初始化"三种,其中自动模式会重启 Pod 来生效,这个重启动作对长连接和在线业务是有影响的,不是所有团队都能接受随时发生的重启。更稳妥的方案是先用 VPA 的 Recommender 模式跑一段时间,等推荐值稳定了再手动应用。

第三个误区是"CA 扩容越快越好"。节点创建需要时间,在云环境里从触发扩容到节点 Ready,通常要两三分钟甚至更久。CA 为了防止频繁抖动,天然会做延迟判断,而很多人一看到节点迟迟不扩容就手动去加,最后反而把集群状态搞乱了。正确的心态是:CA 是用来吸收中长期容量缺口的,短时间尖峰应该靠 HPA 和预留缓冲来顶。

2. HPA 落地实操:从基础指标到自定义指标

2.1 HPA 扩缩容算法拆解

HPA 的控制器每隔默认 15 秒(由--horizontal-pod-autoscaler-sync-period控制)会计算一次期望副本数,计算公式大致是这样:

期望副本数 = ceil(当前副本数 × 当前指标值 / 目标指标值)

举个例子:某个 Deployment 当前有 4 个副本,HPA 目标是 CPU 平均利用率 60%,而现在实际平均利用率为 90%,那么期望副本数就是ceil(4 × 90 / 60) = 6。注意这里的"当前指标值"是取所有副本的平均值,而不是单看某一个 Pod。

这里有几个细节很容易踩坑。第一,如果没有采集到指标,HPA 默认不执行缩容动作;如果采集到的指标值为 0,HPA 会把期望副本数放大,使其可以快速扩容。第二,单次计算的结果并不会立刻应用,HPA 有--horizontal-pod-autoscaler-downscale-stabilization-window(默认 5 分钟)的缩容稳定窗口,防止因为指标抖动导致副本数反复横跳。扩容则相对灵敏,这是有意的设计——宁可短期多几个 Pod,也不要因为等待而损失流量。

还有一个必须理解的概念:HPA 计算的是"副本数"这个整数,所以它天然有步进效应。比如你从 5 个副本扩到 8 个,指标降下来后它又会从 8 降到 6、5,这种调整不会是平滑的。如果你的服务对副本数变化很敏感,后面我会讲怎么用behavior参数控制扩缩容的步长和频率。

2.2 一个完整的 HPA 配置示例

我直接给一套我生产环境里常用的配置,再逐步解释每个字段为什么这样写:

apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: payment-api-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: payment-api minReplicas: 3 maxReplicas: 15 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 75 behavior: scaleUp: stabilizationWindowSeconds: 0 policies: - type: Pods value: 2 periodSeconds: 60 - type: Percent value: 100 periodSeconds: 60 selectPolicy: Max scaleDown: stabilizationWindowSeconds: 300 policies: - type: Percent value: 20 periodSeconds: 120

逐项说明一下。

minReplicas我建议不要低于 2,除非你的服务真的不要求任何可用性。很多同学图省事写 1,结果节点一维护,Pod 被驱逐后服务直接不可用。

metrics我同时配了 CPU 和内存两个指标,HPA 在多个指标下会取"期望副本数的最大值"来执行,这样能保证任何一个指标过热都会驱动扩容。

behavior.scaleUp.stabilizationWindowSeconds我设为 0,意思是扩容不做稳定窗口延迟。因为扩容是为了保命,越快越好。scaleUp.policies里两条策略分别限制"每 60 秒最多扩容 2 个 Pod"和"每 60 秒最多扩容 100%(即翻倍)",selectPolicy: Max表示取两个策略中较大的那个作为允许值。这样既能在流量暴涨时快速翻倍,又不会一次扩太多把数据库或其他下游击垮。

behavior.scaleDown.stabilizationWindowSeconds设为 300,这是缩容的冷静期。缩容动作必须谨慎,因为缩多了,下一个流量尖峰可能直接打穿服务。缩容策略限制每 120 秒最多缩容 20% 的副本数,避免一次性缩掉太多。

注意:autoscaling/v2才是带有behavior字段的 API 版本。如果你用的集群比较老,还在用autoscaling/v1,那只能配 CPU 指标,也没有 behavior 控制,生产环境建议优先升级集群版本。

2.3 多指标与自定义指标扩展

默认的 CPU、内存指标对大部分 Web 服务够用,但有些场景它们并不灵敏。比如一个事件处理服务,它的瓶颈可能不在 CPU,而在消息积压量;一个搜索服务,它的压力体现在 P99 延迟。这时候就需要上自定义指标。

Kubernetes 自定义指标的扩展路径主要有三条:

  • 外部指标(External Metrics):从外部系统读取指标,比如云厂商的监控服务、消息队列的积压量。
  • 自定义指标(Custom Metrics):通过 Metrics Adapter 暴露应用自身的指标,比如通过 Prometheus Adapter 把 Prometheus 里的http_requests_total这类指标暴露给 K8s API。
  • 容器资源指标(Container Resource):针对特定容器的资源使用量做扩缩,适合多容器 Pod 的场景。

我拿 Prometheus 场景举个例子。假设你已经部署了 Prometheus 和prometheus-adapter,现在想把 QPS 作为 HPA 指标,配置大概长这样:

apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: api-qps-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: api-server minReplicas: 2 maxReplicas: 20 metrics: - type: Pods pods: metric: name: http_requests_per_second target: type: AverageValue averageValue: 100

这个配置的含义是:每个 Pod 的平均 QPS 目标为 100,当实际平均 QPS 超过 100 时就扩容。注意这里用的是type: Pods,它要求指标必须能按 Pod 维度聚合——通常需要在 Prometheus 的查询里加sum by (pod)才能得到每个 Pod 的指标值。

自定义指标确实灵活,但它有一个隐性成本:你的指标链路多了一环。Prometheus 采集、Adapter 转换、HPA 拉取,任何一个环节挂了,HPA 就拿不到指标,也就不会执行扩容。所以生产环境里,我建议核心链路仍然以 CPU/内存兜底,自定义指标作为辅助,不要把所有宝都押在自定义指标上。

2.4 扩缩容行为控制(Behavior)

很多同学配置 HPA 只配minReplicas、maxReplicas和metrics,完全忽略了behavior,结果生产环境出现"副本数像个风扇一样来回吹"问题。Behavior 就是专门用来治理这个的。

我先解释一下为什么默认行为不够好。默认情况下,HPA 扩容每秒允许翻倍(100%),缩容每 5 分钟最多降 50%。这个配置在大多数场景没问题,但有两个痛点:

  • 扩容太快可能把依赖的下游服务压垮。比如你的服务突然从 5 个副本扩到 20 个,这 20 个副本同时去连数据库,连接池直接打满。
  • 缩容太慢会浪费资源。如果流量已经跌了 30 分钟,HPA 可能还在一次只缩 10%,资源费用居高不下。

Behavior 的基本结构是对scaleUp和scaleDown分别定义策略组。策略支持两种类型:Pods(按 Pod 数量限制)和Percent(按当前副本数百分比限制)。多个策略可以同时生效,通过selectPolicy来决定取哪个——Max取最激进的,Min取最保守的。

我分享一个比较稳妥的配置思路:扩容时,短时间内允许翻倍,同时限制单步最大增加数,防止一次扩太多;缩容时,加长稳定窗口,限制缩容比例,让副本数缓慢回落。这个配置的关键在于,你要结合自己的服务启动时间和下游容忍度来不断调参,没有一个通用值能适配所有业务。

3. VPA 的推荐机制与实战姿势

3.1 VPA 的三种模式

VPA 的部署形态通常包含三个组件:Recommender(推荐器)、Updater(更新器)、Admission Controller(准入控制器)。部署完成后,你通过创建VerticalPodAutoscaler对象来声明要管理哪个工作负载,其中最关键的是updateMode字段,它有三个可选值:

  • Off:只提供推荐值,不做任何修改。适合前期观察阶段。
  • Initial:只在 Pod 创建时应用推荐值,之后不更新。适合重启成本高的服务。
  • Auto:自动更新 Deployment 的资源配额,并通过驱逐 Pod 让变更生效。这是全自动模式。

我强烈建议所有团队第一次接入 VPA 时,先跑Off模式观察两到三周。为什么?因为 VPA 的推荐值是建立在历史 CPU、内存使用曲线上的,如果这个服务刚刚经历过一次大版本发布,或者流量模式变化很大,推荐值会一直在漂移,你过早开启Auto模式,会发现 Pod 被不停重启。等推荐值曲线稳定下来,再切换到Auto也不迟。

3.2 推荐值是怎么算出来的

VPA 的 Recommender 会从 Metrics Server 或者自定义的指标源拉取 Pod 的资源使用数据,然后按照分位数算法计算推荐值。简单理解:它会统计过去一段时间(默认 8 天)内"某个资源使用率的分位数",比如取 95 分位数作为 request 推荐值,这样能保证大多数情况下资源不会被打爆,又不会预留太多。

这里有一个非常实用的细节:VPA 会根据容器实际使用量,并且结合 Pod 当前的 request 和 limit 做出推荐。具体而言,它给出的推荐值包含lowerBound、target和upperBound三档:

  • target:推荐给 request 的值,满足绝大部分时间的需求。
  • lowerBound:低于这个值可能导致资源不足,通常用于判断是否需要扩容。
  • upperBound:高于这个值属于浪费,通常用于判断是否需要缩容。

你在查看 VPA 状态时,能看到类似这样的信息:

Status: Recommendation: Container Recommendations: Container Name: server Lower Bound: Cpu: 500m Memory: 512Mi Target: Cpu: 800m Memory: 768Mi Upper Bound: Cpu: 1200m Memory: 1Gi

这个信息相当有用。你可以直接把target值应用到 Deployment 的 request 上,比开发自己拍脑袋定资源靠谱太多了。

3.3 VPA 和 HPA 的兼容性问题

这是 VPA 方案里最容易翻车的地方。官方文档明确说:不要同时对同一个工作负载同时启用基于 CPU/内存的 HPA 和 VPA。这两个自动扩展器面对同一个指标做出的反应会互相冲突——HPA 觉得 CPU 高了就加副本,VPA 觉得 CPU 高了就想给单个 Pod 升配,最后两个都在改,谁也控制不住局面。

但实际生产环境里,很多服务既需要横向扩缩容应对流量波动,又需要资源配额合理化,怎么办?实践中比较可行的组合方式有三种:

  • HPA 基于自定义指标(比如 QPS、延迟),VPA 基于 CPU/内存指标。两者看的是不同的指标源,交集较小。
  • HPA 负责高峰期的横向扩容,minReplicas设高一些,VPA 只在Off或Initial模式下提供资源建议,由人工定期应用。
  • 同一个工作负载只启用 VPA,但通过 VPA 之外的手段(比如工作负载自身的分片逻辑)来提升水平能力。

我自己更偏向第一种。把 HPA 的指标切换成 QPS 这类业务指标,CPU/内存则交给 VPA 来合理化,这样两者的控制目标不重叠,系统才能稳定运行。如果你确实没法提供自定义指标,那至少要把 HPA 的目标值设得比较保守(比如 CPU 目标是 80%),让 HPA 在极端情况下才介入,日常资源调整交给 VPA。

3.4 VPA 配置与运维要点

一个标准的最小配置如下:

apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: payment-service-vpa spec: targetRef: apiVersion: apps/v1 kind: Deployment name: payment-service updatePolicy: updateMode: Auto resourcePolicy: containerPolicies: - containerName: "*" minAllowed: cpu: 100m memory: 128Mi maxAllowed: cpu: "4" memory: 8Gi controlledResources: ["cpu", "memory"]

minAllowed和maxAllowed是必须认真设置的。不设的话,VPA 可能把某个容器的内存推荐得非常高,或者低到没法运行。我之前遇过一个问题:某个批量任务容器内存只用 30Mi,VPA 把推荐值压到了 50Mi,结果任务数据量一上来直接 OOM。所以资源和业务特性强相关的容器,一定要给出合理的上下限。

另外一个运维细节:VPA 的Auto模式在更新资源后,是通过驱逐 Pod 来让新配置生效的。如果你的业务有长连接、WAF 白名单、或者不允许随意断开的场景,要慎重考虑是否接受这种"突变式"更新。Kubernetes 的 PodDisruptionBudget(PDB)可以用来限制同时驱逐的 Pod 数量,但 VPA 驱逐时会不会完全遵守 PDB,不同版本表现有差异,测试的时候要专门验证。

最后,VPA 组件本身是需要部署的,它不像 HPA 那样内置在 K8s 控制平面里。通常用 Helm Chart 部署,安装完成后记得检查vpa-recommender组件的日志,确认它能正常读取 Metrics Server 数据。有个常见问题是 Metrics Server 本身抓不到某些节点的 kubelet 指标,导致 VPA 没有数据来源,推荐值一直为空。

4. CA 集群扩缩容:从节点池设计到防抖治理

4.1 CA 的核心工作流程

CA(Cluster Autoscaler)是一个独立运行在集群里的控制器,它会定期扫描集群状态,核心逻辑就两个方向:扩容和缩容。

扩容的触发条件是"存在无法调度的 Pod"。这个无法调度往往是资源不足导致的——所有节点都没有足够的 CPU、内存或 GPU 等资源来满足这个 Pod 的 request。CA 发现这种情况后,会去找对应的节点池(Node Group),看看能否通过扩容节点来解决。如果能,它就会调用云厂商的接口创建节点,或者唤醒已经缩容的节点。

缩容的触发条件是"存在利用率过低的节点"。CA 会检查每个节点上所有 Pod 的实际资源使用量(不是 request),如果某个节点在所有非终止状态的 Pod 里,利用率都低于一个阈值(默认 50%),且节点上所有 Pod 都可以被重新调度到其他节点,CA 就会先把 Pod 驱散,然后把这个节点缩掉。

这个机制看起来并不复杂,但实际生产环境里涉及大量细节。比如节点上有本地存储的 Pod 不能被随便迁移、节点上有 kube-system 命名的 Pod 也不会被驱逐、某些有严格调度约束的 Pod(比如独占 GPU 的)可能永远无法被"挤"到其他节点上。CA 会谨慎处理这些情况,宁可不缩,也不要造成服务中断。

4.2 扩容判断与不可调度 Pod

我见过很多同学把 CA 的扩容想象成"集群资源快满了就自动加节点",其实不完全是。CA 只看一件事:有没有 Pod 处于 Pending 状态,并且这个 Pending 的原因是资源不足。如果你的集群里有很高的资源水位,但所有 Pod 都还能被调度,CA 是不会扩容的。

那什么时候会出现"所有节点都满了但 CA 不扩容"的情况?最常见的原因是节点组设置不对。比如你的节点池设置了最大节点数 10,当前已经是 10 个节点,Pod 依然 Pending,CA 也没法再加了。或者某些特殊资源(GPU、本地 SSD)对应的节点池没有配置自动扩容,CA 只会尝试现有的节点组。

还有一类情况经常被忽略:Pod 的调度约束太强,比如要求必须调度到某个可用区、必须带有某个标签的节点上。如果这个"池子"已经满了,即使集群里有其他空闲节点,CA 也不会为它扩容——因为它知道新加的普通节点满足不了这个 Pod 的约束条件。排查这类问题,最有效的命令是:

kubectl describe pod <pod-name> | tail -40

看Events里有没有FailedScheduling的详细原因,CA 的日志里也会写明为什么无法扩容。别一看到 Pod Pending 就怪 CA,先确认调度器给你的信息。

4.3 缩容判断与节点利用率

CA 的缩容判断比扩容更保守。默认情况下,节点必须持续 10 分钟(由scale-down-unneeded-time参数控制)都处于低利用率状态,才可能被缩掉。这个参数直接影响成本——设短了容易抖动,设长了浪费钱。我建议先保持默认的 10 分钟,运行一段时间看数据再调。

另一个关键参数是scale-down-utilization-threshold,默认 0.5。意思是节点资源平均利用率低于 50% 时,这个节点才"有资格"被缩减。这个值偏好会导致资源利用率长期偏低,偏好又可能导致节点刚腾出一点空间就被缩掉,然后突然一个流量波峰又需要立刻扩容,来回折腾。生产环境我一般设在 0.5 到 0.6 之间,但要结合节点的规格来看——如果节点规格很大(比如 32C64G),利用率达到 60% 意味着还有大量闲置资源被放在那里,其实可以更激进一点。

CA 还有一个细节值得注意:它不会缩掉自己。"自己"指的是 CA 所在的节点。同时,如果节点上有运行中的 Pod 不受任何控制器管理(比如裸 Pod),或者设置了PodDisruptionBudget且当前不满足 PDB 的最小可用副本数要求,CA 会跳过这个节点。所以你要实现有效的自动缩容,业务 Pod 最好都有 Deployment/StatefulSet 管理,并且别把 PDB 的minAvailable设得比节点上副本数还大。

4.4 与云厂商的集成差异

CA 本身是一个通用的开源控制器,但它和云厂商的集成方式不同,这会直接影响你配置的写法。以常见的云环境为例:

  • 在公有云 Kubernetes 服务环境里,CA 通常已经由云厂商集成,你只需要在节点池(Node Group)配置里打开弹性伸缩,并设置最小/最大节点数即可。
  • 在自建集群里,CA 需要你通过--cloud-provider参数指定提供方,并配置对应的凭据和节点组发现规则,比如通过标签或 ASG 名称识别哪些节点属于哪个组。

不管是哪种方式,有几个通用参数我要提一下:

--scale-down-enabled=true --scale-down-utilization-threshold=0.5 --scale-down-unneeded-time=10m --new-pod-scale-up-delay=0s --max-nodes-total=100

new-pod-scale-up-delay决定了一个新 Pod 创建后,CA 等待多久才允许扩容。设成 0 意味着立即响应,但如果你的调度器本来就需要时间做调度决策,设一个 30 秒到 60 秒的延迟反而更合理,能避免因为 Pod 调度到已有节点后又取消扩容的情况。我踩过这个坑:当时设了 0,结果 HPA 连续触发扩容,CA 几乎同时开启了十几个节点,流量一过这些节点又全要缩掉,云账单那两周非常难看。

4.5 成本与稳定性取舍

CA 的最终价值是把集群的节点数量贴合实际负载,但如果不做约束,它反而会成为费用黑洞。我总结几个必须做的事:

第一,节点池的min值一定要谨慎设置。这个值决定了你的集群在任何情况下至少保留多少节点,也决定了集群的"保底成本"。不要觉得设大一点无所谓,哪怕没有任何业务流量,这些节点也会产生费用。

第二,为不同的工作负载划分独立的节点池。比如把在线 Web 服务和离线批处理任务分成不同的节点池,分别设置弹性范围和扩缩容参数。否则混在一起,离线任务把节点占满,在线服务的扩容请求会被延迟响应。

第三,频繁扩缩容的节点池要考虑节点"预热"时间。云厂商创建一台新节点,从启动到 Kubelet 注册成功、镜像拉取完成、Pod 调度上去,往往需要 2~5 分钟。如果你的业务对扩容时间很敏感,可以提前在节点池里预留一定的空闲节点,或者使用"突发性能"型节点的自动恢复能力来缓解。这个就不能全指望 CA 了,它本质上是一个成本优化工具,不是高可用兜底方案。

5. 三合一方案设计与落地检查清单

5.1 推荐的组合架构

说了这么多,到底怎么把三个自动扩展器组合进同一个集群?我给出一套经过多环境验证的推荐架构,你可以按这套思路展开。

整体思路是分层处理:HPA 应对短时流量波动,VPA 做资源配额的持续合理化,CA 做集群容量的动态伸缩。

  • 对每个核心在线服务,启用 HPA,指标优先选择业务类(QPS、延迟),CPU/内存作为第二指标辅助判断。配置behavior控制扩容步进和缩容冷静期。
  • 同时对同一组服务部署 VPA,但updateMode先设为Off,观察两周以上。如果 HPA 用的是自定义业务指标,VPA 可以安全地使用 Auto 模式;如果 HPA 用的还是 CPU,那么 VPA 保持 Off,仅人工参考推荐值调资源。
  • 集群层面启用 CA,按服务类型拆分节点池,每个节点池需要配置合理的最小值、最大值。在线服务节点池的 min 值要考虑"基础流量 + HPA 扩容响应延迟"所需的缓冲容量。

下面这个表可以帮你快速做选型判断:

需求首选方案辅助方案不推荐
应对流量高峰HPA(按QPS/CPU)CA节点缓冲单纯靠 VPA
资源请求长期不合理VPA(Off/Auto)人工规范审查—
集群节点容量不足CA节点池扩容预留节点手动批量建节点
服务不适合横向扩容VPA Auto调整单副本规格HPA

5.2 落地前需要确认的资源参数

在给所有服务统一上自动扩展器之前,我建议先组织一次"资源梳理"行动。你至少要为每个服务确认三个数:

  • 稳态副本数:服务在正常流量下需要多少个副本能稳定运行。
  • 峰值副本数:服务在最大预期流量下需要多少个副本才扛得住。
  • 单副本资源基线:单个 Pod 实际需要多少 CPU 和内存(可以用 VPA 的 Off 模式沉淀几天数据)。

为什么要先梳理?因为 HPA 的minReplicas、VPA 的上下限、CA 的节点池 min 值,最终都要根据这些数来设。没有基线的团队,所有参数都是拍脑袋,上线后不是频繁扩容导致费用飙升,就是指标触达不到导致该扩没扩。

我之前配合过某团队做过一次梳理,结果发现他们一个在线服务单 Pod 的 request 写的是 2C4Gi,实际运行只需要 500m1Gi,几乎浪费了 75% 的配额。把配额调低后,节点上直接多出了三分之一的可调度容量,CA 的缓冲区压力顿时小了很多。这才是扩容体系的真正基础。

5.3 监控与告警设计

自动扩展器本身不产生告警,你要自己建立监控体系。我建议至少监控以下几类指标:

  • HPA 的当前副本数和期望副本数(kube_horizontalpodautoscaler_status_current_replicas、kube_horizontalpodautoscaler_status_desired_replicas),如果期望一直大于当前,说明扩容跟不上。
  • HPA 的指标缺少率,可以用kube_horizontalpodautoscaler_spec_target_metric和实际采集到的指标做对比,一旦发现指标缺失,及时排查 Metrics Server 或 Adapter。
  • VPA 的推荐值和当前 request 的差值。如果长期偏离,说明 Pod 配置没跟上推荐值。
  • CA 的扩容/缩容事件,以及节点池里节点的数量变化。可以用云厂商的事件日志或者 CA 自身的 Prometheus 指标来观察。
  • Pending Pod 数量,这是一个非常直观的红线指标。只要 Pending Pod 持续存在超过 5 分钟,就必须拉告警。

尤其要提醒的是 HPA 的状态输出。你可以用这条命令快速查看当前 HPA 的状态:

kubectl get hpa -n <namespace>

输出里有TARGETS列,格式类似120%/70%。如果你的 TARGETS 长期超目标但REPLICAS没有增长,那就说明扩展器没能及时扩容,或者maxReplicas到达上限了。这类问题往往不会直接让服务挂掉,但会在流量再涨一步时瞬间触发故障,所以平时就要盯住。

6. 常见问题与排查技巧实录

6.1 HPA 不扩容,副本数纹丝不动

这是被问得最多的问题。HPA 配好了,Deployment 也引用了,但无论怎么压测,副本数就是不变。我通常按这个顺序排查:

第一步,检查 HPA 有没有拿到指标:

kubectl describe hpa <name> -n <namespace>

看Status.Metrics和Conditions,如果AbleToScale为 False,说明 HPA 到 scale 子资源都调不通;如果ScalingActive为 False,说明指标没有采集到。最常见的原因是 Metrics Server 没装好,或者 Pod 的资源 request 没有设置(HPA 基于 Utilization 计算时必须依赖 request)。

第二步,检查 target 值设置是否合理。假如 Deployment 有 10 个副本,每个副本的 CPU request 是 2 核,你的 CPU 目标是 70%,那么 HPA 需要总使用量达到10 × 2 × 0.7 = 14 核才会触发扩容。如果你只压到 5 核,那当然不会扩。这个计算要心里有数。

第三步,确认 HPA API 版本。autoscaling/v1只支持 CPU,且没法配置多指标;如果你配了autoscaling/v2beta2,但集群版本较老,某些字段会被忽略。最好的办法是升级集群到较新的稳定版,统一用autoscaling/v2。

6.2 VPA 看起来没生效,推荐值一直不变

VPA 部署成功后,如果推荐值几天都不变,先看 VPA 有没有拿到历史数据。VPA 的 Recommender 会定期把 Pod 的资源使用记录写进自身的存储里,如果集群刚搭建不久,或者 Metrics Server 的历史数据保留时间太短,推荐值会延迟出现。

另一个很隐蔽的原因是目标工作负载的 Pod 有些没有设置 request。VPA 只会管理设置了 request 的容器,如果一个 Deployment 的资源定义里压根没有 request,VPA 的推荐器可能根本不会处理它。所以你的基线 YAML 里一定要有 request,哪怕先写成很小的值也可以,关键是让 VPA 知道"这个容器的规格是可控的"。

还有一种情况是,你同时开启了多个 VPA 指向同一个 Deployment,后面的 VPA 直接报冲突,只有匹配条件更精准的会生效。用kubectl get vpa查看状态,有Failed状态的直接看 Events。

6.3 CA 频繁扩容缩容抖动

抖动是 CA 最常见的问题,表现为节点数一会儿加一两个、过一会儿又缩掉,如此反复。为什么会这样?最根本的原因是节点利用率刚好卡在临界值附近,再加上 HPA 也在不断调整副本数,导致节点总量在"够用"和"不够用"之间摇摆。

解决抖动我有几个实用建议:

  • 调大scale-down-unneeded-time,默认 10 分钟可以适当加到 15 到 30 分钟,给系统更长的观察期。
  • 调低scale-down-utilization-threshold,比如从 0.5 降到 0.4,这样只有节点是真的空闲才缩,不会刚腾出来就被塞回去。
  • 给重要服务单独建节点池,避免和批处理任务混跑导致的干扰。
  • 排查是否有大量镜像拉取拖慢 Pod 启动时间——如果新节点上 Pod 长时间没有 Ready,它占的资源可能很低,CA 可能误判这个节点不需要缩容。

另外,检查你 CA 的--skip-nodes-with-system-pods参数,如果设成 true,带有 kube-system Pod 的节点永远不缩,那么在系统 Pod 调度较分散的大集群里,你可能发现"能缩的节点根本没几个"。

6.4 三个自动扩展器同场时的"资源幽灵"

最后分享一个比较进阶的坑,我把它叫"资源幽灵"。

场景是这样的:服务的 HPA 基于 CPU 目标值为 60%,同时 VPA 开着 Auto 模式。某次流量上涨,HPA 把副本从 4 扩到了 10,而 VPA 此时也在调整单 Pod 的资源配额。于是节点上所有 Pod 的 request 总和突然增大,节点看起来"满了",但实际 CPU 使用率并不高——因为大部分新增的 request 是 VPA 调上去的"纸面配额"。CA 感知到节点容量不足开始扩容,但实际运行负载根本不需要那么多节点。钱就这样无声无息地烧掉了。

这种问题非常难发现,因为它不会直接引发故障,只有对账单敏感的人才能感觉到不对劲。解决思路还是回到第 3 章的兼容性讨论:HPA 和 VPA 不要同时基于 CPU/内存做控制。资源配额这块只让一个"大脑"做主,另一个要么用业务指标,要么关掉自动模式。

我现在的做法是:核心在线服务全部使用 HPA(基于 QPS 和 CPU 组合),VPA 只开 Off 模式,每周由运维同学根据推荐值批量整理一次资源调整工单,人工审核后应用。 VPA 的 Auto 模式留给那些稳定运行、版本发布不频繁的内部服务,并且严格控制minAllowed和maxAllowed的范围。这个策略谈不上多优雅,但胜在可控,毕竟自动扩展器帮你省下的资源成本,如果因为互相干扰变成故障成本,那才是真的得不偿失。

最后再给一个实用建议:不管是 HPA、VPA 还是 CA,我强烈建议你在接入前先搭一套压测环境,按照"预期的 2 倍流量"来压一次。观察三者的反应时序——HPA 应该先动,CA 随后补节点,VPA 按周维度调整配额。如果时序乱了,多半是参数设置或者指标链路有问题,趁早发现,别等上线了再救火。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询