资讯动态

【Kubernetes 调度与稳定性】HPA 与 VPA:5 个生产级伸缩配置与避坑指南

发布时间:2026/10/2 22:29:31 来源:尧图企业网站定制
你是否遇到过这种情况流量突增时应用跟不上等到扩容 Pod 跑起来用户已经骂了半天或者流量下去了Pod 还堆在那里账单看着心在滴血Horizontal Pod AutoscalerHPA和 Vertical Pod AutoscalerVPA就是解决这两个问题的。HPA 负责横向——流量多了加 Pod流量少了缩 PodVPA 负责纵向——每天蹲在那里看你 Pod 吃多少资源完了告诉你“你这 Request 设大了”帮你调小资源配额。但注意它们不是二选一的关系而是互补的共同构成 K8s 弹性伸缩体系。HPA 到底怎么工作——不是玄学稳定窗口stabilizationWindow、容忍度tolerance这些容易被忽略的参数怎么调VPA 的 updateMode 变了——Auto 已废弃InPlaceOrRecreate 怎么用HPA 和 VPA 一起跑为什么可能“打起来”死亡螺旋怎么避免自定义指标接入Prometheus Adapter的生产级配置我用的是 Go 1.21.5 Kubernetes v1.30.6环境在自建集群MacOS 上测试的Linux 应该也一样。这个文档里的 YAML 都是真实跑过的你需要的话可以直接复制。前置条件你至少得知道 Deployment、ReplicaSet、Pod 是啥了解 K8s 的控制循环怎么玩。Metrics Server 已经在集群里跑起来VPA 组件也装好了VPA 不是 K8s 默认自带的得手动装。一、HPA 到底怎么运行的——数据流向搞清楚了调错才有方向说 HPA 之前先搞清楚数据从哪来、经过谁、最终去哪。很多 HPA 不生效的根本原因是 Metrics Server 没部署好而不是 HPA 的 YAML 写错了。数据流向按时间顺序cAdvisor集成在 kubelet 里采集每个容器的 CPU 用量、内存占用等原始数据。kubelet暴露/stats/summaryAPI把汇总后的节点和 Pod 级别的指标传出去。Metrics Server默认 15 秒轮询一次去轮询所有 kubelet 的接口把数据聚合到集群层面然后通过custom.metrics.k8s.io端点注册到 API Server。HPA Controller跑在 kube-controller-manager 里默认 15 秒算一次从 API Server 拿到当前副本数和目标指标值算出来你要多少副本然后写进 Scale 子资源Deployment Controller 负责去干活。实践 1创建你的第一个 HPA先验证 Metrics Server 是否正常工作kubectl get deployment metrics-server -n kube-system kubectl top nodes # 能看到 CPU/MEM 使用就算 OK部署官方测试应用 ph-apache它的 resources.requests.cpu 设了 200mHPA 依赖这个才能干活kubectl apply -f https://k8s.io/examples/application/php-apache.yaml创建 HPAkubectl autoscale deployment php-apache --cpu-percent50 --min1 --max10看看 HPA 状态kubectl get hpa # 预期输出 NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE php-apache Deployment/php-apache 0%/50% 1 10 1 5s压测一下kubectl run -i --tty load-generator --rm --imagebusybox:1.28 --restartNever -- /bin/sh -c while sleep 0.01; do wget -q -O- http://php-apache; done大概一分多钟后HPA 响应要时间副本数应该会涨上去。实践 2stabilizationWindow、tolerance 和 policies——这些参数才是 HPA 调优的关键稳定窗口stabilizationWindowSeconds解决的是“刚把 Pod 缩了流量又上来白缩一场”的问题。缩容时不是看当前指标而是看过去 N 秒内的最高期望副本数。扩容的稳定窗口默认是 0——指标显示要扩HPA 马上执行。说实话我个人建议给扩容也加个 30~60 秒的稳定窗口防止那种突突突的短暂流量峰值引发不必要的急速扩容。下面是完整配置示例apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: php-apache spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: php-apache minReplicas: 2 maxReplicas: 20 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 behavior: scaleUp: stabilizationWindowSeconds: 30 # 扩容也给 30 秒稳定窗口 policies: - type: Percent value: 100 periodSeconds: 60 # 每 60 秒最多翻倍 - type: Pods value: 4 periodSeconds: 60 selectPolicy: Max scaleDown: stabilizationWindowSeconds: 300 # 默认就是 300 秒 policies: - type: Percent value: 10 periodSeconds: 60还有个容易忽略的参数是 tolerance容忍度默认 0.1也就是说目标利用率设 70% 的话在 63% 到 77% 之间它不动防止指标轻微波动就反复伸缩。踩坑 1Pod 总数为什么会超过 maxSurge——别问问就是踩过RollingUpdate 和 HPA 一起跑的时候有时候你会突然发现 Pod 总数远超预期。这个问题的根因是两个控制器互不知情。HPA 按副本数去扩容Deployment 的 RollingUpdate 按配置的 maxSurge 加新的 Pod老的还开着一下子冒出来很多。更坑爹的是Terminating 状态的 Pod 不计入可用副本数HPA 觉得“我算出来需要 10 个现在只有 7 个在提供服务那就再扩”——然后你就看到 Pod 数冒上去了。缓解方案是在 HPA 里设 maxReplicas 时留出 maxSurge 的空间另外适当调小 Deployment 的 maxSurge。二、HPA VPA 为什么可能“打起来”——死亡螺旋Death Spiral拆解这是一个经典的大坑。VPA 每 24~48 小时收集数据然后压出一条推荐值说“你的 Pod 用不了 200m CPURequest 降到 100m 吧”。VPA 改了 Request 之后固定用 150m CPU 的 Pod利用率从 75% 变成了 150%。HPA 看到利用率超过 70%觉得“天啊我扛不住了”立刻疯狂扩容。ReplicaSet 里 Pod 增多之后负载分散到更多 Pod 上每个 Pod 的利用率反而下降了。VPA 的直方图看到利用率下降以为需求减少了继续往下调 Request。然后 HPA 看到利用率又反弹——循环加速整个集群的资源配置和副本数一起开始来回震荡。这个问题极度恶心尤其是在有状态应用上可能造成不可恢复的抖动。建议方案不要让 HPA 和 VPA 同时针对 CPU 和内存去做自动伸缩。让 HPA 管理副本数VPA 只在Off模式下跑定期看一眼推荐值然后手动改 Request。如果非要用 Auto 模式那就把 HPA 的伸缩行为配置到其他的指标上去比如 QPS不要让它们用同一套指标互相干扰。某厂商的托管集群里默认同时装了这两样很多人都不知道结果一跑起来整个环境就崩了。避坑法则VPA 只在 Off 模式下跑看完推荐值手动改别让它自动动。实战中我见过有个哥们直接把两个都开了 Auto第二天早上发现集群都快自己把自己锤散了咖啡都没来得及喝就冲过去手动 rebuild。老实说我不喜欢 VPA 的 Auto 模式太绕了还是 Off 跑着看推荐值最稳妥。踩坑 2Pod 资源请求没设HPA 计算不了这个坑很小但特别常见。HPA 算 CPU/内存使用量的分母是resources.requests.xx没有 Request 就没法算。验证你所有 Deployment 有没有设 Request 的快速命令kubectl get deployments -A -o jsonpath{range .items[*]}{.metadata.namespace}/{.metadata.name}: requests{.spec.template.spec.containers[0].resources.requests}{\n}{end}实践 3自定义指标扩展 HPA——用 Prometheus AdapterK8s 默认只支持 CPU 和内存的 HPA。要用 QPS、消息队列堆积深度、GPU 利用率这些业务指标来做伸缩判断一般社区里会用 Prometheus Adapter 去打通 Prometheus 和 K8s Custom Metrics API。用 Helm 安装 Prometheus Adapterhelm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm install prometheus-adapter prometheus-community/prometheus-adapter \ --namespace monitoring \ --set prometheus.urlhttp://prometheus.monitoring.svc \ --set prometheus.port9090注册 Custom Metrics API 后验证一下kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1 | jq .resources[].name配置 HPA 使用请求数自定义指标http_requests_per_second这个 metric 需要在 Prometheus 里已经配置好规则apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: api-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: api-server minReplicas: 2 maxReplicas: 20 metrics: # 自定义指标每 Pod 每秒请求数 - type: Pods pods: metric: name: http_requests_per_second target: type: AverageValue averageValue: 100 # 每 Pod 达到 100 req/s 就扩容 behavior: scaleUp: stabilizationWindowSeconds: 30 scaleDown: stabilizationWindowSeconds: 300此外KEDA是另一个方向。它把触发条件从资源利用率扩展到了任意事件源Kafka、RabbitMQ、Cron 等做事件驱动的伸缩更方便。两者互补Prometheus Adapter HPA 更贴近原生体系KEDA 做缩容到 0 和支持 ScaleToZero 的场景更强。三、VPA 详解VPA 通过三个组件干活Recommender监控资源使用给出推荐值、Updater按推荐值更新 Pod 资源、Admission Controller拦截 Pod 创建时把推荐值写进去。实践 4VPA 的 updateMode 变了——Auto 从 1.4.0 开始已废弃VPA 1.4.0 及以后版本中Auto模式已经被废弃改成了Recreate。这个改动很多老司机都不知道直接拿老的 YAML 跑会跟预期完全不一样。目前支持的 updateMode 值有四个模式行为Off只给推荐值status.recommendation不自动更新——我最推荐的模式Initial只在 Pod 创建时设一次 Request之后不管了Recreate推荐值和当前 Request 差别大了就驱逐 Pod 重建InPlaceOrRecreate先尝试原地更新需要 K8s ≥ 1.27不行就回退到 RecreateInPlaceOrRecreate这个模式很香。Kubernetes 1.27 引入了 InPlace Pod Vertical Scaling 特性Pod 不用重启就能改资源配额。这个模式利用的就是这个能力。但是注意原地更新的时候内存限制缩减目前还不完全支持而且节点要有足够的空闲资源才能就地调大配额。apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: nginx-vpa spec: targetRef: apiVersion: apps/v1 kind: Deployment name: nginx updatePolicy: updateMode: Off # 先 Off 模式跑几天看推荐值合理了再改 resourcePolicy: containerPolicies: - containerName: nginx minAllowed: cpu: 100m memory: 256Mi maxAllowed: cpu: 500m memory: 1Gi跑几天之后查看推荐值kubectl get vpa nginx-vpa -o yaml # 看 status.recommendation.containerRecommendations然后取target或unsappedTarget里的值手动写到 Deployment 的resources.requests里去。踩坑 3VPA 推荐值可能超过节点可用资源VPA 有时候会推荐超过节点上最大可用容量的资源值。这种情况一旦发生Pod 在重建的时候就直接 Pending 住调度不出来永远卡在Pending状态这是非常可怕的灾难。minAllowed和maxAllowed必须设这是生产级别的安全底线。实战决策什么时候用 HPA、VPA 还是 KEDA很多人在社区里问“这个场景我到底该用什么”这里直接给一个判断表场景推荐理由危险提示无状态 Web 服务HPA根据 CPU/QPS 加 Pod简单直接别忘了设 minReplicas 和 maxReplicas有状态单副本应用VPAOff模式没法横向扩只能改单 Pod 资源配额Auto 模式不要碰原地更新第一选择消息队列消费者KEDA HPA根据队列深度弹性伸缩 KEDA 用 ScaledObject 做直接缩容到 0 的冷启动延迟要预算批处理任务/TimerKEDACron scaler定时拉起跑完了缩回 0任务切换开销要预估好大规模 GPU/ML 推理HPA Prometheus Adapter用 DCGM 的 GPU 利用率指标做决策热启动很耗时扩缩平滑窗口要加大成本优先的 ServerlessKEDA ECI/Fargate支持缩容到 0账单按秒计算冷启动至少撑 2~5 秒四、常见故障排查故障 1HPA 显示unknownkubectl describe hpa看 Metrics 列显示 Unknown大概率是 Metrics Server 挂了或者资源请求没设。检查命令kubectl get pod -n kube-system -l k8s-appmetrics-server kubectl top nodes如果是自建集群kubeadm 那种拿 TLS 跳过的手段kubectl edit deploy metrics-server -n kube-system # 在 args 里加 # --kubelet-preferred-address-typesInternalIP # --kubelet-insecure-tls故障 2HPA 已经超过 maxReplicas 了还在扩检查是不是 RollingUpdate 的 maxSurge 设太大导致 Deployment 在 HPA 之外加 Pod。另外看看 Target 的 Pod 是不是有很多 Terminating 状态拖慢统计延迟。故障 3VPA 更新 Pod 导致持续重启updateMode: Recreate会在每次资源不匹配时 Pod 重建。把它改成 Off用推荐值手动配 Deployment。总结与最后的建议好了大概就这些东西。总结几个核心要点部署顺序Metrics Server先搞起来没用它 HPA 完全是空的HPA 写 YAML 时minReplicas和maxReplicas别乱设稳定窗口stabilizationWindowSeconds按场景调。需要业务指标做决策配 Prometheus Adapter。安全使用 VPA必须设minAllowed和maxAllowedupdateMode用Off或者Initial。VPA 1.4.0 把 Auto 改成了 RecreateYAML 一定要改过来。能利用 InPlaceOrRecreate 和 InPlace Pod Vertical Scaling 能力用。不要在生产环境把 HPA 和 VPA 同时设成针对 CPU 的 Auto 模式绝对不要。死亡螺旋预防HPA 和 VPA 要在两套不同指标上去做伸缩判断不要让他们互相踩——HPA 走 QPS 或者消息队列深度VPA 的 CPU 和内存只是静态推荐不参与实时伸缩决策。这是最稳的组合。你还有什么更好的办法或者踩过的坑评论区见。如果觉得有用欢迎分享给其他被 HPA/VPA 折磨的兄弟姐妹们。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑