4、弹性伸缩策略:让资源像呼吸一样自然——HPA与VPA的黄金组合

说实话,做PaaS平台最头疼的问题是什么?

不是功能不够多,也不是性能不够强。而是——资源到底该给多少?

给多了,成本飙升,老板找你谈话。给少了,业务抖动,用户投诉。我见过太多团队在这上面栽跟头。今天我们就聊聊怎么让资源分配这件事,变得像呼吸一样自然。

4.1 为什么需要两种伸缩?

先问一个问题:你的Pod为什么会扛不住流量?

无非两种情况:

  • 实例不够了——需要更多副本(水平伸缩)
  • 单个实例太弱了——需要更大规格(垂直伸缩)

HPA(Horizontal Pod Autoscaler)管第一种,VPA(Vertical Pod Autoscaler)管第二种。两者缺一不可。

核心观点:HPA解决“量”的问题,VPA解决“质”的问题。一个管横向,一个管纵向。组合使用,才能做到真正的弹性。

我在项目中遇到过一种情况:某个Java服务,CPU使用率一直很低,但内存持续上涨。如果只用HPA,它永远不会触发扩容——因为CPU没超标。但实际服务已经快OOM了。这时候VPA就派上用场了,它能动态调整内存的Request和Limit。

4.2 HPA:水平伸缩的正确姿势

HPA的原理很简单:监控指标 → 计算期望副本数 → 调整Deployment的replicas。

但很多人用错了。我见过最典型的错误——只盯着CPU和内存。

你想想看,如果你的应用是消息处理型的,CPU可能一直很低,但队列堆积严重。这时候CPU-based HPA根本不会动。怎么办?用自定义指标。

4.2.1 基于CPU/Memory的HPA

这是最基础的用法,适合大部分无状态服务。

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

我的习惯:CPU目标值设在60%-80%之间,内存设在70%-85%之间。太低容易频繁伸缩,太高又来不及反应。具体调优要看业务特性。

4.2.2 基于自定义指标的HPA

这才是真正的进阶玩法。比如基于QPS、请求延迟、队列深度等。

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: queue-worker-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: queue-worker
  minReplicas: 1
  maxReplicas: 20
  metrics:
  - type: Pods
    pods:
      metric:
        name: queue_depth
      target:
        type: AverageValue
        averageValue: 10

这里的关键是:你得先把指标暴露出来。我一般用Prometheus Adapter或者Kubernetes Metrics Server的自定义指标API。

避坑指南:我曾经把HPA的冷却时间设得太短,结果流量一波动,Pod疯狂创建和删除,搞得集群鸡飞狗跳。建议:--horizontal-pod-autoscaler-downscale-stabilization 设成5分钟以上,给系统一点缓冲。

4.3 VPA:让每个Pod都“合身”

VPA的作用是自动调整Pod的CPU和内存Request。说白了,就是帮你回答一个问题:这个Pod到底需要多少资源?

VPA有三种模式:

  • Auto:自动调整,并且会重建Pod
  • Initial:只在Pod创建时设置初始值
  • Off:只给出建议,不自动执行

我个人最推荐的做法是:先用Off模式跑一段时间,收集数据,看看VPA给出的建议是否合理。确认没问题了,再切到Auto模式。

4.3.1 VPA配置示例

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

注意:一定要设置minAllowed和maxAllowed。否则VPA可能会给你一个离谱的值——比如给一个sidecar容器分配2核CPU。我见过这种事故,真的。

4.4 HPA + VPA 黄金组合

单独用HPA或VPA都有局限。组合起来才是王道。

怎么组合?我的经验是:

  1. VPA负责“调优”——让每个Pod的资源申请更精准
  2. HPA负责“扩缩”——根据整体负载调整副本数

但这里有个坑:HPA和VPA不能同时修改同一个资源指标。比如VPA在调整CPU Request,HPA也在看CPU利用率,两者会打架。

解决办法是什么?

  • 让VPA只调整内存,HPA只看CPU
  • 或者用VPA的Initial模式,只在Pod启动时设置一次
  • 再或者,用HPA基于自定义指标(如QPS),VPA基于资源指标

我一般用第三种方案。举个例子:

# VPA只负责内存
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: web-vpa
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: web
  updatePolicy:
    updateMode: "Auto"
  resourcePolicy:
    containerPolicies:
    - containerName: "*"
      controlledResources: ["memory"]

---
# HPA只负责CPU和QPS
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  minReplicas: 2
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Pods
    pods:
      metric:
        name: requests_per_second
      target:
        type: AverageValue
        averageValue: 1000

一个小技巧:先用VPA跑一周的Off模式,收集建议。然后手动调整Deployment的Request值。等稳定了,再开Auto模式。这样能避免VPA刚上线时频繁重建Pod。

4.5 实战中的避坑指南

这些年踩过的坑,我总结了几条:

场景 问题 解决方案
HPA频繁伸缩 Pod频繁创建/删除,影响稳定性 增大冷却时间,设置合理的min/max
VPA重建Pod 业务中断,连接池失效 配合PDB使用,设置优雅终止
指标采集延迟 伸缩滞后,扛不住突发流量 缩短采集周期,或使用预测式伸缩
资源碎片化 集群节点资源利用率不均 结合Cluster Autoscaler使用

我曾经遇到过一个经典案例:某个电商大促期间,HPA配置了基于CPU的伸缩。结果流量上来时,CPU还没涨,但QPS已经翻了三倍。等CPU反应过来,服务已经超时了。

后来怎么解决的?加了基于QPS的自定义指标,并且把HPA的指标采集周期从60秒改成了15秒。效果立竿见影。

4.6 总结与建议

弹性伸缩这件事,说白了就是三个字:快、准、稳。

  • ——指标采集要快,决策要快,执行要快
  • ——指标选择要准,阈值设置要准
  • ——不要频繁抖动,不要影响业务

HPA和VPA的组合,就像汽车的油门和悬挂。HPA决定踩多深,VPA决定车身调多稳。两者配合好了,你的PaaS平台才能真正做到“资源像呼吸一样自然”。

最后说一句:没有银弹。每个业务的流量模型都不一样。我的建议是——先跑起来,再调优。别想着一步到位。弹性伸缩是持续优化的过程,不是一次配置就完事的。