Kubernetes 弹性伸缩体系:HPA、VPA、KEDA 与 Cluster Autoscaler 全解

深入解析 Kubernetes 弹性伸缩体系:HPA 水平扩缩容算法与自定义指标、metrics-server、VPA 垂直扩容的 Recommender/Updater/Admission Controller、KEDA 基于事件驱动的伸缩、Cluster Autoscaler 节点级扩缩容、多指标联动与规模化避坑。

Kubernetes 弹性伸缩体系由四个独立又联动的组件构成:HPA(水平扩缩 Pod 副本)、VPA(垂直调整单个 Pod 的请求资源)、KEDA(事件驱动的退避式伸缩)、Cluster Autoscaler(节点级扩容/缩容)。理解它们各管哪一段、如何配合,是让"按需伸缩"真正落地、而不是"要么固定副本要么只有 HPA"的关键。


目录


1. 弹性伸缩全景:四层各管什么

1.1 四层职责与协作

┌─────────────────────────────────────────────────────────┐
│  Cluster Autoscaler(节点级)                             │
│  不够节点 → 加节点;节点闲置 → 缩减节点                   │
│  与云厂商联动:AWS ASG / GCP MIG / 云节点池               │
└─────────────────────────┬───────────────────────────────┘
                          │ 节点就绪
┌─────────────────────────▼───────────────────────────────┐
│  HPA(Pod 副本级)                                       │
│  CPU/内存/自定义/外部指标 → 调 replicas                   │
└─────────────────────────┬───────────────────────────────┘
                          │ 请求量
┌─────────────────────────▼───────────────────────────────┐
│  VPA(单 Pod 额度级)                                    │
│  观测历史用量 → 建议并调整 requests/limits                 │
└─────────────────────────┬───────────────────────────────┘
                          │ 请求量
┌─────────────────────────▼───────────────────────────────┐
│  KEDA(事件驱动,横向下层之上)                            │
│  Kafka/Redis/队列深度/定时 → 触发扩容/缩到零              │
└─────────────────────────────────────────────────────────┘

一句话:CA 管"机器够不够",HPA 管"进程够不够",
      VPA 管"每份申请多少",KEDA 用业务事件驱动这一切。

ℹ️ 核心:HPA 是"响应式"的(指标超过阈值才动),KEDA 是"前置式"的(队列积压就提前扩)。不想让用户等待扩容的那几秒,就用 KEDA。


2. metrics-server 与度量管线

2.1 metrics-server:HPA 的度量来源

# 部署 metrics-server(收集每个 Pod/Node 的 CPU/内存用量)
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
# 检查是否正常
kubectl top nodes
kubectl top pods -n dev
metrics-server 特点:
  · 只提供 CPU/内存的"瞬时用量"
  · 数据来自 kubelet 的 cAdvisor,非持久化
  · HPA 仅靠它就能做 CPU/内存扩缩
  · 若需要更多指标(QPS/延迟/队列)→ 接 Prometheus 等自定义指标源

注意:metrics-server 是"每秒采样"的近似值,
      不适合做精确的告警/容量——那是 Prometheus 的职责。

2.2 度量管线的三条路径

路径一:metrics-server(resource metrics)→ 内置
  供 HPA 的 resource 指标(cpu/memory)

路径二:自定义指标 API(metrics.k8s.io)
  应用自定义指标(QPS、错误率)→ 由 Prometheus Adapter 聚合

路径三:外部指标 API(external.metrics.k8s.io)
  外部服务的指标(SQS 队列长度、Kafka lag、Redis 长度)
  → 常配合 KEDA 使用

3. HPA:水平扩缩容算法与配置

3.1 算法:副本数 = 当前值 / 目标值

HPA 每周期(默认 15s)计算:
  期望副本数 = ceil(当前指标值 / 目标值 × 当前副本数)

例:
  当前 CPU 平均利用率 80%,target 50%,副本数 4
  → 期望 = ceil(80/50 × 4) = ceil(6.4) = 7

平滑机制:
  - 防止震荡:只允许"每步扩 max(N, 2×current)"(快速扩容)
  - 缩容有--horizontal-pod-autoscaler-downscale-stabilization(默认 5 分钟)
    避免指标抖动导致反复缩容

3.2 一个典型的 HPA

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: orders-hpa
  namespace: shop
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: orders
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization     # 平均值(%)
          averageUtilization: 60
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 75
  behavior:                      # 细粒度扩缩控制
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 50             # 每次最多缩一半
          periodSeconds: 60

3.3 扩容周期性是"目标"而非"绝对"

关键理解:HPA 不保证"每个 Pod 都在 60% 以下",
  它只保证"副本数 × Utilization 调整到接近目标"。
  - CPU 利用率是平均值:某个 Pod 高、其他低也 OK
  - 扩太慢 → 短时间峰值响应不过来 → 需要更宽预警(KEDA)
  - 缩太慢 → 高峰过后也占用资源 → 调 downscale 策略

4. 多指标:自定义与外部指标

4.1 接 Prometheus 自定义指标

# HPA 用应用自定义指标(如每秒请求数)扩容
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-hpa
spec:
  scaleTargetRef:
    kind: Deployment
    name: api-gateway
  minReplicas: 2
  maxReplicas: 50
  metrics:
    - type: Object                  # 对象指标(按服务对象计算)
      object:
        metric: { name: "http_requests_per_second" }
        describedObject:
          kind: Service
          name: api-gateway
        target:
          type: Value
          value: 500               # 每个副本约 500 QPS

4.2 自定义指标需要一套采集器

自定义指标常用的承载方案:
  - Prometheus Adapter(prometheus-adapter):把 Prometheus 指标转成
    custom.metrics.k8s.io,供 HPA 消费
  - 采集:应用暴露 /metrics + ServiceMonitor / Prometheus 拉取

典型问题:指标缺失 / 命名不匹配 → HPA 无法匹配
  排查:kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1

5. VPA:垂直扩缩容的三个组件

5.1 VPA 由三个组件构成

VPA 三件套:
  1. Recommender   定期读取历史用量(metrics-server + 外部),
                  输出"建议的 requests/limits"
  2. Updater       决定是否要杀 Pod 以让新配置生效
    (先升级 Pod 来 replace requests)
  3. Admission Controller(OOM 时预警、Pod 创建时注入推荐值)

两种模式:
  - vpa-updater:改完要重启 Pod 生效(有牺牲)
  - admission:新 Pod 创建时就带上推荐值(推荐日常)

5.2 VPA 配置与查看建议

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: orders-vpa
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: "Deployment"
    name: orders
  updatePolicy:
    updateMode: "Auto"      # Auto / Initial / Off
  resourcePolicy:
    containerPolicies:
      - containerName: "*"
        minAllowed:
          cpu: 50m
        maxAllowed:
          cpu: "2"
          memory: 4Gi
# 查看 VPA 推荐(Auto 模式下生效前的中间态)
kubectl get vpa orders-vpa -o yaml | grep -A8 recommendation
# recommendation:
#   containerRecommendations:
#   - containerName: main
#     target:
#       cpu: 250m
#       memory: 1Gi

ℹ️ VPA 注意:VPA 与 HPA 在"同一维度(CPU)“上不应同时使用——否则互相打架。推荐:HPA 管副本数,VPA 只负责 requests 基准,二者通过对不同指标分工来协作(如 HPA 看 CPU 、VPA 调 requests)。


6. KEDA:事件驱动的伸缩

6.1 为什么需要 KEDA

HPA 是"响应式":指标超过才扩 → 高峰到来前的几秒有 latency
KEDA 是"事件驱动":队列一深、定时一到 → 提前扩容
  典型场景:
    - 消息消费者随队列深度扩容
    - 定时任务(Cron)按秒表缩放拉到 0(省资源)
    - 吞吐波动的工作负载(批处理、爬虫)
    - 拉起/缩到 0 的 Serverless 式工作负载

6.2 KEDA 配置示例(队列驱动)

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: consumer-scaler
  namespace: shop
spec:
  scaleTargetRef:
    name: consumer-deploy        # 被缩放对象
  minReplicaCount: 1
  maxReplicaCount: 30
  triggers:
    - type: kafka                 # 事件源
      metadata:
        topic: orders
        bootstrapServers: kafka:9092
        consumerGroup: workers
        lagThreshold: "100"       # 积压超过 100 条就扩容
    - type: cron                  # 双触发可组合
      metadata:
        timezone: Asia/Shanghai
        start: "0 9 * * *"
        end: "0 20 * * *"
        desiredReplicas: "20"
KEDA 内部:
  - Scaler Controller 连接事件源统计积压量
  - 把积压量映射成"External 指标"塞给 HPA
  - HPA 据此扩缩;队列空了 → 缩到 min(甚至 0)
  → 本质是"HPA + 事件适配器"的组合

6.3 KEDA 的活跃与闲置策略

要点:
  - 结合 HPA 的 minReplicas/maxReplicas 设置下限/上限
  - "缩到 0"需要确保应用能冷启动(Ready 探针/启动率)
  - 过多 ScaledObject → 抢 HPA 资源管理器,量力而行
  - 事件触发器的"消费组隔离":多个 worker 共享的组深度
    才是真的积压(避免把"已读未处理"当积压)

7. Cluster Autoscaler:节点级扩缩容

7.1 CA 做什么

Cluster Autoscaler 观察"待调度的 Pod":
  - 有 Pod Pending(没节点放得下)→ 添加节点
  - 节点长时间(默认 10 分钟)低占用 & 所有 Pod 可搬迁 → 缩节点

联动性:
  - 它只在"资源不足"时扩,"资源空闲"时缩
  - 配合 HPA:HPA 扩副本 → 节点指标上涨 → CA 加节点
  - 配合 VPA:VPA 提高 requests → 也可能触发 CA 扩节点

7.2 CA 的部署

# Cluster Autoscaler 需要云厂商节点组(如 AWS Auto Scaling Group)
# 以 AWS 为例,deployment 启动参数:
spec:
  containers:
  - name: cluster-autoscaler
    image: k8s.gcr.io/autoscaling/cluster-autoscaler:v1.30.0
    command:
    - ./cluster-autoscaler
    - --cloud-provider=aws
    - --nodes=1:10:worker-groups-eks  # min:max:ASG名
    - --scale-down-unneeded-time=10m
    - --scale-down-utilization-threshold=0.5
CA 关键参数:
  --min/max:每组节点的最小最大数
  --scale-down-unneeded-time:缩容前"空闲多久"
  --scale-down-utilization-threshold:节点利用率低于多少可缩
  --balance-similar-node-groups:平衡同构节点组

7.3 CA 避坑

坑一:节点不可由 CA 缩
  有 Pod 用了 local storage / 有 PDB → 打断了 → 加 `--skip-nodes-with-local-storage`
  单副本且无 PDB → CA 不敢缩 → 给关键工作负载建 PDB

坑二:扩缩回环
  负载下降 → CA 缩节点 → 集群小脚 → 又扩回 → 抖动
  用 --scale-down-unneeded-time 拉大 + utilization 阈值留缓冲

坑三:一次性泡沫
  高峰过去 → 需要时间 reduce → 用 CA 的延迟与上下限控制

8. 四者的联动与规模化避坑

8.1 一个完整的联动示例

场景:订单服务高峰
  1. Kafka 队列积压 → KEDA 触发扩 worker 副本(前置)
  2. worker CPU 涨 → HPA 发现 CPU>60% → 再补副本
  3. 现有节点放不下 → Pod Pending → CA 扩一个节点
  4. Prometheus 观测 → 队列消化 → KEDA 缩 → CA 缩节点
  → 四层协同,IP 高峰时段也能"按需就位"

HPA / VPA 分工表:
  - 指标类型         扩缩对象        适用
  - CPU/utilization  副本数         稳态服务
  - 内存/utilization 副本数         内存密集记录
  - 自定义/外部       副本数         QPS/队列/业务指标
  - 事件警报/KEDA     副本数+缩到0  需要前置伸缩的消息/批次
  - requests调整(VPA)r请求配置      组装建议(不与 HPA 抢同名指标)

8.2 常见坑汇总

问题现象对策
无 metrics-serverkubectl top 为空,HPA 无数据装 metrics-server
VPA/HPA 抢 CPU扩缩竞争/Config 冲突分工:VPA 只管 requests 不重叠指标
HPA 抖动频繁扩缩浪费stabilization window 拉宽
CA 缩不复空闲节点长期不降PDB/–max-utilization 配好
KEDA 缩 0调用方超时拉长缩容稳定窗口 + minReplicaCount 保底
指标口径含糊扩错了不缩明确 target 与误差、alerts 监控

8.3 监控伸缩的可观测性

# 观察 HPA 是否在合理工作
kube_horizontalpodautoscaler_spec_target_replicas     # 期望
kube_horizontalpodautoscaler_status_current_replicas    # 当前
kube_horizontalpodautoscaler_status_desired_replicas    # 期望由 HPA 计算
# 关键告警:current_replicas 长期 < desired(可能卡在 max/CA 不够)

9. 生产最佳实践

9.1 择路决策

- 常规稳定服务 → HPA + metrics-server(CPU/memory)
- 有流量概念 → 自定义/外部指标(QPS、队列)
- 高峰无等待 → 事件驱动 KEDA(消息/调度)
- 请求配置不合理 → VPA 轮询建议 + 人工/自动应用
- 负载跨节点 → Cluster Autoscaler + PDB + 预创建节点池

9.2 落地清单

□ 装好 metrics-server,HPA 有 CPU/内存基线
□ HPA 设 min/max + stabilizationWindow
□ 关键服务用自定义指标(QPS/延迟),不只看 CPU
□ KEDA 处理消息积压与缩到 0
□ VPA 与 HPA 分工清晰(不同指标,避免打架)
□ Cluster Autoscaler 配 PDB + --max--nodes 上限
□ 监控 desired vs current replicas,防卡死
□ 每一层都有上下限,防止失控

小结

弹性伸缩是被动响应 HPA、前置事件 KEDA、额度优化 VPA、节点增补 CA 四者的拼图。开始方案要简单(HPA + metrics-server 就够),当"响应式扩不动"再上 KEDA、当"节点不够"再上 CA。记住一句:HPA 与 VPA 分开看指标、KEDA 管事件前置、CA 管机器、每一层都要有上下限与监控——当这个体系协同工作时,你的集群才能在流量潮汐里既扛得住高峰、又不在低谷烧钱。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「云原生」更多文章

  1. Kubernetes 备份容灾与数据保护:Velero、etcd 与恢复演练实战
  2. Kubernetes 渐进式交付:Argo Rollouts、金丝雀/蓝绿与流量治理实战
  3. Kubernetes 集群排障与诊断:从 Pod 症状到节点/集群根因的实战手册