4、HPA(水平Pod自动伸缩):HPA工作原理、基于CPU/Metrics的自动伸缩、HPA配置实战

说到弹性伸缩,HPA绝对是Kubernetes里最常用的手段之一。我刚开始接触K8s那会儿,最头疼的就是流量一来,Pod扛不住;流量一走,资源又白白浪费。HPA就是来解决这个问题的——说白了,它帮你自动调整Pod的数量,让应用始终处于一个“刚刚好”的状态。

4.1 HPA的工作原理

HPA的全称是Horizontal Pod Autoscaler,水平Pod自动伸缩。它不关心Pod里跑的是什么,只关心Pod的数量够不够。

它的工作流程其实很简单,我拆成三步来讲:

  1. 采集指标:HPA通过Metrics Server(或者自定义的指标API)获取Pod的CPU、内存等资源使用率。
  2. 计算期望副本数:根据当前指标值和目标值,算出一个“应该有多少个Pod”的数字。
  3. 调整副本数:调用K8s的API,更新Deployment或StatefulSet的replicas字段。

嗯,这里要注意:HPA不会直接操作Pod,它操作的是控制器(比如Deployment)。你想想看,如果直接操作Pod,那Pod挂了谁负责重建?所以HPA只管“数量”,不管“生死”。

核心公式(简化版):

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

举个例子:当前有2个Pod,CPU使用率平均80%,目标设为50%。那么期望副本数 = 2 × (80% / 50%) = 3.2,向上取整就是4个Pod。

我个人习惯把HPA理解成一个“PID控制器”的简化版——它没有积分和微分项,只有比例控制。所以如果指标波动剧烈,HPA可能会频繁伸缩。后面我会讲到怎么避免这个问题。

4.2 基于CPU/Metrics的自动伸缩

HPA支持两种指标来源:

  • 资源指标:CPU和内存,由Metrics Server提供。这是最常用的方式,配置也最简单。
  • 自定义指标:比如QPS、请求延迟、队列长度等,需要配合Prometheus Adapter或Kubernetes Metrics API来实现。

我在项目中遇到过一个问题:只用CPU做伸缩,结果某个服务是IO密集型的,CPU一直很低,但请求已经堆积成山了。后来我加上了自定义指标——基于请求队列长度来伸缩,效果立竿见影。

我的建议:

对于Web服务,建议同时使用CPU和自定义指标(如QPS)。CPU负责应对突发计算负载,QPS负责应对流量波动。两者互补,伸缩更精准。

4.3 HPA配置实战

光说不练假把式。我们直接上手一个完整的HPA配置。

4.3.1 前置条件:安装Metrics Server

HPA依赖Metrics Server来获取Pod的CPU和内存数据。如果你还没装,先跑这条命令:

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

验证一下:

kubectl top pods
# 如果能看到每个Pod的CPU和内存使用率,说明Metrics Server正常工作

4.3.2 创建一个Deployment(作为伸缩目标)

我们先部署一个简单的Nginx服务,给它设置资源请求:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        resources:
          requests:
            cpu: "200m"
            memory: "256Mi"
        ports:
        - containerPort: 80

注意:HPA必须依赖resources.requests来计算使用率。如果你没设置requests,HPA会报错。我曾经踩过这个坑,排查了半天才发现是资源限制没写。

4.3.3 创建HPA(基于CPU)

最简单的HPA配置,基于CPU使用率:

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: 50

解释一下关键字段:

  • minReplicas / maxReplicas:伸缩的上下限。我建议最小值至少设2,保证高可用。
  • averageUtilization: 50:目标是让每个Pod的CPU使用率维持在50%左右。

创建并查看状态:

kubectl apply -f hpa.yaml
kubectl get hpa web-server-hpa
# 输出示例:
# NAME             REFERENCE               TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
# web-server-hpa   Deployment/web-server   30%/50%   2         10        2          1m

看到TARGETS列显示30%/50%,说明当前使用率30%,目标50%,副本数2个,一切正常。

4.3.4 基于内存的HPA

有时候CPU不准,内存更能反映压力。比如Java应用,GC频繁时CPU可能不高,但内存已经快爆了。配置如下:

metrics:
- type: Resource
  resource:
    name: memory
    target:
      type: Utilization
      averageUtilization: 70

也可以同时配置CPU和内存:

metrics:
- type: Resource
  resource:
    name: cpu
    target:
      type: Utilization
      averageUtilization: 50
- type: Resource
  resource:
    name: memory
    target:
      type: Utilization
      averageUtilization: 70

这种情况下,HPA会取两个指标中“最需要扩容”的那个作为决策依据。说白了,只要有一个指标超标,就会触发扩容。

4.3.5 基于自定义指标(QPS示例)

如果你已经部署了Prometheus和Prometheus Adapter,可以这样配置基于QPS的HPA:

metrics:
- type: Pods
  pods:
    metric:
      name: http_requests_per_second
    target:
      type: AverageValue
      averageValue: "100"

这个配置的意思是:每个Pod的QPS超过100时,就扩容。我记得有一次线上大促,QPS从50飙到500,HPA在30秒内把Pod从3个扩到了20个,稳住了服务。如果没有HPA,那天估计得通宵扩容了。

避坑指南:

我曾经遇到过HPA“震荡”的问题——Pod数量在短时间内反复增减。原因是指标波动太快,HPA反应过度。解决方案有两个:

  • 设置--horizontal-pod-autoscaler-downscale-stabilization参数,默认5分钟,可以调大到10分钟。
  • 使用behavior字段配置伸缩策略,比如扩容可以快,缩容要慢。

4.3.6 高级配置:behavior(伸缩行为控制)

从K8s 1.18开始,HPA支持behavior字段,让你精细控制伸缩行为:

behavior:
  scaleDown:
    stabilizationWindowSeconds: 300  # 缩容稳定窗口,5分钟内不重复缩容
    policies:
    - type: Percent
      value: 10
      periodSeconds: 60
  scaleUp:
    stabilizationWindowSeconds: 0     # 扩容不等待
    policies:
    - type: Pods
      value: 4
      periodSeconds: 15

这个配置的意思是:

  • 扩容:每15秒最多增加4个Pod,不设稳定窗口,响应要快。
  • 缩容:每60秒最多减少10%的Pod,且有5分钟的稳定窗口,防止频繁缩容。

我个人习惯把扩容设得激进一些,缩容设得保守一些。毕竟扩容慢了可能丢请求,缩容慢了顶多多花点钱。

4.4 总结

HPA是K8s弹性伸缩的基石。它不复杂,但用好它需要你对业务指标有清晰的认识。我的建议是:

  • 先用CPU做基础伸缩,保证基本弹性。
  • 再根据业务特点,加入自定义指标(QPS、延迟、队列深度等)。
  • 最后用behavior控制伸缩节奏,避免震荡。

嗯,HPA的内容就到这里。下一章我们会聊聊VPA(垂直Pod自动伸缩),它和HPA是互补关系——一个管数量,一个管大小。到时候我会讲讲什么时候该用HPA,什么时候该用VPA。

一句话记住HPA:

HPA = 根据指标自动调整Pod数量,让你从“手动扩容”中解放出来。