一、概述
1.1 背景介绍
线上业务流量存在明显的波峰波谷。白天高峰期Pod数量不够导致请求排队,凌晨低谷期大量Pod空跑浪费资源。手动扩缩容不现实——你不可能每天早上8点上线加Pod、晚上12点再缩回去。
Kubernetes提供了两种自动伸缩机制:
HPA(Horizontal Pod Autoscaler):水平伸缩,根据指标自动调整Pod副本数
VPA(Vertical Pod Autoscaler):垂直伸缩,根据实际使用量自动调整Pod的CPU/内存Request和Limit
两者解决的问题不同。HPA解决"需要多少个Pod",VPA解决"每个Pod需要多少资源"。生产环境中通常HPA用得更多,VPA更多用于辅助资源规划。
1.2 技术特点
HPA:基于Metrics Server或自定义指标(Prometheus Adapter)实现水平扩缩,支持CPU、内存、自定义指标和外部指标四种类型,扩缩算法基于desiredReplicas = ceil(currentReplicas * (currentMetricValue / desiredMetricValue))
VPA:由三个组件构成(Recommender、Updater、Admission Controller),通过分析历史资源使用数据推荐合理的Request值,支持Auto、Recreate、Initial、Off四种更新模式
扩缩容稳定窗口:HPA v2支持配置stabilizationWindowSeconds,避免指标抖动导致频繁扩缩。默认缩容稳定窗口300秒,扩容0秒
1.3 适用场景
HPA适用场景:Web服务、API网关等无状态服务的流量波动应对;消息队列消费者根据队列积压深度自动扩缩;批处理任务根据待处理任务数动态调整Worker数量
VPA适用场景:新上线服务不确定资源需求,用VPA的Off模式获取推荐值后手动调整;Java应用JVM内存使用模式固定,用VPA自动调整避免OOMKill;长期运行的有状态服务不适合水平扩展,通过垂直扩展提升单Pod处理能力
HPA+VPA混合场景:VPA设为Off模式只提供推荐值,HPA负责实际扩缩。两者同时以Auto模式运行在CPU/内存指标上会冲突,生产环境不要这么干
1.4 环境要求
| 组件 | 版本要求 | 说明 |
|---|---|---|
| Kubernetes | 1.23+ | HPA v2 API在1.23 GA,低版本只能用v2beta2 |
| Metrics Server | 0.6.0+ | HPA依赖它获取CPU/内存指标,必装 |
| VPA | 0.14.0+ | 需要单独部署,不包含在K8s默认组件中 |
| Prometheus + Adapter | Prometheus 2.40+, Adapter 0.11+ | 自定义指标扩缩必须,纯CPU/内存扩缩不需要 |
| 集群节点 | 建议至少3个Worker节点 | 节点数太少HPA扩出来的Pod没地方调度 |
二、详细步骤
2.1 准备工作
2.1.1 确认集群状态
# 确认K8s版本,HPA v2需要1.23+ kubectl version --short # 检查是否已安装Metrics Server kubectl get deployment metrics-server -n kube-system # 如果没有安装Metrics Server,检查节点指标是否可用 kubectl top nodes # 如果报错 "Metrics API not available",说明需要安装Metrics Server
2.1.2 安装Metrics Server
Metrics Server是HPA的基础依赖,没有它HPA拿不到CPU和内存指标。
# 下载Metrics Server部署文件 kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.7.1/components.yaml # 如果是自签证书的集群(比如kubeadm搭建的),需要加启动参数跳过证书校验 # 编辑Metrics Server的Deployment kubectl edit deployment metrics-server -n kube-system
在containers.args中添加:
args: ---cert-dir=/tmp ---secure-port=10250 ---kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname ---kubelet-use-node-status-port ---metric-resolution=15s # 自签证书集群必须加这行,否则Metrics Server连不上kubelet ---kubelet-insecure-tls
# 等待Metrics Server就绪 kubectl rollout status deployment metrics-server -n kube-system # 验证指标可用 kubectl top nodes kubectl top pods -A
注意:--kubelet-insecure-tls只在测试环境或自签证书环境使用。正式环境应该配置正确的CA证书。
2.1.3 部署测试应用
创建一个用于测试HPA的Deployment,必须设置resources.requests,否则HPA无法计算CPU利用率百分比。
# test-app.yaml apiVersion:apps/v1 kind:Deployment metadata: name:php-apache namespace:default spec: replicas:1 selector: matchLabels: app:php-apache template: metadata: labels: app:php-apache spec: containers: -name:php-apache image:registry.k8s.io/hpa-example ports: -containerPort:80 resources: requests: cpu:200m memory:128Mi limits: cpu:500m memory:256Mi --- apiVersion:v1 kind:Service metadata: name:php-apache namespace:default spec: selector: app:php-apache ports: -port:80 targetPort:80
kubectl apply -ftest-app.yaml kubectlwait--for=condition=available deployment/php-apache --timeout=60s
2.2 核心配置
2.2.1 HPA基础配置——基于CPU指标
# hpa-cpu.yaml apiVersion:autoscaling/v2 kind:HorizontalPodAutoscaler metadata: name:php-apache-hpa namespace:default spec: scaleTargetRef: apiVersion:apps/v1 kind:Deployment name:php-apache minReplicas:2 maxReplicas:20 metrics: -type:Resource resource: name:cpu target: type:Utilization # 目标CPU利用率50%,基于requests计算 # Pod requests 200m,实际用到100m时就是50% averageUtilization:50 behavior: scaleUp: stabilizationWindowSeconds:0 policies: -type:Percent value:100 periodSeconds:15 -type:Pods value:4 periodSeconds:15 selectPolicy:Max scaleDown: stabilizationWindowSeconds:300 policies: -type:Percent value:10 periodSeconds:60 selectPolicy:Min
kubectl apply -f hpa-cpu.yaml kubectl get hpa php-apache-hpa
参数说明:
minReplicas: 2:最小副本数。生产环境至少设为2,设为1意味着缩容到底只剩一个Pod,挂了就全挂
maxReplicas: 20:最大副本数。根据集群资源容量设置上限,防止失控扩容把节点资源吃光
averageUtilization: 50:目标利用率。设太低(如20%)会导致Pod数量过多浪费资源,设太高(如90%)留不出余量应对突发流量。50%是比较稳妥的起点
scaleUp.stabilizationWindowSeconds: 0:扩容不等待,流量来了立刻扩
scaleDown.stabilizationWindowSeconds: 300:缩容等5分钟,避免流量短暂下降就缩容,结果流量又上来了又要扩
behavior策略解读:
扩容策略用Max:取"当前副本数翻倍"和"加4个Pod"中的较大值,保证突发流量时扩得够快
缩容策略用Min:取"缩10%"中的较小值,每分钟最多缩10%,保证缩容足够平滑
2.2.2 HPA多指标配置——CPU+内存+自定义指标
# hpa-multi-metrics.yaml
apiVersion:autoscaling/v2
kind:HorizontalPodAutoscaler
metadata:
name:web-app-hpa
namespace:production
spec:
scaleTargetRef:
apiVersion:apps/v1
kind:Deployment
name:web-app
minReplicas:3
maxReplicas:50
metrics:
# CPU指标
-type:Resource
resource:
name:cpu
target:
type:Utilization
averageUtilization:60
# 内存指标
-type:Resource
resource:
name:memory
target:
type:Utilization
averageUtilization:70
# 自定义指标:每个Pod的QPS(需要Prometheus Adapter)
-type:Pods
pods:
metric:
name:http_requests_per_second
target:
type:AverageValue
averageValue:"1000"
# 外部指标:消息队列积压数(需要Prometheus Adapter)
-type:External
external:
metric:
name:rabbitmq_queue_messages
selector:
matchLabels:
queue:"task-queue"
target:
type:AverageValue
averageValue:"50"
注意:多指标HPA的计算逻辑是取所有指标计算出的期望副本数的最大值。比如CPU算出需要5个Pod,QPS算出需要8个Pod,最终扩到8个。
2.2.3 安装Prometheus Adapter(自定义指标扩缩必须)
# 使用Helm安装Prometheus Adapter helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update helm install prometheus-adapter prometheus-community/prometheus-adapter --namespace monitoring --setprometheus.url=http://prometheus-server.monitoring.svc --setprometheus.port=9090
Prometheus Adapter配置文件,将Prometheus指标映射为K8s Custom Metrics API:
# prometheus-adapter-config.yaml
apiVersion:v1
kind:ConfigMap
metadata:
name:prometheus-adapter
namespace:monitoring
data:
config.yaml:|
rules:
# 将Prometheus的http_requests_total映射为每秒请求数
- seriesQuery: 'http_requests_total{namespace!="",pod!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
pod: {resource: "pod"}
name:
matches: "^(.*)_total$"
as: "${1}_per_second"
metricsQuery: 'rate(<<.Series>>{<<.LabelMatchers>>}[2m])'
# RabbitMQ队列深度
- seriesQuery: 'rabbitmq_queue_messages{queue!=""}'
resources:
template: "<<.Resource>>"
name:
matches: "^(.*)"
as: "$1"
metricsQuery: '<<.Series>>{<<.LabelMatchers>>}'
# 验证自定义指标API是否可用 kubectl get --raw"/apis/custom.metrics.k8s.io/v1beta1"| jq . kubectl get --raw"/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/http_requests_per_second"| jq .
2.2.4 VPA安装与配置
# 克隆VPA项目 gitclonehttps://github.com/kubernetes/autoscaler.git cdautoscaler/vertical-pod-autoscaler # 安装VPA(会部署Recommender、Updater、Admission Controller三个组件) ./hack/vpa-up.sh # 验证VPA组件运行状态 kubectl get pods -n kube-system | grep vpa # 应该看到三个Pod: # vpa-admission-controller-xxx Running # vpa-recommender-xxx Running # vpa-updater-xxx Running
VPA配置示例:
# vpa-auto.yaml - 自动模式,VPA会自动调整Pod资源 apiVersion:autoscaling.k8s.io/v1 kind:VerticalPodAutoscaler metadata: name:php-apache-vpa namespace:default spec: targetRef: apiVersion:apps/v1 kind:Deployment name:php-apache updatePolicy: # Auto: 自动重建Pod并应用推荐值 # Recreate: 同Auto,只在Pod重建时应用 # Initial: 只在Pod首次创建时应用 # Off: 只推荐不执行,最安全 updateMode:"Off" resourcePolicy: containerPolicies: -containerName:php-apache minAllowed: cpu:100m memory:64Mi maxAllowed: cpu:2 memory:2Gi controlledResources:["cpu","memory"] controlledValues:RequestsOnly
kubectl apply -f vpa-auto.yaml # 等待几分钟后查看VPA推荐值 kubectl get vpa php-apache-vpa -o yaml
生产环境建议:VPA的updateMode先用Off,观察推荐值是否合理,确认没问题后再切到Auto。直接上Auto模式,VPA会重建Pod来应用新的资源配置,业务高峰期Pod被重建可能导致短暂不可用。
2.2.5 HPA与VPA混合使用
HPA和VPA不能同时基于CPU/内存指标工作,会互相打架。正确的混合方式:
# 方案一:VPA用Off模式,只看推荐值,HPA正常工作 # vpa-off-mode.yaml apiVersion:autoscaling.k8s.io/v1 kind:VerticalPodAutoscaler metadata: name:web-app-vpa namespace:production spec: targetRef: apiVersion:apps/v1 kind:Deployment name:web-app updatePolicy: updateMode:"Off" --- # HPA正常配置 apiVersion:autoscaling/v2 kind:HorizontalPodAutoscaler metadata: name:web-app-hpa namespace:production spec: scaleTargetRef: apiVersion:apps/v1 kind:Deployment name:web-app minReplicas:3 maxReplicas:30 metrics: -type:Resource resource: name:cpu target: type:Utilization averageUtilization:60
# 方案二:VPA管内存,HPA基于自定义指标(不用CPU/内存) # 这种方式VPA可以用Auto模式 apiVersion:autoscaling.k8s.io/v1 kind:VerticalPodAutoscaler metadata: name:web-app-vpa namespace:production spec: targetRef: apiVersion:apps/v1 kind:Deployment name:web-app updatePolicy: updateMode:"Auto" resourcePolicy: containerPolicies: -containerName:web-app controlledResources:["memory"] controlledValues:RequestsOnly --- apiVersion:autoscaling/v2 kind:HorizontalPodAutoscaler metadata: name:web-app-hpa namespace:production spec: scaleTargetRef: apiVersion:apps/v1 kind:Deployment name:web-app minReplicas:3 maxReplicas:30 metrics: # 只用自定义指标,不用CPU/内存,避免和VPA冲突 -type:Pods pods: metric: name:http_requests_per_second target: type:AverageValue averageValue:"800"
2.3 启动和验证
2.3.1 HPA扩容压测验证
# 开一个终端持续观察HPA状态 kubectl get hpa php-apache-hpa -w # 另开一个终端,用busybox发压力 kubectl run -i --tty load-generator --rm --image=busybox:1.36 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done" # 观察HPA输出,大约1-2分钟后会看到: # NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE # php-apache-hpa Deployment/php-apache 248%/50% 2 20 2 5m # php-apache-hpa Deployment/php-apache 248%/50% 2 20 10 6m # 停止压力后,等待5分钟(缩容稳定窗口),Pod数量会逐步缩回minReplicas
2.3.2 VPA推荐值验证
# 查看VPA推荐值
kubectl get vpa php-apache-vpa -o jsonpath='{.status.recommendation}'| jq .
# 输出示例:
# {
# "containerRecommendations": [
# {
# "containerName": "php-apache",
# "lowerBound": { "cpu": "100m", "memory": "64Mi" },
# "target": { "cpu": "250m", "memory": "180Mi" },
# "uncappedTarget": { "cpu": "250m", "memory": "180Mi" },
# "upperBound": { "cpu": "500m", "memory": "300Mi" }
# }
# ]
# }
# target就是VPA推荐的资源配置值
2.3.3 验证扩缩容事件
# 查看HPA相关事件 kubectl describe hpa php-apache-hpa # 关注Events部分: # Events: # Type Reason Age From Message # ---- ------ ---- ---- ------- # Normal SuccessfulRescale 2m horizontal-pod-autoscaler New size: 10; reason: cpu resource utilization above target # 查看Pod扩缩容历史 kubectl get events --field-selector reason=SuccessfulRescale --sort-by='.lastTimestamp'
三、示例代码和配置
3.1 完整配置示例
3.1.1 生产级HPA完整配置
# 文件路径:/opt/k8s/hpa/production-hpa.yaml # 适用于生产环境的电商API服务HPA配置 apiVersion:autoscaling/v2 kind:HorizontalPodAutoscaler metadata: name:ecommerce-api-hpa namespace:production labels: app:ecommerce-api managed-by:hpa annotations: # 记录配置变更原因,方便审计 kubernetes.io/change-cause:"初始HPA配置,目标CPU 60%,QPS 1200/pod" spec: scaleTargetRef: apiVersion:apps/v1 kind:Deployment name:ecommerce-api minReplicas:5 maxReplicas:100 metrics: # 主指标:CPU利用率 -type:Resource resource: name:cpu target: type:Utilization averageUtilization:60 # 辅助指标:内存利用率(防止内存泄漏场景) -type:Resource resource: name:memory target: type:Utilization averageUtilization:75 # 业务指标:每Pod每秒请求数 -type:Pods pods: metric: name:http_requests_per_second target: type:AverageValue averageValue:"1200" # 业务指标:P99延迟(毫秒) -type:Pods pods: metric: name:http_request_duration_p99_milliseconds target: type:AverageValue averageValue:"200" behavior: scaleUp: stabilizationWindowSeconds:0 policies: # 快速扩容:每15秒最多扩当前数量的100% -type:Percent value:100 periodSeconds:15 # 兜底:每15秒至少能扩5个Pod -type:Pods value:5 periodSeconds:15 selectPolicy:Max scaleDown: # 缩容稳定窗口10分钟,比默认5分钟更保守 stabilizationWindowSeconds:600 policies: # 慢速缩容:每60秒最多缩5% -type:Percent value:5 periodSeconds:60 selectPolicy:Min
3.1.2 VPA完整配置(Off模式+资源推荐脚本)
# 文件路径:/opt/k8s/vpa/production-vpa.yaml apiVersion:autoscaling.k8s.io/v1 kind:VerticalPodAutoscaler metadata: name:ecommerce-api-vpa namespace:production spec: targetRef: apiVersion:apps/v1 kind:Deployment name:ecommerce-api updatePolicy: updateMode:"Off" resourcePolicy: containerPolicies: -containerName:ecommerce-api minAllowed: cpu:200m memory:256Mi maxAllowed: cpu:4 memory:8Gi controlledResources:["cpu","memory"] controlledValues:RequestsAndLimits # sidecar容器单独设置策略 -containerName:istio-proxy mode:"Off" -containerName:filebeat minAllowed: cpu:50m memory:64Mi maxAllowed: cpu:500m memory:512Mi
3.1.3 Prometheus Adapter完整配置
# 文件路径:/opt/k8s/prometheus-adapter/values.yaml
# Helm values文件
prometheus:
url:http://prometheus-server.monitoring.svc
port:9090
replicas:2
resources:
requests:
cpu:100m
memory:128Mi
limits:
cpu:500m
memory:512Mi
rules:
default:false
custom:
# HTTP请求速率
-seriesQuery:'http_requests_total{namespace!="",pod!=""}'
resources:
overrides:
namespace:{resource:"namespace"}
pod:{resource:"pod"}
name:
matches:"^(.*)_total$"
as:"${1}_per_second"
metricsQuery:'sum(rate(<<.Series>>{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>)'
# HTTP P99延迟
-seriesQuery:'http_request_duration_seconds_bucket{namespace!="",pod!=""}'
resources:
overrides:
namespace:{resource:"namespace"}
pod:{resource:"pod"}
name:
as:"http_request_duration_p99_milliseconds"
metricsQuery:'histogram_quantile(0.99, sum(rate(<<.Series>>{<<.LabelMatchers>>}[2m])) by (le, <<.GroupBy>>)) * 1000'
# gRPC请求速率
-seriesQuery:'grpc_server_handled_total{namespace!="",pod!=""}'
resources:
overrides:
namespace:{resource:"namespace"}
pod:{resource:"pod"}
name:
matches:"^(.*)_total$"
as:"${1}_per_second"
metricsQuery:'sum(rate(<<.Series>>{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>)'
# 消息队列积压
-seriesQuery:'rabbitmq_queue_messages{namespace!="",queue!=""}'
resources:
template:"<<.Resource>>"
name:
matches:"^(.*)"
as:"$1"
metricsQuery:'<<.Series>>{<<.LabelMatchers>>}'
resource:
cpu:
containerQuery:'sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>}[3m])) by (<<.GroupBy>>)'
nodeQuery:'sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>}[3m])) by (<<.GroupBy>>)'
resources:
overrides:
namespace:{resource:"namespace"}
node:{resource:"node"}
pod:{resource:"pod"}
containerLabel:container
memory:
containerQuery:'sum(container_memory_working_set_bytes{<<.LabelMatchers>>}) by (<<.GroupBy>>)'
nodeQuery:'sum(container_memory_working_set_bytes{<<.LabelMatchers>>}) by (<<.GroupBy>>)'
resources:
overrides:
namespace:{resource:"namespace"}
node:{resource:"node"}
pod:{resource:"pod"}
containerLabel:container
3.2 辅助脚本
3.2.1 VPA推荐值采集脚本
#!/bin/bash
# 文件名:vpa-report.sh
# 功能:采集所有VPA推荐值并生成CSV报告,用于资源规划
# 用法:./vpa-report.sh [namespace]
NAMESPACE=${1:-"--all-namespaces"}
OUTPUT_FILE="/tmp/vpa-report-$(date +%Y%m%d-%H%M%S).csv"
if["$NAMESPACE"="--all-namespaces"];then
NS_FLAG="-A"
else
NS_FLAG="-n$NAMESPACE"
fi
echo"Namespace,VPA Name,Container,Target CPU,Target Memory,Lower CPU,Lower Memory,Upper CPU,Upper Memory">"$OUTPUT_FILE"
kubectl get vpa$NS_FLAG-o json | jq -r'
.items[] |
.metadata.namespace as $ns |
.metadata.name as $vpa |
(.status.recommendation.containerRecommendations // [])[] |
[$ns, $vpa, .containerName,
(.target.cpu // "N/A"), (.target.memory // "N/A"),
(.lowerBound.cpu // "N/A"), (.lowerBound.memory // "N/A"),
(.upperBound.cpu // "N/A"), (.upperBound.memory // "N/A")]
| @csv
'>>"$OUTPUT_FILE"
echo"VPA报告已生成:$OUTPUT_FILE"
echo"---"
column -t -s','"$OUTPUT_FILE"
3.2.2 HPA状态巡检脚本
#!/bin/bash
# 文件名:hpa-check.sh
# 功能:检查所有HPA的健康状态,发现异常HPA
# 用法:./hpa-check.sh
echo"========== HPA健康检查 =========="
echo"检查时间:$(date '+%Y-%m-%d %H:%M:%S')"
echo""
# 检查指标获取失败的HPA
echo"--- 指标获取失败的HPA ---"
kubectl get hpa -A -o json | jq -r'
.items[] |
select(.status.conditions[]? | select(.type=="ScalingActive" and .status=="False")) |
[.metadata.namespace, .metadata.name,
(.status.conditions[] | select(.type=="ScalingActive") | .message)]
| @tsv
'|whileIFS=$' 'read-r ns name msg;do
echo"[异常]$ns/$name:$msg"
done
echo""
# 检查已达到最大副本数的HPA(可能需要调大maxReplicas)
echo"--- 已达最大副本数的HPA ---"
kubectl get hpa -A -o json | jq -r'
.items[] |
select(.status.currentReplicas >= .spec.maxReplicas) |
[.metadata.namespace, .metadata.name,
(.status.currentReplicas | tostring), (.spec.maxReplicas | tostring)]
| @tsv
'|whileIFS=$' 'read-r ns name current max;do
echo"[警告]$ns/$name: 当前副本数$current已达上限$max,考虑调大maxReplicas"
done
echo""
# 检查目标利用率远低于阈值的HPA(可能资源浪费)
echo"--- 资源可能浪费的HPA(CPU利用率<20%且副本数>minReplicas)---"
kubectl get hpa -A -o json | jq -r'
.items[] |
select(.status.currentReplicas > .spec.minReplicas) |
select(.status.currentMetrics[]? |
select(.type=="Resource" and .resource.name=="cpu" and
.resource.current.averageUtilization < 20)) |
[.metadata.namespace, .metadata.name,
(.status.currentMetrics[] | select(.type=="Resource" and .resource.name=="cpu") |
.resource.current.averageUtilization | tostring),
(.status.currentReplicas | tostring)]
| @tsv
' | while IFS=$' 'read -r ns name cpu replicas; do
echo"[提示] $ns/$name: CPU利用率仅 ${cpu}%,当前 $replicas 副本,可能存在资源浪费"
done
echo""
echo"========== 检查完成 =========="
3.3 实际应用案例
案例一:电商大促场景——基于QPS的预测性扩容
场景描述:电商平台双11大促,预计流量是平时的10倍。平时API服务5个Pod,每个Pod处理1200 QPS,总计6000 QPS。大促预计峰值60000 QPS。
实现方案:
大促前1小时,手动将minReplicas调高到预估值,避免HPA扩容速度跟不上瞬时流量
# 大促前:提前扩容到预估值
kubectl patch hpa ecommerce-api-hpa -n production
--typemerge -p'{"spec":{"minReplicas":50}}'
# 确认Pod已扩到50
kubectl get deployment ecommerce-api -n production -w
大促期间HPA继续工作,如果实际流量超过预估,HPA会继续扩容到maxReplicas
# 实时监控HPA状态 watch -n 5'kubectl get hpa ecommerce-api-hpa -n production'
大促结束后,逐步恢复minReplicas,不要一次性缩回去
# 大促结束2小时后,先缩到20
kubectl patch hpa ecommerce-api-hpa -n production
--typemerge -p'{"spec":{"minReplicas":20}}'
# 观察1小时确认流量稳定后,缩回5
kubectl patch hpa ecommerce-api-hpa -n production
--typemerge -p'{"spec":{"minReplicas":5}}'
踩坑经验:大促当天直接靠HPA自动扩容是不够的。HPA从检测到指标超标到Pod Ready,整个链路需要:指标采集间隔(15s)+ HPA计算间隔(15s)+ Pod调度(几秒)+ 镜像拉取(看镜像大小,可能30s-2min)+ 应用启动(Java应用可能30s-1min)。加起来可能要2-3分钟,大促开始瞬间的流量洪峰扛不住。所以必须提前手动扩容。
案例二:消息队列消费者——基于队列积压深度自动扩缩
场景描述:订单处理服务从RabbitMQ消费消息,正常情况下队列深度保持在100以内。当上游突发大量订单时,队列积压到几万条,需要自动扩消费者加速处理。
实现代码:
# order-consumer-hpa.yaml
apiVersion:autoscaling/v2
kind:HorizontalPodAutoscaler
metadata:
name:order-consumer-hpa
namespace:production
spec:
scaleTargetRef:
apiVersion:apps/v1
kind:Deployment
name:order-consumer
minReplicas:2
maxReplicas:30
metrics:
# 基于队列积压深度:每个Pod分摊50条消息
-type:External
external:
metric:
name:rabbitmq_queue_messages
selector:
matchLabels:
queue:"order-processing"
target:
type:AverageValue
averageValue:"50"
behavior:
scaleUp:
stabilizationWindowSeconds:0
policies:
-type:Pods
value:5
periodSeconds:30
selectPolicy:Max
scaleDown:
# 队列消费完后等10分钟再缩,防止上游又来一波
stabilizationWindowSeconds:600
policies:
-type:Pods
value:2
periodSeconds:60
selectPolicy:Min
运行效果:
# 正常状态:队列深度80,2个消费者,每个分摊40 < 50,不扩容 # 积压状态:队列深度5000,2个消费者,每个分摊2500 > 50 # HPA计算:ceil(2 * 2500/50) = 100,但maxReplicas=30,所以扩到30 # 30个消费者处理,队列深度快速下降 # 队列清空后,等10分钟稳定窗口,开始缩容,每分钟缩2个
注意:消费者扩容时要确认下游数据库能扛住。30个消费者同时写库,数据库连接数和写入压力会暴增。建议消费者内部做限流或者用批量写入。
案例三:Java应用VPA资源优化
场景描述:一个Spring Boot微服务,开发给的资源配置是requests: cpu 1, memory 2Gi,limits: cpu 2, memory 4Gi。实际运行后发现CPU平均使用率只有15%,内存稳定在800Mi左右。用VPA分析真实资源需求。
实现步骤:
部署VPA(Off模式)
apiVersion:autoscaling.k8s.io/v1 kind:VerticalPodAutoscaler metadata: name:user-service-vpa namespace:production spec: targetRef: apiVersion:apps/v1 kind:Deployment name:user-service updatePolicy: updateMode:"Off" resourcePolicy: containerPolicies: -containerName:user-service minAllowed: cpu:100m memory:256Mi maxAllowed: cpu:4 memory:8Gi
运行一周后查看推荐值
kubectl get vpa user-service-vpa -n production -o json | jq'.status.recommendation'
# 输出:
# {
# "containerRecommendations": [{
# "containerName": "user-service",
# "target": { "cpu": "350m", "memory": "900Mi" },
# "lowerBound": { "cpu": "200m", "memory": "700Mi" },
# "upperBound": { "cpu": "800m", "memory": "1200Mi" }
# }]
# }
根据推荐值调整Deployment资源配置
# requests用target值,limits用upperBound的1.5倍留余量
kubectl patch deployment user-service -n production --type=json -p='[
{"op":"replace","path":"/spec/template/spec/containers/0/resources","value":{
"requests":{"cpu":"350m","memory":"900Mi"},
"limits":{"cpu":"1200m","memory":"1800Mi"}
}}
]'
优化效果:
CPU requests从1000m降到350m,节省65%
内存requests从2Gi降到900Mi,节省56%
一个20副本的服务,每年节省的云资源费用约¥15000(按阿里云ECS计算)
四、最佳实践和注意事项
4.1 最佳实践
4.1.1 性能优化
合理设置HPA指标采集间隔:默认HPA每15秒计算一次,Metrics Server每15秒采集一次。如果业务流量变化极快(秒杀场景),可以调小Metrics Server的--metric-resolution到10s,但不要低于10s,否则kubelet压力太大。
# 修改Metrics Server采集间隔 kubectl edit deployment metrics-server -n kube-system # 将 --metric-resolution=15s 改为 --metric-resolution=10s
Pod启动速度优化:HPA扩出来的Pod越快Ready越好。几个关键优化点:
# 镜像预拉取DaemonSet apiVersion:apps/v1 kind:DaemonSet metadata: name:image-prepull namespace:kube-system spec: selector: matchLabels: app:image-prepull template: metadata: labels: app:image-prepull spec: initContainers: -name:prepull image:your-registry/ecommerce-api:latest command:["sh","-c","echo Image pulled"] containers: -name:pause image:registry.k8s.io/pause:3.9
使用小镜像:Alpine基础镜像比Ubuntu小10倍,拉取时间从30s降到3s
预拉取镜像:用DaemonSet在每个节点预拉取业务镜像
配置合理的readinessProbe:initialDelaySeconds不要设太大,Java应用建议10-15s,Go应用3-5s
开启Pod Topology Spread:避免新Pod全调度到同一个节点
缩容策略要保守:生产环境缩容稳定窗口建议设为600秒(10分钟),缩容速率每分钟不超过10%。见过太多次流量短暂下降触发缩容,结果2分钟后流量又上来了,Pod又要重新扩,来回折腾影响服务质量。
4.1.2 安全加固
设置合理的maxReplicas上限:maxReplicas一定要根据集群实际资源容量设置。一个3节点集群每节点32核128G,总共96核384G,如果每个Pod requests 1核2G,maxReplicas最多设到80左右(留一些给系统组件)。设成1000,HPA真扩到那个数,节点资源耗尽,所有Pod都会被驱逐。
# 检查集群可分配资源 kubectl describe nodes | grep -A 5"Allocated resources"
RBAC权限控制:限制谁能修改HPA配置。生产环境HPA的minReplicas和maxReplicas修改应该走变更审批流程,不能随便改。
# 限制HPA修改权限的Role apiVersion:rbac.authorization.k8s.io/v1 kind:Role metadata: name:hpa-viewer namespace:production rules: -apiGroups:["autoscaling"] resources:["horizontalpodautoscalers"] verbs:["get","list","watch"] # 只有SRE团队有修改权限 --- apiVersion:rbac.authorization.k8s.io/v1 kind:Role metadata: name:hpa-admin namespace:production rules: -apiGroups:["autoscaling"] resources:["horizontalpodautoscalers"] verbs:["get","list","watch","create","update","patch","delete"]
PodDisruptionBudget配合HPA:HPA缩容时会删Pod,配合PDB确保缩容过程中始终有足够的Pod在运行。
apiVersion:policy/v1 kind:PodDisruptionBudget metadata: name:ecommerce-api-pdb namespace:production spec: minAvailable:"60%" selector: matchLabels: app:ecommerce-api
4.1.3 高可用配置
HPA本身的高可用:HPA Controller运行在kube-controller-manager中,跟随控制平面高可用。多Master集群中只有Leader节点的controller-manager在工作,其他节点待命。不需要额外配置。
Metrics Server高可用:生产环境Metrics Server建议部署2个副本,配合PDB。
kubectl scale deployment metrics-server -n kube-system --replicas=2
备份策略:HPA和VPA配置都是K8s资源对象,跟随etcd备份。建议同时在Git仓库中维护一份YAML,用GitOps方式管理。
4.2 注意事项
4.2.1 配置注意事项
警告:以下几个配置错误会导致严重生产事故,改之前务必确认。
Deployment必须设置resources.requests:没有requests,HPA无法计算CPU/内存利用率百分比,会报FailedGetResourceMetric错误。这是最常见的HPA不工作原因。
HPA和VPA不要同时基于CPU/内存指标运行Auto模式:HPA说"CPU高了加Pod",VPA说"CPU高了加资源",两个控制器互相打架,副本数和资源配置会反复震荡。
minReplicas不要设为1:缩容到1个Pod,这个Pod挂了或者所在节点故障,服务直接中断。生产环境最少设为2,核心服务设为3。
4.2.2 常见错误
| 错误现象 | 原因分析 | 解决方案 |
|---|---|---|
|
HPA显示 |
Metrics Server未安装或Pod没设requests | 安装Metrics Server,给Pod加resources.requests |
| HPA TARGETS显示正常但不扩容 | 当前指标值未超过目标值,或tolerance范围内(默认10%) | 确认指标值确实超过目标值的110% |
| HPA扩容后Pod一直Pending | 集群资源不足,新Pod无法调度 | 扩容集群节点或配置Cluster Autoscaler |
| VPA推荐值一直为空 | VPA Recommender未运行或数据采集时间不够 | 检查vpa-recommender Pod日志,等待至少8小时 |
| VPA Auto模式Pod频繁重启 | VPA每次调整资源都要重建Pod | 设置PDB限制同时重建的Pod数量,或改用Off模式 |
| 自定义指标HPA报no matches for kind "ExternalMetric" | Prometheus Adapter配置错误或未注册API | 检查kubectl get apiservices中custom.metrics相关API状态 |
4.2.3 兼容性问题
版本兼容:HPA v2 API在K8s 1.23 GA。1.18-1.22用autoscaling/v2beta2,1.18以下用autoscaling/v2beta1。升级集群后记得把HPA的apiVersion也改过来。
平台兼容:各云厂商的托管K8s(EKS、AKS、GKE、ACK)都内置了Metrics Server,不需要手动安装。但自定义指标扩缩需要自己部署Prometheus Adapter。
组件依赖:VPA和Cluster Autoscaler(CA)可以配合使用。VPA调大Pod资源后如果节点放不下,CA会自动扩节点。但要注意CA的扩节点速度(通常2-5分钟),这段时间Pod会处于Pending状态。
五、故障排查和监控
5.1 故障排查
5.1.1 日志查看
# 查看HPA Controller日志(在kube-controller-manager中) kubectl logs -n kube-system $(kubectl get pods -n kube-system -l component=kube-controller-manager -o name | head -1) | grep -i"horizontal" # 查看Metrics Server日志 kubectl logs -n kube-system -l k8s-app=metrics-server -f # 查看VPA Recommender日志 kubectl logs -n kube-system -l app=vpa-recommender -f # 查看VPA Updater日志 kubectl logs -n kube-system -l app=vpa-updater -f # 查看Prometheus Adapter日志 kubectl logs -n monitoring -l app=prometheus-adapter -f
5.1.2 常见问题排查
问题一:HPA TARGETS显示
# 第一步:确认Metrics Server是否正常
kubectl get apiservices | grep metrics
# 应该看到:v1beta1.metrics.k8s.io kube-system/metrics-server True
# 第二步:确认能获取到Pod指标
kubectl top pods -n default
# 如果报错,查看Metrics Server日志
kubectl logs -n kube-system -l k8s-app=metrics-server --tail=50
# 第三步:确认Pod有设置resources.requests
kubectl get deployment php-apache -o jsonpath='{.spec.template.spec.containers[0].resources}'
解决方案:
Metrics Server未安装 → 按2.1.2节安装
Metrics Server连不上kubelet → 加--kubelet-insecure-tls参数
Pod没设requests → 给Deployment加resources.requests
问题二:HPA扩容速度太慢,流量已经上来了Pod还没扩够
# 检查HPA当前状态和事件
kubectl describe hpa ecommerce-api-hpa -n production
# 检查扩容策略配置
kubectl get hpa ecommerce-api-hpa -n production -o jsonpath='{.spec.behavior.scaleUp}'| jq .
解决方案:
调整扩容策略,增大每次扩容的比例:
behavior: scaleUp: stabilizationWindowSeconds:0 policies: -type:Percent value:200# 每次最多扩到当前的3倍 periodSeconds:15 -type:Pods value:10 # 每次至少扩10个 periodSeconds:15 selectPolicy:Max
对于可预测的流量高峰,提前手动调高minReplicas
问题三:VPA Auto模式导致服务抖动
症状:VPA频繁重建Pod,服务出现间歇性不可用
排查:
# 查看VPA更新历史 kubectl get events -n production --field-selector reason=EvictedByVPA --sort-by='.lastTimestamp' # 查看VPA推荐值变化 kubectl get vpa -n production -o json | jq'.items[].status.recommendation'
解决:
将updateMode改为Off,手动应用推荐值
如果必须用Auto,配置PDB限制同时驱逐的Pod数量
调大VPA的minAllowed和maxAllowed范围,减少推荐值波动
5.1.3 调试模式
# 提高kube-controller-manager的HPA日志级别 # 编辑kube-controller-manager的启动参数,加上: # --v=4 (会输出HPA的详细计算过程) # 查看HPA详细计算过程 kubectl logs -n kube-system kube-controller-manager-master01 | grep -A 10"autoscaler" # 手动触发Metrics API查看原始指标数据 kubectl get --raw"/apis/metrics.k8s.io/v1beta1/namespaces/default/pods"| jq . # 查看自定义指标原始数据 kubectl get --raw"/apis/custom.metrics.k8s.io/v1beta1/namespaces/production/pods/*/http_requests_per_second"| jq .
5.2 性能监控
5.2.1 关键指标监控
# 查看所有HPA状态概览 kubectl get hpa -A -o wide # 查看特定HPA的详细指标 kubectl get hpa ecommerce-api-hpa -n production -o yaml | grep -A 20"currentMetrics" # 监控Pod资源使用率 kubectl top pods -n production --sort-by=cpu kubectl top pods -n production --sort-by=memory # 监控节点资源使用率(判断是否需要扩节点) kubectl top nodes
5.2.2 监控指标说明
| 指标名称 | 正常范围 | 告警阈值 | 说明 |
|---|---|---|---|
| HPA当前副本数/最大副本数 | < 80% | >= 90% | 接近上限说明可能需要调大maxReplicas |
| HPA目标指标达成率 | 90%-110% | > 150% 持续5分钟 | 指标持续超标说明扩容速度跟不上 |
| Pod CPU利用率 | 40%-70% | > 85% 持续3分钟 | 利用率过高说明HPA目标值设太高 |
| Pod内存利用率 | 50%-80% | > 90% | 接近limits会被OOMKill |
| Metrics Server延迟 | < 1s | > 5s | 延迟过高会导致HPA决策滞后 |
| VPA推荐值与实际值偏差 | < 20% | > 50% | 偏差过大说明资源配置不合理 |
5.2.3 监控告警配置
# Prometheus告警规则:hpa-alerts.yaml
apiVersion:monitoring.coreos.com/v1
kind:PrometheusRule
metadata:
name:hpa-alerts
namespace:monitoring
spec:
groups:
-name:hpa.rules
rules:
# HPA已达最大副本数
-alert:HPAMaxedOut
expr:|
kube_horizontalpodautoscaler_status_current_replicas
==
kube_horizontalpodautoscaler_spec_max_replicas
for:10m
labels:
severity:warning
annotations:
summary:"HPA{{ $labels.namespace }}/{{ $labels.horizontalpodautoscaler }}已达最大副本数"
description:"当前副本数已达maxReplicas上限,持续10分钟。考虑调大maxReplicas或优化服务性能。"
# HPA无法获取指标
-alert:HPAScalingInactive
expr:|
kube_horizontalpodautoscaler_status_condition{condition="ScalingActive",status="false"} == 1
for:5m
labels:
severity:critical
annotations:
summary:"HPA{{ $labels.namespace }}/{{ $labels.horizontalpodautoscaler }}无法获取指标"
description:"HPA ScalingActive条件为False,自动伸缩功能失效。检查Metrics Server或Prometheus Adapter。"
# HPA副本数为0(服务完全不可用)
-alert:HPAReplicasZero
expr:|
kube_horizontalpodautoscaler_status_current_replicas == 0
for:1m
labels:
severity:critical
annotations:
summary:"HPA{{ $labels.namespace }}/{{ $labels.horizontalpodautoscaler }}副本数为0"
description:"服务副本数为0,服务完全不可用。立即检查。"
# CPU利用率持续过高
-alert:HighCPUUtilization
expr:|
avg(rate(container_cpu_usage_seconds_total{container!="",pod!=""}[5m])) by (namespace, pod)
/
avg(kube_pod_container_resource_requests{resource="cpu"}) by (namespace, pod)
> 0.9
for:5m
labels:
severity:warning
annotations:
summary:"Pod{{ $labels.namespace }}/{{ $labels.pod }}CPU利用率超过90%"
# Metrics Server不可用
-alert:MetricsServerDown
expr:|
up{job="metrics-server"} == 0
for:2m
labels:
severity:critical
annotations:
summary:"Metrics Server不可用,所有HPA将无法工作"
5.3 备份与恢复
5.3.1 备份策略
#!/bin/bash
# 文件名:backup-hpa-vpa.sh
# 功能:备份所有HPA和VPA配置到文件
BACKUP_DIR="/opt/k8s/backup/autoscaling/$(date +%Y%m%d)"
mkdir -p"$BACKUP_DIR"
# 备份所有HPA配置
echo"备份HPA配置..."
fornsin$(kubectl get hpa -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"
"}{end}'| sort -u);do
mkdir -p"$BACKUP_DIR/hpa/$ns"
kubectl get hpa -n"$ns"-o yaml >"$BACKUP_DIR/hpa/$ns/all-hpa.yaml"
done
# 备份所有VPA配置
echo"备份VPA配置..."
fornsin$(kubectl get vpa -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"
"}{end}'| sort -u);do
mkdir -p"$BACKUP_DIR/vpa/$ns"
kubectl get vpa -n"$ns"-o yaml >"$BACKUP_DIR/vpa/$ns/all-vpa.yaml"
done
# 备份Prometheus Adapter配置
echo"备份Prometheus Adapter配置..."
kubectl get configmap prometheus-adapter -n monitoring -o yaml >"$BACKUP_DIR/prometheus-adapter-config.yaml"2>/dev/null
echo"备份完成:$BACKUP_DIR"
ls -lR"$BACKUP_DIR"
5.3.2 恢复流程
确认当前状态:kubectl get hpa,vpa -A
恢复HPA配置:kubectl apply -f /opt/k8s/backup/autoscaling/20260208/hpa/
恢复VPA配置:kubectl apply -f /opt/k8s/backup/autoscaling/20260208/vpa/
验证恢复结果:kubectl get hpa,vpa -A确认所有资源已恢复
六、总结
6.1 技术要点回顾
HPA核心公式:desiredReplicas = ceil(currentReplicas * currentMetricValue / desiredMetricValue),理解这个公式就理解了HPA的所有行为
VPA生产用法:Off模式获取推荐值 → 人工审核 → 手动调整资源配置,这是最稳妥的路径
behavior策略:扩容用Max策略快速响应,缩容用Min策略平滑收缩,stabilizationWindowSeconds是防抖的关键参数
HPA+VPA混合:VPA设Off模式做资源顾问,HPA负责实际扩缩,两者不要同时基于CPU/内存做Auto
6.2 进阶学习方向
KEDA(Kubernetes Event-Driven Autoscaling):比原生HPA更灵活的事件驱动扩缩方案,支持从0缩放,内置50+触发器(Kafka、RabbitMQ、Redis、Cron等)
项目地址:https://keda.sh
实践建议:如果你的HPA主要基于消息队列或外部指标扩缩,KEDA比Prometheus Adapter方案简单很多
Cluster Autoscaler与HPA联动:HPA扩Pod,CA扩节点,两者配合实现完整的弹性伸缩链路
文档:https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler
实践建议:配置CA的扩容冷却时间和缩容延迟,避免节点频繁增减
Multidimensional Pod Autoscaler(MPA):Google内部使用的方案,同时做水平和垂直伸缩,社区有类似的开源实现
实践建议:关注K8s社区SIG-Autoscaling的进展
6.3 参考资料
Kubernetes HPA官方文档- HPA配置和算法详解
VPA GitHub仓库- VPA安装和配置指南
Prometheus Adapter文档- 自定义指标配置
KEDA官方文档- 事件驱动自动伸缩
附录
A. 命令速查表
# HPA操作 kubectl get hpa -A # 查看所有HPA kubectl get hpa-o wide # 查看HPA详情 kubectl describe hpa # 查看HPA事件和状态 kubectl autoscale deployment --min=2 --max=10 --cpu-percent=50 # 快速创建HPA kubectl patch hpa --typemerge -p'{"spec":{"minReplicas":10}}'# 修改minReplicas kubectl delete hpa # 删除HPA # VPA操作 kubectl get vpa -A # 查看所有VPA kubectl get vpa -o jsonpath='{.status.recommendation}'| jq . # 查看推荐值 kubectl delete vpa # 删除VPA # Metrics查看 kubectl top nodes # 节点资源使用 kubectl top pods -n --sort-by=cpu # Pod CPU排序 kubectl top pods -n --sort-by=memory # Pod内存排序 kubectl get --raw"/apis/metrics.k8s.io/v1beta1/pods"| jq . # 原始指标数据 kubectl get --raw"/apis/custom.metrics.k8s.io/v1beta1"| jq .# 自定义指标列表
B. 配置参数详解
HPA behavior参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
| scaleUp.stabilizationWindowSeconds | 0 | 扩容稳定窗口,0表示立即扩容 |
| scaleDown.stabilizationWindowSeconds | 300 | 缩容稳定窗口,默认5分钟 |
| policies[].type | - | Pods(固定数量)或Percent(百分比) |
| policies[].value | - | 每次扩缩的数量或百分比 |
| policies[].periodSeconds | - | 策略执行间隔,最小1秒,最大1800秒 |
| selectPolicy | Max | Max取最大变更量,Min取最小变更量,Disabled禁止扩缩 |
VPA resourcePolicy参数:
| 参数 | 说明 |
|---|---|
| containerName | 容器名,"*"表示所有容器 |
| mode | Auto(自动调整)或Off(不调整该容器) |
| minAllowed | 最小资源限制,VPA推荐值不会低于此值 |
| maxAllowed | 最大资源限制,VPA推荐值不会高于此值 |
| controlledResources | 控制哪些资源,可选cpu、memory |
| controlledValues | RequestsOnly(只调requests)或RequestsAndLimits(同时调) |
C. 术语表
| 术语 | 英文 | 解释 |
|---|---|---|
| 水平伸缩 | Horizontal Scaling | 通过增减Pod副本数来调整服务容量 |
| 垂直伸缩 | Vertical Scaling | 通过调整单个Pod的CPU/内存配额来调整服务容量 |
| 稳定窗口 | Stabilization Window | HPA在做扩缩决策前等待的时间窗口,用于防止指标抖动导致频繁扩缩 |
| 指标服务器 | Metrics Server | K8s集群级资源指标聚合器,提供CPU和内存使用数据 |
| 推荐器 | Recommender | VPA组件,分析历史资源使用数据并生成资源推荐值 |
| 更新器 | Updater | VPA组件,负责驱逐需要更新资源配置的Pod |
| 准入控制器 | Admission Controller | VPA组件,在Pod创建时注入推荐的资源配置 |
| 自定义指标 | Custom Metrics | 用户自定义的业务指标,如QPS、队列深度等,通过Prometheus Adapter暴露给K8s |
-
cpu
+关注
关注
68文章
11326浏览量
225836 -
内存
+关注
关注
9文章
3231浏览量
76499 -
kubernetes
+关注
关注
0文章
274浏览量
9530
原文标题:从流量突增到资源优化:Kubernetes HPA+VPA 实战指南
文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
Kubernetes Helm入门指南
阿里云容器Kubernetes监控(二) - 使用Grafana展现Pod监控数据
如何部署基于Mesos的Kubernetes集群
Kubernetes云平台的弹性伸缩实现方案
Kubernetes HPA和VPA使用实战指南
评论