news 2026/4/28 18:52:56

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Kubernetes 调度与稳定性】HPA 与 VPA:5 个生产级伸缩配置与避坑指南

你是否遇到过这种情况:流量突增时应用跟不上,等到扩容 Pod 跑起来,用户已经骂了半天?或者流量下去了,Pod 还堆在那里,账单看着心在滴血?

Horizontal Pod Autoscaler(HPA)和 Vertical Pod Autoscaler(VPA)就是解决这两个问题的。HPA 负责横向——流量多了加 Pod,流量少了缩 Pod;VPA 负责纵向——每天蹲在那里看你 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 写错了。

数据流向(按时间顺序):

  1. cAdvisor(集成在 kubelet 里)采集每个容器的 CPU 用量、内存占用等原始数据。
  2. kubelet暴露/stats/summaryAPI,把汇总后的节点和 Pod 级别的指标传出去。
  3. Metrics Server(默认 15 秒轮询一次)去轮询所有 kubelet 的接口,把数据聚合到集群层面,然后通过custom.metrics.k8s.io端点注册到 API Server。
  4. 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

部署官方测试应用 php-apache(它的 resources.requests.cpu 设了 200m,HPA 依赖这个才能干活):

kubectl apply -f https://k8s.io/examples/application/php-apache.yaml

创建 HPA

kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10

看看 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 --image=busybox:1.28 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"

大概一分多钟后(HPA 响应要时间),副本数应该会涨上去。

实践 2|stabilizationWindow、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% 之间它不动,防止指标轻微波动就反复伸缩。

踩坑 1|Pod 总数为什么会超过 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 CPU,Request 降到 100m 吧”。VPA 改了 Request 之后,固定用 150m CPU 的 Pod,利用率从 75% 变成了 150%。HPA 看到利用率超过 70%,觉得“天啊,我扛不住了”,立刻疯狂扩容。ReplicaSet 里 Pod 增多之后,负载分散到更多 Pod 上,每个 Pod 的利用率反而下降了。VPA 的直方图看到利用率下降,以为需求减少了,继续往下调 Request。然后 HPA 看到利用率又反弹——循环加速,整个集群的资源配置和副本数一起开始来回震荡。

这个问题极度恶心,尤其是在有状态应用上,可能造成不可恢复的抖动。

建议方案

  1. 不要让 HPA 和 VPA 同时针对 CPU 和内存去做自动伸缩。让 HPA 管理副本数,VPA 只在Off模式下跑,定期看一眼推荐值然后手动改 Request。
  2. 如果非要用 Auto 模式,那就把 HPA 的伸缩行为配置到其他的指标上去,比如 QPS,不要让它们用同一套指标互相干扰。
  3. 某厂商的托管集群里默认同时装了这两样,很多人都不知道,结果一跑起来整个环境就崩了。避坑法则:VPA 只在 Off 模式下跑,看完推荐值手动改,别让它自动动

(实战中我见过有个哥们直接把两个都开了 Auto,第二天早上发现集群都快自己把自己锤散了,咖啡都没来得及喝就冲过去手动 rebuild。老实说,我不喜欢 VPA 的 Auto 模式,太绕了,还是 Off 跑着看推荐值最稳妥。)

踩坑 2|Pod 资源请求没设,HPA 计算不了

这个坑很小但特别常见。HPA 算 CPU/内存使用量的分母是resources.requests.xxx,没有 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 Adapter

K8s 默认只支持 CPU 和内存的 HPA。要用 QPS、消息队列堆积深度、GPU 利用率这些业务指标来做伸缩判断,一般社区里会用 Prometheus Adapter 去打通 Prometheus 和 K8s Custom Metrics API。

用 Helm 安装 Prometheus Adapter

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm install prometheus-adapter prometheus-community/prometheus-adapter \ --namespace monitoring \ --set prometheus.url=http://prometheus.monitoring.svc \ --set prometheus.port=9090

注册 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 创建时把推荐值写进去)。

实践 4|VPA 的 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),不行就回退到 Recreate

InPlaceOrRecreate这个模式很香。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

然后取targetunsappedTarget里的值手动写到 Deployment 的resources.requests里去。

踩坑 3|VPA 推荐值可能超过节点可用资源

VPA 有时候会推荐超过节点上最大可用容量的资源值。这种情况一旦发生,Pod 在重建的时候就直接 Pending 住调度不出来,永远卡在Pending状态,这是非常可怕的灾难。minAllowedmaxAllowed必须设,这是生产级别的安全底线。

实战决策|什么时候用 HPA、VPA 还是 KEDA

很多人在社区里问“这个场景我到底该用什么”,这里直接给一个判断表:

场景

推荐

理由

危险提示

无状态 Web 服务

HPA

根据 CPU/QPS 加 Pod,简单直接

别忘了设 minReplicas 和 maxReplicas

有状态单副本应用

VPA(Off模式)

没法横向扩,只能改单 Pod 资源配额

Auto 模式不要碰,原地更新第一选择

消息队列消费者

KEDA + HPA

根据队列深度弹性伸缩 KEDA 用 ScaledObject 做直接

缩容到 0 的冷启动延迟要预算

批处理任务/Timer

KEDA(Cron scaler)

定时拉起,跑完了缩回 0

任务切换开销要预估好

大规模 GPU/ML 推理

HPA + Prometheus Adapter

用 DCGM 的 GPU 利用率指标做决策

热启动很耗时,扩缩平滑窗口要加大

成本优先的 Serverless

KEDA + ECI/Fargate

支持缩容到 0,账单按秒计算

冷启动至少撑 2~5 秒

四、常见故障排查

故障 1:HPA 显示<unknown>

kubectl describe hpa看 Metrics 列显示 Unknown,大概率是 Metrics Server 挂了或者资源请求没设。

检查命令:

kubectl get pod -n kube-system -l k8s-app=metrics-server kubectl top nodes

如果是自建集群(kubeadm 那种),拿 TLS 跳过的手段:

kubectl edit deploy metrics-server -n kube-system # 在 args 里加: # --kubelet-preferred-address-types=InternalIP # --kubelet-insecure-tls

故障 2:HPA 已经超过 maxReplicas 了还在扩

检查是不是 RollingUpdate 的 maxSurge 设太大导致 Deployment 在 HPA 之外加 Pod。另外看看 Target 的 Pod 是不是有很多 Terminating 状态拖慢统计延迟。

故障 3:VPA 更新 Pod 导致持续重启

updateMode: Recreate会在每次资源不匹配时 Pod 重建。把它改成 Off,用推荐值手动配 Deployment。

总结与最后的建议

好了,大概就这些东西。总结几个核心要点:

  1. 部署顺序Metrics Server先搞起来(没用它 HPA 完全是空的),HPA 写 YAML 时minReplicasmaxReplicas别乱设,稳定窗口(stabilizationWindowSeconds)按场景调。需要业务指标做决策,配 Prometheus Adapter。
  2. 安全使用 VPA:必须设minAllowedmaxAllowedupdateModeOff或者Initial。VPA 1.4.0 把 Auto 改成了 Recreate,YAML 一定要改过来。能利用 InPlaceOrRecreate 和 InPlace Pod Vertical Scaling 能力用。不要在生产环境把 HPA 和 VPA 同时设成针对 CPU 的 Auto 模式,绝对不要。
  3. 死亡螺旋预防:HPA 和 VPA 要在两套不同指标上去做伸缩判断,不要让他们互相踩——HPA 走 QPS 或者消息队列深度,VPA 的 CPU 和内存只是静态推荐,不参与实时伸缩决策。这是最稳的组合。

你还有什么更好的办法或者踩过的坑?评论区见。如果觉得有用,欢迎分享给其他被 HPA/VPA 折磨的兄弟姐妹们。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/28 18:50:31

002 运动控制系统的组成与架构

002 运动控制系统的组成与架构 从一次深夜调试说起 凌晨两点,示波器屏幕上跳动着一条诡异的电流波形。我盯着那串毛刺,后背发凉——伺服驱动器在低速运行时出现了肉眼可见的抖动,电机发出“嗡嗡”的共振声。更让人崩溃的是,同样的…

作者头像 李华
网站建设 2026/4/28 18:49:27

计算机视觉如何革新现代农业:五大应用场景解析

1. 农业中的计算机视觉革命 站在田埂上,看着无人机掠过麦浪,我突然意识到:这片延续了上万年的耕作场景,正在被计算机视觉彻底改变。去年帮山东某农场部署作物监测系统时,亲眼见证了AI如何将病虫害识别准确率从老农经验…

作者头像 李华
网站建设 2026/4/28 18:47:25

量子计算VQE算法与CUDA-Q实践指南

1. 量子计算基础与VQE算法原理量子计算利用量子力学特性如叠加态和纠缠态实现并行计算,与传统计算机的二进制位不同,量子计算机使用量子比特(qubit)作为基本单元。一个量子比特可以表示为|ψ⟩α|0⟩β|1⟩,其中α和β…

作者头像 李华
网站建设 2026/4/28 18:45:22

手把手掌握Metasploit Framework:零基础直达实战的网络安全权威指南

Metasploit Framework(简称MSF)是网络安全领域最著名、最强大的开源渗透测试框架之一。它被安全社区誉为“可以黑掉整个宇宙”的工具,为渗透测试人员提供了完整的漏洞发现、利用和验证平台。本文将带你从零开始,全面掌握MSF的核心…

作者头像 李华
网站建设 2026/4/28 18:43:15

GEO崛起,做SEO的还能继续做吗?预算有限也能尝试的技巧

2026年的搜索环境确实变了样。走在市场前端的朋友可能已经发现,输入一个问题后,占据半个屏幕的不再是那些排在首位的网页,而是由大语言模型自动整理好的答案。这套被称为 GEO 的玩法,让原本守着旧规则的网站感到一阵凉意。翻看去年…

作者头像 李华
网站建设 2026/4/28 18:42:32

从零开始:手把手教你用Tabby和Anaconda在超算上跑通第一个AI模型

从零开始:手把手教你用Tabby和Anaconda在超算上跑通第一个AI模型 第一次接触超级计算机时,那种既兴奋又忐忑的心情我至今记忆犹新。看着屏幕上闪烁的命令行,仿佛面对一扇通往未知世界的大门。作为AI研究者或学生,掌握超算使用技能…

作者头像 李华