Kubernetes弹性伸缩体系:HPA、VPA、KEDA与节点扩容实战

弹性伸缩不是“CPU到80%就加Pod”。真正的生产体系需要把业务指标、Pod副本、资源请求、节点容量、启动时间和下游承载能力连接起来。HPA负责横向副本,VPA负责资源规格,KEDA把事件源接入伸缩链路,节点伸缩器为待调度Pod补充底层容量;其中任何一层设计错误,都可能出现抖动、排队、资源浪费或雪崩。

本文面向使用 Kubernetes 承载在线服务、异步消费者或周期性任务的团队。示例参数必须结合真实负载测试调整,不应直接复制到生产。

一、先画清四层控制回路

层级 观察对象 调整对象 典型实现
业务弹性 QPS、并发、队列长度、延迟 Pod副本 HPA、KEDA
资源校准 CPU、内存、OOM和历史用量 requests/limits VPA
集群容量 Pending Pod、调度约束 节点数量和规格 Cluster Autoscaler、Karpenter或云实现
业务保护 错误率、下游容量、预算 上下界、限流和停止条件 发布平台、网关、应用策略

伸缩链路通常是:负载上升→指标变化→增加Pod→Pod进入Pending→节点扩容→镜像拉取→Pod就绪。最终生效时间可能是几十秒到数分钟,所以只看控制器轮询周期会严重低估响应时间。

二、伸缩前先完成资源基线

HPA按CPU利用率伸缩时,利用率是“实际使用量/requests”。若没有 requests,相关Pod可能无法参与计算;requests严重偏低会过度扩容,偏高则可能迟迟不扩容。先采集至少一个完整业务周期的CPU、工作集内存、GC、吞吐和延迟,再给出初始值。

建议区分:

  • 可压缩资源CPU:过载通常表现为节流和延迟;
  • 不可压缩资源内存:超限通常导致OOMKill;
  • 启动资源峰值:JVM预热、模型加载、缓存恢复;
  • 单Pod安全吞吐:压测得到的持续承载量,而非瞬时峰值。

弹性不能替代容量底线。关键服务的 minReplicas 至少覆盖一次故障域失效、滚动发布和PDB约束。

三、HPA v2的基本配置

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: checkout
  namespace: shop
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: checkout
  minReplicas: 4
  maxReplicas: 40
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
      - type: Percent
        value: 100
        periodSeconds: 60
      - type: Pods
        value: 4
        periodSeconds: 60
      selectPolicy: Max
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 20
        periodSeconds: 60
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 60

maxReplicas 应由下游容量、节点上限和成本预算共同决定。扩容可相对积极,缩容通常需要稳定窗口,防止短暂低谷后立刻删除刚预热完成的实例。

四、选择能代表瓶颈的指标

CPU只适合CPU与吞吐近似线性的服务。以下场景应考虑自定义或外部指标:

  • Web服务:每Pod并发、请求速率、排队长度或高分位延迟;
  • 消费者:待处理消息数、最老消息年龄、Kafka lag;
  • 批处理:剩余任务数和任务截止时间;
  • 连接型服务:活跃连接数和连接池占用;
  • GPU工作负载:队列深度、显存与批次等待时间。

高分位延迟可能在下游故障时升高,此时盲目扩容会放大压力。指标必须配合错误率、限流和熔断。若配置多个指标,HPA通常选择建议副本数最大的结果,应确认不会由噪声指标长期顶到上限。

五、控制抖动和指标缺失

常见抖动来源包括指标延迟、冷启动、突发流量、Pod尚未Ready和缩容过快。治理方法:

  • 为启动探针和就绪探针设置真实预热边界;
  • 让扩容指标领先于SLO恶化,例如队列长度早于超时;
  • 为缩容设置更长稳定窗口和较小比例;
  • 配置合理的容忍度,不追逐细小波动;
  • 指标系统不可用时保留安全副本,不把“无数据”解释为零;
  • 记录期望副本、当前副本、指标时间戳和伸缩原因。

不要让发布系统持续写 spec.replicas 与HPA争夺字段所有权。使用GitOps时,应按工具建议忽略或转移该字段的管理权。

六、VPA先从推荐模式开始

VPA由推荐器、更新器和准入组件协作。生产接入第一阶段宜使用 Off,只读取建议:

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: checkout-vpa
  namespace: shop
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: checkout
  updatePolicy:
    updateMode: "Off"
  resourcePolicy:
    containerPolicies:
    - containerName: app
      minAllowed:
        cpu: 200m
        memory: 256Mi
      maxAllowed:
        cpu: "4"
        memory: 4Gi
      controlledValues: RequestsOnly

至少覆盖工作日、周末、批处理和发布波峰,再决定是否自动应用。自动模式可能驱逐Pod或尝试原地调整;具体能力与Kubernetes、VPA版本和特性开关相关。关键业务要验证PDB、最小副本和单故障域容量。

七、HPA与VPA的组合边界

若HPA按CPU利用率伸缩,而VPA同时修改CPU requests,两者会相互影响。常见安全组合:

  • HPA使用业务或外部指标,VPA管理CPU和内存requests;
  • HPA使用CPU,VPA仅管理内存;
  • VPA只输出建议,由容量评审或流水线定期调整;
  • 对不可水平扩展的单实例服务使用VPA,但保留维护窗口和回退规格。

不要让VPA自动修改DaemonSet资源后又期待节点伸缩器准确预测新节点开销。系统组件应单独评估。

八、用KEDA连接事件源并缩到零

KEDA通常负责从0到1的激活,并创建或管理HPA完成1到N的伸缩。下面按Kafka积压伸缩消费者:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: order-consumer
  namespace: shop
spec:
  scaleTargetRef:
    name: order-consumer
  pollingInterval: 15
  cooldownPeriod: 300
  minReplicaCount: 1
  maxReplicaCount: 30
  fallback:
    failureThreshold: 3
    replicas: 4
  triggers:
  - type: kafka
    metadata:
      bootstrapServers: kafka.messaging.svc:9092
      consumerGroup: order-worker
      topic: orders
      lagThreshold: "200"
      activationLagThreshold: "10"

凭据应通过认证引用或工作负载身份提供,不写进清单。并非所有服务都适合缩到零:需要常驻连接、低延迟、较长启动时间或无法可靠唤醒的服务,应保留最小副本。

九、队列消费者的副本上限不是越大越好

Kafka同一消费组的有效并行度受分区数限制;20个分区配置100个消费者,额外副本可能处于空闲。RabbitMQ还受consumer prefetch、单消息处理时间和下游写入能力影响。

可用近似式估算所需并发:

所需消费者数 ≈ 峰值到达速率 × 单消息处理秒数 ÷ 单消费者并行度

再通过压测修正。伸缩指标应同时考虑总积压与最老消息年龄,避免低速大消息或毒消息被平均值掩盖。若下游数据库最多承受200个连接,就应通过连接池和最大副本限制把并发控制在安全范围。

十、节点伸缩要接住Pending Pod

节点伸缩器主要根据不可调度Pod的requests和调度约束决策,并不直接根据Pod真实使用量扩容。以下配置会影响结果:

  • nodeSelector、亲和/反亲和、拓扑分布;
  • taint与toleration;
  • 本地盘、GPU、架构和可用区要求;
  • DaemonSet预留和系统保留;
  • 节点组上限、云配额、库存和启动时间。

必须监控从“Pod Pending”到“节点Ready”和“应用Ready”的总时长。对突发业务可保留缓冲节点、占位Pod或定时预扩容;仅依赖临时创建节点往往赶不上秒级流量峰值。

十一、冷启动与就绪控制

容器启动完成不等于可承载流量。应拆分:

  • startupProbe:允许慢启动,但限定最大时间;
  • readinessProbe:缓存、依赖和监听端口均准备好才接流量;
  • livenessProbe:只判断是否需要重启,避免依赖抖动导致连环重启;
  • preStop和优雅终止:缩容时停止接新请求并完成在途任务。

镜像预热、JIT、模型加载和连接池建立都应计入伸缩延迟。通过分阶段压测确认新增副本何时真正降低队列或延迟。

十二、容量保护与失败回退

为每个伸缩对象定义:

  • 最小副本:满足高可用和常态负载;
  • 最大副本:受下游、安全预算和节点容量限制;
  • 指标失效回退副本;
  • 伸缩冻结开关和人工固定方法;
  • 扩容未改善时的限流、降级和熔断;
  • 节点无法交付时的告警和业务优先级。

当延迟持续升高而副本已达上限,应判断是容量不足、下游瓶颈、锁竞争还是单Pod效率下降。继续提高上限可能只会加速故障。

十三、观测与告警

至少建立以下面板:

  • 当前、期望、最小和最大副本;
  • 原始指标值、目标值、采集时间和缺失状态;
  • HPA/KEDA条件、事件和失败次数;
  • Pending Pod及调度失败原因;
  • 节点申请、注册、Ready和整合时长;
  • 新Pod启动、就绪、首个请求和稳定吞吐时间;
  • 扩容前后SLO、错误率、队列年龄和下游负载;
  • VPA建议与当前requests的偏差。

只在“副本变化”时告警会产生噪声。更有价值的是:长期顶到上限、指标不可用、持续Pending、扩容后SLO未恢复和频繁反向伸缩。

十四、故障排查顺序

kubectl describe hpa -n shop checkout
kubectl get hpa -n shop checkout -o yaml
kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes
kubectl get pods -n shop -l app=checkout -o wide
kubectl get events -n shop --sort-by=.lastTimestamp

依次确认:指标是否新鲜→目标对象和selector是否正确→requests是否存在→是否已到上下限→behavior是否限制变化→Pod是否Pending→节点伸缩是否受配额或调度约束阻挡→新增Pod是否真正Ready。

十五、上线演练

  1. 用历史流量回放验证指标与负载相关;
  2. 阶梯加压,记录每次扩容和SLO恢复时间;
  3. 模拟指标系统中断,确认安全回退;
  4. 模拟节点库存不足和镜像拉取慢;
  5. 注入下游限速,验证不会无界扩容;
  6. 执行缩容,确认连接排空和任务不丢失;
  7. 回滚HPA/KEDA/VPA配置并验证固定副本模式。

十六、生产检查清单

  • [ ] requests来自真实观测和压测,而非随意填写;
  • [ ] 指标代表瓶颈并有缺失处理;
  • [ ] 最小副本覆盖发布、故障域和PDB;
  • [ ] 最大副本受下游容量、分区数和成本约束;
  • [ ] 扩容、节点交付、启动和就绪总时长已测量;
  • [ ] HPA与VPA不争夺同一控制信号;
  • [ ] KEDA认证信息未明文写入配置;
  • [ ] 缩容具备稳定窗口和优雅终止;
  • [ ] 指标故障、节点不足和下游故障均有回退;
  • [ ] 每季度用真实流量模型复验参数。

十七、官方参考

成熟的弹性体系不追求副本变化得最频繁,而是用最少的控制动作在目标时间内恢复SLO,并且不突破下游容量与成本边界。

Copyright © yan-jian.com 2023 - 2026 · Powered by GitBook更新时间: 2026-10-21 09:42:20

results matching ""

    No results matching ""