1. VPA 是什么不是“自动扩容”而是 Kubernetes 的智能资源调优引擎你刚接触 Kubernetes看到 “Vertical Pod Autoscaler” 这个名字第一反应可能是“哦又一个自动扩缩容工具和 HPA 差不多”——这是绝大多数新手踩进的第一个认知坑。VPA 和 HPA 根本不是同一类东西它们解决的是完全不同的问题甚至在技术哲学上是互斥的。HPAHorizontal Pod Autoscaler是“加人”当 CPU 使用率飙升到 80%它就立刻拉起第二个、第三个 Pod 来分担压力像餐厅高峰期临时叫来兼职服务员。而 VPA 是“调岗”它不增加服务员数量而是重新评估每个服务员的工作量——发现张三每天只端 3 盘菜却配了 5 盘菜的工时李四要端 15 盘菜却只给了 8 盘菜的工时于是把张三的工时砍掉一半把李四的工时翻倍。这个“调岗”的过程就是对 Pod 的CPU 请求值requests和内存请求值requests进行动态重设。VPA 的核心价值从来不是“让服务永不宕机”而是“让集群资源永不浪费”。我管理过一个中型电商集群上线前按经验给所有 Java 服务统一配了 2Gi 内存 1 核 CPU。三个月后一查 metrics-server 数据发现订单服务平均只用了 600Mi 内存但库存服务峰值会冲到 1.8Gi。结果就是订单服务白白占着 1.4Gi 内存闲置而库存服务又因 requests 不足在节点内存压力大时被 OOM Kill 频繁驱逐。VPA 就是来干这个活的它不靠人眼盯监控而是用算法持续分析每个 Pod 过去 8 天的真实 CPU/内存使用曲线算出三个关键数字——目标值Target、下限值Lower Bound、上限值Upper Bound。目标值就是“最理想状态”比如订单服务该配 768Mi下限值是“保命线”哪怕再闲也不能低于 512Mi否则调度器可能把它塞进一个只剩 300Mi 空闲内存的节点上限值是“天花板”比如库存服务再怎么涨也不能超过 2.5Gi防止它吃光整个节点。这三个数不是拍脑袋定的而是基于真实历史数据的统计学推断它会看过去 24 小时的 P95 峰值再叠加 20% 的安全冗余同时过滤掉那些持续不到 5 分钟的毛刺尖峰。这背后是 Recommender 组件在默默运行它每 10 分钟扫一次 metrics-server 的聚合指标把原始数据喂给一套内置的贝叶斯时间序列模型。所以 VPA 不是“实时响应”而是“精准预判”——它不救火它修灶台。很多人误以为 VPA 是个“开箱即用”的黑盒装上就能省 30% 云成本。错。VPA 是一把手术刀用错了会切掉动脉。它要求你必须理解 Kubernetes 资源模型的底层逻辑requests 是调度器分配 Pod 的唯一依据limits 是 cgroup 的硬性闸门。VPA 只动 requests默认也动 limits但它绝不会碰 limits 单独设置的场景。如果你的 Pod 里写了resources.limits.memory: 4Gi却没写 requestsVPA 会直接拒绝生效因为这违反了 Kubernetes 的基本契约——没有 requests 的 Pod 是“流浪汉”调度器根本不知道往哪塞。这也是为什么 VPA 必须和 metrics-server 深度绑定metrics-server 提供的是Pod 级别、分钟级、聚合后的 CPU/内存使用量而不是容器内进程的精确堆栈。它看不到 JVM 的 GC 日志也抓不到 Go 程序的 goroutine 泄漏它只相信“这个 Pod 在过去 5 分钟平均吃了 1.2Gi 内存”这个事实。所以 VPA 的推荐永远带着保守主义色彩——它宁可多给 200Mi也不愿少给 100Mi 导致 OOM。这种设计哲学决定了它的适用边界它最适合那些资源消耗模式稳定、有明显波峰波谷的无状态服务比如定时批处理任务、日志清洗管道、API 网关。而对于像 Elasticsearch 这种内存使用高度依赖 JVM 参数且波动剧烈的有状态组件VPA 的推荐往往滞后且失真这时候你得靠更专业的 APM 工具做精细化调优。2. VPA 的三大支柱Recommender、Updater、Admission Controller 如何协同作战VPA 不是一个单一进程而是一套精密咬合的齿轮组由 Recommender、Updater、Admission Controller 三个核心组件构成。它们分工明确又环环相扣共同完成“观测-决策-执行”的闭环。理解它们各自的职责和协作逻辑是避免配置灾难的第一步。我见过太多团队把 VPA 当成“一键优化”按钮结果上线后服务雪崩——问题往往出在对这三个组件的误解上。2.1 Recommender冷静的数据分析师从不越界决策Recommender 是 VPA 的大脑但它只负责“看”和“算”绝不“动手”。它的唯一输入是 metrics-server 提供的/apis/metrics.k8s.io/v1beta1API 数据输出是写入 VPA CRD 对象.status.recommendation字段的 JSON 结构。这个结构里包含三个关键数组containerRecommendations每个元素对应 Pod 中的一个容器里面存着target目标值、lowerBound下限、upperBound上限三个对象每个对象又包含cpu和memory字段。Recommender 的工作流极其克制它首先根据 VPA 对象中的spec.targetRef比如指向一个 Deployment找到目标 workload再通过该 workload 的spec.selector标签筛选出所有关联的 Pod最后批量调用 metrics-server 的pods端点获取这些 Pod 的历史指标。这里有个关键细节Recommender 默认只分析过去8 天的数据这个窗口期是硬编码在源码里的pkg/recommender/logic/recommender.go你无法通过配置修改。它会过滤掉所有运行时间不足 10 分钟的 Pod认为数据不可靠并自动忽略那些被标记为vpa.recommender.kubernetes.io/exclude: true的 Pod。Recommender 的计算逻辑是其精华所在它对 CPU 使用率采用P95 分位数 20% 安全裕度对内存则采用P99 分位数 30% 安全裕度因为内存峰值一旦突破 limits 就是 OOM Kill代价远高于 CPU 短暂超频。更聪明的是它会检测 OOMKilled 事件——如果某个 Pod 在过去 24 小时内被 OOM Kill 过 3 次Recommender 会立即将其upperBound.memory提升到最近一次 OOM 前的峰值内存使用量的 150%这是一种被动的“创伤后应激反应”。但 Recommender 有一个铁律它从不修改任何 Pod 的实际资源配置它只是把建议写在 VPA 对象的状态里像一个严谨的顾问提交报告至于老板Updater 或 Admission Controller是否采纳它概不负责。2.2 Updater谨慎的执行者只在安全前提下动手Updater 是那个真正“拧螺丝”的人但它极度敬畏系统稳定性。它的核心任务是周期性地检查当前运行中 Pod 的resources.requests是否与 Recommender 给出的target值存在显著偏差并在满足严格条件时触发更新。Updater 的行动准则写在updatePolicy.updateMode字段里这是 VPA 最易被误用的配置点。Recreate模式是目前最成熟、最推荐的生产模式。它的工作流程是Updater 发现 Pod A 的 CPU requests 是 500m而 Recommender 的 target 是 1200m偏差超过 20%默认阈值且updateMode是Recreate那么 Updater 就会给 Pod A 打上vpa.updater.kubernetes.io/eviction-timestamp注解然后调用 Kubernetes API 发起DELETE请求。注意这不是粗暴的 kill而是优雅的驱逐Eviction。Kubernetes 的 Eviction API 会先检查该 Pod 是否受PodDisruptionBudgetPDB保护如果 PDB 要求至少保留 2 个副本而当前只有 2 个Updater 就会暂停操作等待 PDB 条件满足。只有当驱逐成功Deployment 控制器才会感知到副本缺失拉起一个新 Pod。此时Admission Controller 就登场了。InPlaceOrRecreate模式是折中方案它优先尝试 Kubernetes 1.27 引入的Resize Container Resources In-Place特性直接 PATCH Pod 的spec.containers[].resources.requests字段无需重启。但这有苛刻限制节点 kubelet 必须启用InPlacePodVerticalScalingfeature gate且只能调整 requests不能调 limitslimits 调整仍需重启。我在一个测试集群试过InPlace模式VPA 1.7结果发现当节点剩余内存不足新 requests 时Updater 不会 fallback 到 evict而是无限重试导致 Pod 长期处于“半调优”状态。这就是为什么官方文档将InPlace标记为 alpha——它追求零中断却牺牲了确定性。Updater 还有一个隐藏职责它会主动规避对initContainers的调整因为 init 容器的生命周期太短调整毫无意义它也会跳过那些resources.requests为0的容器认为这是用户明确放弃 VPA 管理的信号。2.3 Admission Controller守门员确保每个新 Pod 都“生而正确”Admission Controller 是 VPA 的最后一道防线也是最常被忽视的关键环节。它不是一个独立进程而是一个 Mutating Webhook深度集成在 Kubernetes API Server 的准入控制链中。它的作用时机是在 Pod 被创建CREATE的瞬间在对象被持久化到 etcd 之前拦截请求并注入推荐的 resources。具体来说当 Deployment 创建一个新 Pod 时API Server 收到请求后会先调用所有已注册的 Mutating Webhook其中就包括 VPA 的 webhook。Webhook 会查询集群中所有VerticalPodAutoscaler对象检查这个新 Pod 的标签是否匹配某个 VPA 的spec.targetRef关联 workload 的 selector。如果匹配它就从该 VPA 对象的.status.recommendation.containerRecommendations中取出对应容器名的target值覆盖 Pod spec 中原有的resources.requests和resources.limits取决于controlledValues设置。这个过程是原子的、不可逆的——一旦 Pod 被创建它的 resources 就固定了Updater 后续的 evict 操作也只是为了催生下一个“生而正确”的 Pod。这里有个致命陷阱如果你的集群没有部署 VPA 的 Admission Controller或者它的 webhook 配置错误比如failurePolicy: Fail但证书过期那么所有新创建的 Pod 都会因为 webhook 调用失败而被 API Server 拒绝整个集群的滚动更新都会卡死。我曾经在一个灰度环境遇到这个问题现象是kubectl rollout restart deployment/my-app后新 Pod 一直 Pending 在ContainerCreatingkubectl describe pod显示Error creating: Internal error occurred: failed calling webhook vpa-webhook.kube-system.svc:443. 排查发现是 webhook 的 Service Account token 过期了。所以Admission Controller 的健康检查必须纳入你的 SRE 监控大盘它应该和 API Server 本身一样是集群的基石组件。3. 从零部署 VPA避坑指南与生产级配置详解在 Kubernetes 集群里部署 VPA绝不是kubectl apply -f https://raw.githubusercontent.com/kubernetes/autoscaler/master/vertical-pod-autoscaler/deploy/vpa.yaml一行命令就完事。那只是把四个 YAML 文件Recommender、Updater、Admission Controller、VPA CRD扔进集群离真正可用还差着十万八千里。我经历过三次 VPA 的生产部署每一次都踩过不同的深坑最终沉淀出一套“最小可行、最大安全”的落地路径。下面我会拆解每一个步骤背后的原理、风险和我的实操选择。3.1 前置条件检查metrics-server 是 VPA 的氧气瓶VPA 的 Recommender 组件完全依赖 metrics-server 提供的指标数据。没有 metrics-serverVPA 就是瞎子。但 metrics-server 本身也有诸多版本兼容性陷阱。首先确认你的 Kubernetes 版本与 metrics-server 版本匹配。例如Kubernetes 1.26 要求 metrics-server 0.6.2因为旧版本不支持v1beta1.metrics.k8s.ioAPI 的新字段。部署 metrics-server 的最佳实践是使用 Helm Chartbitnami/metrics-server而非裸 YAML因为 Helm 能自动处理 RBAC 和 ServiceAccount 的复杂关系。部署后务必执行kubectl top nodes和kubectl top pods -A验证数据是否正常返回。如果kubectl top nodes返回error: Metrics not available for node, 说明 metrics-server 的--kubelet-insecure-tls参数可能没开仅限测试环境或--kubelet-preferred-address-types没配对应设为InternalIP,ExternalIP,Hostname。更隐蔽的问题是 metrics-server 的资源限制它默认只给 100m CPU / 300Mi 内存但在大型集群1000 Pods中它会因内存不足频繁 OOM。我的解决方案是将其resources.limits.memory提升到1Gi并添加--metric-resolution30s参数降低指标采集频率以减轻压力。还有一个关键点metrics-server 默认只采集kube-system命名空间的指标而 VPA 需要所有命名空间的数据。你需要编辑 metrics-server 的 Deployment添加--kubelet-insecure-tls测试或--tls-cert-file/--tls-private-key-file生产参数并确保其 ServiceAccount 有足够的 ClusterRole 权限读取所有节点和 Pod 的指标。你可以用kubectl auth can-i --list --assystem:serviceaccount:kube-system:metrics-server来验证权限。3.2 VPA 核心组件部署为什么我坚持用 Helm 而非 kubectl apply官方 GitHub 仓库kubernetes/autoscaler提供的vpa.yaml是一个巨大的、未经拆分的单体文件包含了 Recommender、Updater、Admission Controller 的 Deployment、Service、ServiceAccount、ClusterRole、ClusterRoleBinding、MutatingWebhookConfiguration 等全部资源。这种“all-in-one”方式在 demo 环境很爽但在生产环境是灾难。原因有三第一它把所有组件的镜像 tag 锁死在一个 commit hash 上如v0.15.0-rc.1-1-ga1e0b5c你无法单独升级 Recommender 而不动 Updater第二它的 MutatingWebhookConfiguration 的caBundle是空的需要你手动用base64 -w 0 certs/ca.crt填充极易出错第三它没有提供任何资源限制resources和健康探针liveness/readiness probes的配置导致组件在高负载下不稳定。因此我强烈推荐使用社区维护的 Helm Chartkubernetes-charts/vertical-pod-autoscaler。Helm 的优势在于模块化你可以为 Recommender、Updater、Admission Controller 分别设置replicaCount、resources、nodeSelector还能精细控制 webhook 的failurePolicy我始终设为Ignore避免 webhook 故障导致整个集群创建 Pod 失败。部署命令如下helm repo add autoscaler https://kubernetes.github.io/autoscaler helm repo update helm install vpa autoscaler/vertical-pod-autoscaler \ --namespace kube-system \ --set recommender.resources.limits.memory512Mi \ --set updater.resources.limits.memory512Mi \ --set admissionController.resources.limits.memory512Mi \ --set admissionController.webhook.failurePolicyIgnore \ --set global.imagePullSecrets[0].namemy-registry-secret这里--set global.imagePullSecrets是为了解决私有镜像仓库拉取问题。部署后用kubectl get pods -n kube-system | grep vpa确认所有 Pod Running再用kubectl get mutatingwebhookconfigurations检查 webhook 是否注册成功。一个快速验证方法是创建一个简单的 nginx Deployment然后kubectl get vpa如果能看到STATUS: Active且RECOMMENDATION字段开始填充数字说明 Recommender 已连通 metrics-server。3.3 编写第一个 VPA 对象从Off模式开始用数据说话永远不要一上来就给生产服务配置updateMode: Recreate。VPA 的威力在于数据驱动而数据需要时间积累。我的标准流程是第一步创建一个updateMode: Off的 VPA让它先跑 3-5 天纯粹收集和观察 Recommender 的建议。YAML 如下apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: nginx-vpa-off namespace: default spec: targetRef: apiVersion: apps/v1 kind: Deployment name: nginx-deployment updatePolicy: updateMode: Off resourcePolicy: containerPolicies: - containerName: nginx minAllowed: cpu: 100m memory: 128Mi maxAllowed: cpu: 2 memory: 2Gi controlledResources: [cpu, memory] controlledValues: RequestsAndLimits这个配置里minAllowed和maxAllowed是安全网防止 Recommender 给出过于激进的建议比如把一个日志服务的内存从 128Mi 推荐到 16Gi。controlledValues: RequestsAndLimits表示 VPA 会同时调整 requests 和 limits且保持它们的原始比例如果原 Pod 的 requests.cpu:limits.cpu 1:2新值也会维持这个比例。部署后耐心等待。3 天后执行kubectl get vpa nginx-vpa-off -o yaml重点看.status.recommendation.containerRecommendations[0]。你会看到类似这样的输出containerRecommendations: - containerName: nginx lowerBound: cpu: 74m memory: 132152448 target: cpu: 125m memory: 2147483648 upperBound: cpu: 300m memory: 4294967296注意单位CPU 是mmilliCPU内存是字节2147483648 2Gi。现在对比你原始 Deployment 的 resourcesresources: requests: cpu: 500m memory: 512Mi limits: cpu: 1 memory: 1Gi你会发现VPA 认为当前的 CPU requests (500m) 是严重过剩的target 只要125m而内存 requests (512Mi) 则严重不足target 要2Gi。这就是数据的价值——它用客观事实告诉你哪里该砍哪里该补。此时你才进入第二步把updateMode改为Initial重新部署 VPA。Initial模式只会在新 Pod 创建时应用 recommendations不会动现有 Pod风险极低。观察新滚动更新的 Podkubectl get pod -o wide看其READY状态kubectl describe pod new-pod看 Events 是否有Successfully updated pod resources。一切平稳后再切换到Recreate模式让 Updater 开始主动驱逐旧 Pod。这个渐进式策略是我保障 VPA 平稳落地的核心经验。4. 实战配置解析如何为不同业务场景定制 VPA 策略VPA 不是“一刀切”的银弹它需要根据业务应用的特性进行精细化配置。一个给在线交易网关用的 VPA和一个给离线数据清洗任务用的 VPA其resourcePolicy和updateMode必须截然不同。我管理的集群里有 12 类不同特征的应用我为它们建立了 VPA 配置矩阵。下面我将用三个典型场景为例详细拆解配置背后的业务逻辑和我的实操选择。4.1 场景一高并发、低延迟的 API 网关Nginx/Envoy这类服务的特点是CPU 使用率波动剧烈秒级脉冲内存占用相对稳定但对 Pod 启动时间和中断零容忍。任何一次Recreate模式的驱逐都可能导致数十毫秒的请求失败。因此我的 VPA 配置核心是“保内存、慎调 CPU、零中断”。apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: api-gateway-vpa namespace: istio-system spec: targetRef: apiVersion: apps/v1 kind: Deployment name: istio-ingressgateway updatePolicy: updateMode: InPlaceOrRecreate # 优先 in-placefallback to recreate resourcePolicy: containerPolicies: - containerName: istio-proxy # Envoy sidecar minAllowed: cpu: 200m # 保证基础转发能力 memory: 512Mi # Envoy 内存池最低水位 maxAllowed: cpu: 2 # 防止突发流量打爆节点 memory: 2Gi # Envoy 内存上限 controlledResources: [memory] # 只调内存CPU 交给 HPA controlledValues: RequestsOnly # limits 由 Istio CRD 统一管理 - containerName: istio-validation # 非主容器排除 minAllowed: cpu: 0 memory: 0 controlledResources: [] # 显式禁用关键点解析第一controlledResources: [memory]是灵魂。API 网关的瓶颈几乎永远是内存连接数、TLS 握手缓存CPU 是可水平扩展的。让 VPA 只管内存CPU 交给 HPA 做横向伸缩这才是正交解耦。第二controlledValues: RequestsOnly是为了尊重 Istio 的治理模型。Istio 的PeerAuthentication和DestinationRule会为 sidecar 注入limits我们不想让 VPA 覆盖它。第三InPlaceOrRecreate模式是权衡之选。Envoy 支持 in-place resize但只限于 requests且要求 kubelet 1.27。如果 in-place 失败fallback 到 recreate 也在可接受范围内因为网关 Pod 通常有多个副本PDB 可以保证服务不中断。第四为istio-validation这类辅助容器显式禁用 VPA避免无谓的干扰。4.2 场景二长周期、高内存的批处理作业Spark Driver这类服务的特点是启动慢分钟级运行时间长小时级内存消耗巨大且随数据量线性增长但 CPU 消耗很低。一个典型的 Spark Driver Pod可能申请 32Gi 内存但实际只用 8Gi而 Executor Pod 则相反。VPA 的挑战在于如何避免 Driver 因 requests 过小被 OOM Kill又不因 requests 过大导致资源浪费apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: spark-driver-vpa namespace:>apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: payment-service-vpa namespace: finance spec: targetRef: apiVersion: apps/v1 kind: Deployment name: payment-service updatePolicy: updateMode: Recreate resourcePolicy: containerPolicies: - containerName: payment-app # Java 主容器 minAllowed: cpu: 200m memory: 1Gi maxAllowed: cpu: 4 memory: 8Gi controlledResources: [cpu, memory] controlledValues: RequestsAndLimits - containerName: fluent-bit # 日志 sidecar minAllowed: cpu: 50m memory: 128Mi maxAllowed: cpu: 200m memory: 256Mi controlledResources: [cpu, memory] controlledValues: RequestsOnly # limits 由 DaemonSet 统一管理 - containerName: otel-collector # OTel sidecar minAllowed: cpu: 100m memory: 512Mi maxAllowed: cpu: 500m memory: 1Gi controlledResources: [cpu, memory] controlledValues: RequestsAndLimits # 全局策略所有未指定的容器VPA 自动跳过关键点解析第一为每个容器单独定义containerPolicies这是 VPA 的高级功能。containerName必须与 Deployment 中spec.template.spec.containers[].name完全一致大小写敏感。第二sidecar 的controlledValues: RequestsOnly是为了与集群级 DaemonSet 策略对齐。Fluent Bit 的limits由fluent-bit-daemonset统一设置VPA 只负责告诉它“该要多少”不负责“最多能拿多少”。第三minAllowed的设定逻辑不同对payment-appminAllowed.memory是基于 JVM-Xms参数设定的初始堆对fluent-bit则是基于其配置文件中Buffers大小计算的理论最小值。第四全局策略# 全局策略所有未指定的容器VPA 自动跳过是我加的注释提醒团队如果未来新增了一个redis-exporter容器必须在这里显式声明否则 VPA 会静默忽略它导致该容器永远得不到资源优化。5. 常见故障排查从No Recommendation到Eviction Loop的实战手册VPA 的故障现象往往隐晦而致命。它不会报错只会沉默——比如 Recommender 一直显示No Recommendation或者 Updater 陷入无限驱逐循环。这些问题不会出现在kubectl get events里你需要深入组件日志和指标才能定位。以下是我在生产环境中总结的五大高频故障及其根因分析每一条都来自真实的深夜告警电话。5.1 故障一kubectl get vpa显示STATUS: InitializingRECOMMENDATION字段为空这是新手最常见的“卡住”状态。表面看是 VPA 没工作根源却五花八门。我的排查清单是检查 Recommender 日志kubectl logs -n kube-system deploy/vpa-recommender | grep -i error。最常见的错误是Failed to list *v1.Pod: Unauthorized这意味着 Recommender 的 ServiceAccount 没有list pods权限。解决方案是检查ClusterRoleBinding vpa-recommender是否绑定到了正确的ClusterRole。检查 metrics-server 连通性kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes。如果返回Error from server (NotFound): the server could not find the requested resource说明 metrics-server 的 APIService 没注册。执行kubectl get apiservice v1beta1.metrics.k8s.io看STATUS是否为True。如果不是kubectl describe apiservice v1beta1.metrics.k8s.io查看Conditions字段的reason。检查 Pod 标签匹配VPA 的spec.targetRef指向一个 Deployment而 Recommender 是通过该 Deployment 的spec.selector标签去匹配 Pod 的。如果 Deployment 的spec.selector.matchLabels是app: nginx但你创建的 Pod 手动加了env: prod标签而 Deployment 的spec.template.metadata.labels里没有env: prod那么 Recommender 就找不到 Pod。用kubectl get deploy nginx-deployment -o jsonpath{.spec.selector}和kubectl get pods -l appnginx -o jsonpath{.items[0].metadata.labels}对比即可。检查 Pod 运行时长Recommender 默认忽略运行时间 10m的 Pod。如果你的 Pod 因 CrashLoopBackOff 频繁重启它永远达不到 10 分钟Recommendation 就永远为空。此时先修复应用自身的健康问题如 liveness probe 配置过严再让 VPA 工作。5.2 故障二kubectl get vpa显示STATUS: Active但RECOMMENDATION有值UPDATER STATUS却是Inactive这表示 Recommender 正常工作但 Updater 拒绝执行。根因通常是updateMode配置与集群能力不匹配。例如你配置了updateMode: InPlace但集群 kubelet 没启用InPlacePodVerticalScalingfeature gate。此时kubectl logs -n kube-system deploy/vpa-updater | grep -i inplace会看到大量In-place update is not supported on this cluster。解决方案是要么降级为InPlaceOrRecreate要么在所有节点的 kubelet 启动参数中加入--feature-gatesInPlacePodVerticalScalingtrue并重启 kubelet。另一个常见原因是PodDisruptionBudgetPDB阻塞。假设你的 Deployment 有 3 个副本PDB 设置为minAvailable: 2而 Updater 尝试驱逐一个 Pod 时发现当前只有 2 个 Pod 在 Running它就会暂停。用kubectl get pdb -n ns和kubectl get pods -n ns -l selector交叉验证副本数和 PDB 策略。5.3 故障三VPA 不断驱逐同一个 Pod形成Eviction Loop这是最危险的故障会导致服务持续抖动。现象是kubectl get pods看到 Pod 名字不断变化my-app-5d8f9b7c4-abcde-my-app-5d8f9b7c4-fghijkubectl describe pod的 Events 里反复出现Evicted by VPA Updater。根因几乎总是VPA 的 recommendation 与 Pod 的实际 limits 冲突。例如Recommender 建议memory: 2Gi但 Pod 的limits.memory是1.5Gi。Updater 驱