Kubernetes 弹性伸缩体系由四个独立又联动的组件构成:HPA(水平扩缩 Pod 副本)、VPA(垂直调整单个 Pod 的请求资源)、KEDA(事件驱动的退避式伸缩)、Cluster Autoscaler(节点级扩容/缩容)。理解它们各管哪一段、如何配合,是让"按需伸缩"真正落地、而不是"要么固定副本要么只有 HPA"的关键。
目录
- 1. 弹性伸缩全景:四层各管什么
- 2. metrics-server 与指标管线
- 3. HPA:水平扩缩容算法与配置
- 4. 多指标与自定义/外部指标
- 5. VPA:垂直扩容的三个组件
- 6. KEDA:事件驱动的伸缩
- 7. Cluster Autoscaler:节点级扩缩容
- 8. 四者联动的规模化实践与避坑
- 9. 生产最佳实践
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-server | kubectl 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 管机器、每一层都要有上下限与监控——当这个体系协同工作时,你的集群才能在流量潮汐里既扛得住高峰、又不在低谷烧钱。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。