Kubernetes 自动伸缩
Kubernetes 自动伸缩
什么是自动伸缩
在传统的固定容量部署模式中,运维人员需要根据经验预估流量高峰并手动调整资源。这种方式面临两个根本问题:预估不足导致服务降级和过度配置导致资源浪费。Kubernetes 自动伸缩机制通过持续监控负载指标并自动调整资源,解决了这个矛盾。
Kubernetes 的自动伸缩覆盖三个维度:
| 维度 | 组件 | 调整对象 | 响应速度 | 适用场景 |
|---|---|---|---|---|
| 水平伸缩 | HPA | Pod 副本数 | 秒级 | 无状态服务应对流量波动 |
| 垂直伸缩 | VPA | Pod 资源请求 | 分钟级 | 资源配置优化 |
| 节点伸缩 | Cluster Autoscaler | 集群节点数 | 分钟级 | 底层资源供给 |
这三个维度不是互相替代的关系,而是互补的:
- HPA 解决"需要多少个 Pod"的问题
- VPA 解决"每个 Pod 需要多少资源"的问题
- Cluster Autoscaler 解决"集群是否有足够的节点运行这些 Pod"的问题
正确配置这三个维度的自动伸缩,是实现云原生应用弹性伸缩和成本优化的关键。
HPA(水平 Pod 自动伸缩)
HPA 工作原理
HPA 控制器的工作流程如下:
1. Metrics Server 或 Prometheus Adapter 采集指标
↓
2. HPA 控制器获取目标 Deployment 的当前指标值
↓
3. 计算期望副本数 = ceil(当前指标值 / 目标指标值) × 当前副本数
↓
4. 与 minReplicas 和 maxReplicas 比较,确定最终副本数
↓
5. 调整 Deployment 的 replicas 字段基于 CPU/内存的 HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-api-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-api
minReplicas: 3 # 最小副本数(保障最低可用性)
maxReplicas: 20 # 最大副本数(成本上限)
metrics:
# CPU 利用率指标
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # 目标:平均 CPU 利用率 70%
# 内存使用量指标
- type: Resource
resource:
name: memory
target:
type: AverageValue
averageValue: "500Mi" # 目标:平均内存使用 500Mi
# 伸缩行为控制
behavior:
# 缩容行为(防止误缩容)
scaleDown:
stabilizationWindowSeconds: 300 # 5 分钟稳定窗口
policies:
- type: Percent # 按百分比缩容
value: 25 # 每次最多缩容 25%
periodSeconds: 60 # 每 60 秒评估一次
- type: Pods # 按固定数量缩容
value: 2 # 每次最多缩容 2 个 Pod
periodSeconds: 60
selectPolicy: Min # 选择影响最小的策略
# 扩容行为(快速响应流量增长)
scaleUp:
stabilizationWindowSeconds: 60 # 1 分钟稳定窗口
policies:
- type: Percent
value: 100 # 每次最多扩容 100%(翻倍)
periodSeconds: 30
- type: Pods
value: 4 # 每次最多扩容 4 个 Pod
periodSeconds: 30
selectPolicy: Max # 选择影响最大的策略重要前提:Pod 必须配置 resources.requests 才能被 HPA 管理。如果没有设置 requests,HPA 无法计算利用率:
# Deployment 必须设置 resources.requests
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-api
spec:
template:
spec:
containers:
- name: app
image: myapp:v2.0
resources:
requests: # HPA 基于这个值计算利用率
cpu: 500m # 0.5 CPU 核心
memory: 512Mi
limits:
cpu: "1" # 限制最大使用 1 CPU 核心
memory: 1GiHPA 扩容计算示例
当前状态:
- 副本数:4
- 每个副本 CPU request:500m
- 每个 Pod 实际 CPU 使用:400m
- 目标 CPU 利用率:70%
计算过程:
- 当前总 CPU 使用:4 × 400m = 1600m
- 当前总 CPU request:4 × 500m = 2000m
- 当前利用率:1600m / 2000m = 80%
- 期望副本数 = ceil(80% / 70% × 4) = ceil(4.57) = 5
结果:从 4 个副本扩容到 5 个副本基于自定义指标的 HPA
CPU 和内存是通用指标,但很多业务场景需要基于业务指标进行伸缩:
# Prometheus Adapter 配置(将 Prometheus 指标暴露给 HPA)
apiVersion: v1
kind: ConfigMap
metadata:
name: adapter-config
namespace: monitoring
data:
config.yaml: |
rules:
# HTTP QPS 指标
- seriesQuery: 'http_requests_total{namespace!="",pod!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
pod: {resource: "pod"}
name:
matches: "http_requests_total"
as: "http_requests_per_second"
metricsQuery: 'sum(rate(<<.Series>>{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>)'
# 消息队列深度指标
- seriesQuery: 'rabbitmq_queue_messages_ready{namespace!="",queue!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
name:
matches: "rabbitmq_queue_messages_ready"
as: "queue_depth"
metricsQuery: 'rabbitmq_queue_messages_ready{<<.LabelMatchers>>}'
---
# 基于自定义 QPS 指标的 HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-qps-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 2
maxReplicas: 15
metrics:
# 自定义 QPS 指标
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "1000" # 每个 Pod 目标 1000 QPS
# 容器指标(Ingress 请求数)
- type: ContainerResource
containerResource:
name: cpu
container: app
target:
type: Utilization
averageUtilization: 75
---
# 基于消息队列深度的 HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: worker-queue-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: queue-worker
minReplicas: 1
maxReplicas: 10
metrics:
- type: External
external:
metric:
name: queue_depth
selector:
matchLabels:
queue: order-processing
target:
type: AverageValue
averageValue: "100" # 每个目标 100 条消息HPA 监控与运维
# 查看 HPA 状态
kubectl get hpa -n production
# NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
# web-api-hpa Deployment/web-api 80%/70% 3 20 5 30d
# 查看 HPA 详细事件
kubectl describe hpa web-api-hpa -n production
# Events:
# Type Reason Age From Message
# ---- ------ ---- ---- -------
# Normal SuccessfulRescale 60s horizontal-pod-autoscaler New size: 5
# 查看 HPA 使用的指标
kubectl get --raw /apis/metrics.k8s.io/v1beta1/namespaces/production/pods
# 查看自定义指标
kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/namespaces/production
# 查看当前 Pod 的资源使用
kubectl top pods -n production --sort-by=cpu
kubectl top pods -n production --sort-by=memoryVPA(垂直 Pod 自动伸缩)
VPA 工作模式
VPA 有三种工作模式:
| 模式 | 说明 | 影响 |
|---|---|---|
| Auto | 自动更新 Pod 的资源请求 | 会重建 Pod(有短暂中断) |
| Recreate | 自动更新,通过重建 Pod | 与 Auto 类似 |
| Initial | 只在新 Pod 创建时应用建议 | 不影响运行中的 Pod |
| Off | 只提供建议,不执行变更 | 无影响,用于分析 |
# VPA 推荐配置(使用 Initial 模式,避免中断)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: api-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
updatePolicy:
updateMode: Initial # 只在新 Pod 创建时应用建议
resourcePolicy:
containerPolicies:
- containerName: "*"
minAllowed:
cpu: 100m
memory: 128Mi
maxAllowed:
cpu: "4"
memory: 4Gi
controlledResources: ["cpu", "memory"]
controlledValues: RequestsAndLimits # 同时调整 requests 和 limits# 查看 VPA 建议
kubectl describe vpa api-vpa -n production
# Status:
# Recommendation:
# Container Recommendations:
# Container Name: app
# Lower Bound:
# Cpu: 250m
# Memory: 256Mi
# Target: ← 推荐值
# Cpu: 500m
# Memory: 512Mi
# Upper Bound:
# Cpu: 1
# Memory: 1Gi
# Uncapped Target:
# Cpu: 500m
# Memory: 512MiHPA 与 VPA 的冲突
重要警告:HPA 和 VPA 不应同时管理同一资源的 CPU/内存指标。HPA 基于资源利用率调整副本数,VPA 调整资源请求,两者同时使用会导致竞争循环:
1. HPA 检测到 CPU 利用率过高 → 扩容(增加副本)
2. VPA 检测到 CPU 利用率过高 → 增加 CPU requests
3. CPU requests 增加后利用率下降 → HPA 缩容
4. 副本减少后单个 Pod 负载增加 → VPA 再次增加 requests
→ 无限循环推荐做法:
- 方案一:使用 HPA(基于 CPU)+ VPA(Auto 模式管理内存,不管理 CPU)
- 方案二:使用 HPA(基于自定义指标)+ VPA(管理 CPU 和内存)
- 方案三:只用 HPA,通过压测确定合理的 resources.requests
Cluster Autoscaler
Cluster Autoscaler 工作原理
Cluster Autoscaler 监控集群中处于 Pending 状态的 Pod(因为资源不足无法调度),并自动向云平台请求增加节点:
1. Pod 创建 → 调度器尝试调度 → 资源不足 → Pod Pending
↓
2. Cluster Autoscaler 检测到 Pending Pod
↓
3. 评估:是否可以通过调整现有 Pod 位置来解决?
↓
4. 请求云平台 API 创建新节点
↓
5. 新节点加入集群 → Pod 调度成功
↓
6. 节点利用率低时,Cluster Autoscaler 自动移除空闲节点云厂商配置示例
# AWS EKS 启用 Cluster Autoscaler
eksctl create cluster --name my-cluster \
--nodegroup-name standard-workers \
--node-type m6i.large \
--nodes-min 3 \
--nodes-max 20 \
--asg-access \
--auto-kubeconfig
# 查看自动伸缩事件
kubectl get events --field-selector reason=ScaledUpGroup
kubectl get events --field-selector reason=ScaleDown
# 查看 Cluster Autoscaler 日志
kubectl logs -n kube-system deployment/cluster-autoscaler --tail=100# GKE 启用 Cluster Autoscaler
gcloud container clusters update my-cluster \
--enable-autoscaling \
--min-nodes 3 \
--max-nodes 20 \
--autoscaling-profile balanced
# autoscaling-profile 选项:
# - balanced: 平衡扩缩容速度
# - optimize-utilization: 优先提高利用率(更激进的缩容)Cluster Autoscaler 配置参数
# Cluster Autoscaler 常用配置(通过命令行参数)
# --balance-similar-node-groups=true # 平衡同类节点组的大小
# --scale-down-unneeded-time=10m # 节点空闲多久后缩容
# --scale-down-delay-after-add=30m # 新节点添加后多久可以缩容
# --scale-down-delay-after-failure=3m # 缩容失败后多久重试
# --skip-nodes-with-system-pods=true # 跳过运行系统 Pod 的节点
# --max-graceful-termination-sec=600 # 缩容时 Pod 优雅终止超时PodDisruptionBudget 配合
当 Cluster Autoscaler 缩容节点时,需要驱逐该节点上的 Pod。PodDisruptionBudget 确保 Kubernetes 不会驱逐过多 Pod,导致服务不可用:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
namespace: production
spec:
minAvailable: "50%" # 缩容时至少保留 50% 的 Pod 可用
# 或者使用 maxUnavailable
# maxUnavailable: 1 # 最多允许 1 个 Pod 不可用
selector:
matchLabels:
app: api-serverKEDA(事件驱动自动伸缩)
KEDA(Kubernetes Event-Driven Autoscaling)是 HPA 的增强方案,支持基于外部事件源的自动伸缩:
# KEDA 基于 Redis 队列长度伸缩
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: redis-worker-scaler
namespace: production
spec:
scaleTargetRef:
name: redis-worker
minReplicaCount: 0 # 支持缩容到零!
maxReplicaCount: 50
cooldownPeriod: 30 # 缩容冷却时间
triggers:
- type: redis
metadata:
host: redis-service
port: "6379"
listName: task-queue
listLength: "10" # 每个目标处理 10 条消息
---
# KEDA 基于 Cron 表达式伸缩(定时任务场景)
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: cron-scaler
namespace: production
spec:
scaleTargetRef:
name: batch-processor
minReplicaCount: 0
maxReplicaCount: 10
triggers:
- type: cron
metadata:
timezone: Asia/Shanghai
start: "0 8 * * 1-5" # 工作日 8:00 扩容
end: "0 20 * * 1-5" # 工作日 20:00 缩容
desiredReplicas: "5"伸缩策略最佳实践
确定合理的基准配置
# 1. 压测确定服务的资源消耗基线
# 使用 hey 或 wrk 进行压力测试
hey -n 10000 -c 100 http://api-server:8080/health
# 2. 根据压测结果设置 resources.requests
# requests 应设置为 P50 或 P75 的资源使用量
# limits 应设置为 P99 或 P99.9 的资源使用量
# 3. 设置 HPA 阈值
# CPU 利用率阈值建议 70%-80%
# 太低(如 30%)会导致频繁扩缩容
# 太高(如 95%)会导致响应延迟增加
# 4. 验证伸缩行为
# 手动触发扩容
kubectl run load-test --image=busybox --rm -it -- /bin/sh
# 在 load-test 中执行
while true; do wget -q -O- http://api-server:8080/health; done
# 观察 HPA 扩容过程
kubectl get hpa -n production -w监控自动伸缩
# Grafana 监控面板指标
# - kube_hpa_status_current_replicas
# - kube_hpa_status_desired_replicas
# - kube_hpa_status_max_replicas
# - kube_hpa_status_min_replicas
# - kube_hpa_spec_max_replicas
# - kube_hpa_spec_min_replicas
# 告警规则示例
# - HPA 达到 maxReplicas 上限
# - HPA 扩容频率异常(可能表示配置不合理)
# - Cluster Autoscaler 扩容失败(资源配额不足)优点
缺点
总结
Kubernetes 自动伸缩是云原生应用的弹性基础。HPA 适合无状态服务的横向扩展,VPA 适合资源调优,Cluster Autoscaler 负责底层节点供给。三者需要协同配置,并通过压力测试验证伸缩行为是否符合预期。
推荐实施路径:
- 第一阶段:部署 Metrics Server,为所有 Deployment 配置基于 CPU 的 HPA
- 第二阶段:引入自定义指标(Prometheus Adapter),按业务需求调整伸缩策略
- 第三阶段:启用 Cluster Autoscaler,实现节点级别的弹性
- 第四阶段:评估 VPA 的建议,优化资源配置
- 第五阶段:引入 KEDA,实现事件驱动和缩容到零
关键知识点
- HPA 基于 metrics-server 或 Prometheus Adapter 采集的指标进行决策
- autoscaling/v2 API 支持多指标、行为策略和自定义指标,是生产推荐版本
- Pod 必须设置 resources.requests 才能被 HPA 管理
- VPA 的 updateMode: Auto 会重建 Pod,可能造成短暂服务中断
- HPA 与 VPA 不应同时管理同一资源的 CPU 指标
- Cluster Autoscaler 需要 PodDisruptionBudget 配合以保障可用性
项目落地视角
- 每个服务上线前必须经过压测,确定合理的 resources.requests/limits 和 HPA 阈值
- 使用 PodDisruptionBudget 保障自动缩容时服务的最低可用副本数
- 将 HPA 配置纳入 Helm Chart,通过 values.yaml 按环境差异化配置
- 为 Cluster Autoscaler 设置合理的 min/max 节点数,防止无限扩容
常见误区
- 只配置 HPA 不配置 resources.requests,导致 HPA 无法计算利用率
- HPA 阈值设置过低(如 30% CPU),导致频繁扩缩容抖动
- 同时对同一 Deployment 使用 HPA(基于 CPU)和 VPA(Auto 模式),造成资源竞争
- Cluster Autoscaler 的 max 节点数设置过高,导致意外的高额账单
- 忽略冷启动延迟,以为扩容可以瞬间完成
进阶路线
- 学习 KEDA(Kubernetes Event-Driven Autoscaling),基于事件队列长度等外部指标驱动伸缩
- 掌握 Knative Serving 的自动伸缩机制,支持缩容到零
- 了解 Pod 垂直伸缩的无损方案(in-place vertical scaling)
- 学习 Predictive HPA(基于历史数据的预测性伸缩)
适用场景
- 流量有明显波峰波谷的在线服务(电商、社交、API 网关)
- 消息队列消费者需要根据积压量动态调整并发度
- 多租户平台需要根据负载自动调整资源分配
- 批处理任务需要根据队列长度动态扩容
落地建议
- 从 HPA 开始,基于 CPU 指标设置保守阈值,再逐步引入自定义指标
- 为每个 HPA 配置 behavior 策略,控制扩缩容速率防止抖动
- 在 Grafana 中建立 HPA 监控看板,实时跟踪副本数变化和指标趋势
- 建立伸缩演练机制,定期验证自动伸缩行为
排错清单
- HPA 无法获取指标:检查 metrics-server 是否正常运行,kubectl top pods 是否返回数据
- HPA 扩容但 Pod 处于 Pending:检查节点资源是否充足,Cluster Autoscaler 是否启用
- 扩缩容频繁抖动:检查 behavior.scaleDown.stabilizationWindowSeconds 是否设置
- Cluster Autoscaler 不扩容:检查云平台配额、节点组最大节点数、Pod 的 PodDisruptionBudget
- VPA 建议值异常高:检查是否有异常流量,VPA 的 minAllowed/maxAllowed 是否设置
复盘问题
- 最近一次流量高峰时,HPA 的扩容响应是否及时?是否有服务因扩容不及时而降级?
- 当前 HPA 的指标选择是否合理?是否应该引入业务指标(如 QPS、队列深度)?
- VPA 的建议是否经过人工审查后再应用?直接使用 Auto 模式是否有风险?
- Cluster Autoscaler 的节点数上下限是否合理?云账单是否有异常波动?
