为什么你需要了解 VPA?
在 Kubernetes 集群的日常运维中,资源管理始终是最核心也最令人头疼的问题之一。大部分团队最开始接触的是 HPA(Horizontal Pod Autoscaler),它通过增减 Pod 副本来应对流量变化。但 HPA 有一个先天局限:它只解决”数量”问题,不解决”大小”问题。如果你的 Pod 设置的 Request 和 Limit 本身就不合理——比如一个 Java 应用实际只需要 512MB 内存,但你给它配了 4GB——HPA 扩出来的每个副本都在浪费资源。
这时就需要 VPA(Vertical Pod Autoscaler)出场了。VPA 会自动分析 Pod 的历史资源使用数据,动态调整 CPU 和内存的 Request 与 Limit 值,让每个 Pod 的资源配置与实际需求精准匹配。简单说:HPA 管”要几个 Pod”,VPA 管”每个 Pod 要多大”。

很多开发者把 VPA 和 HPA 看作二选一的方案,这其实是个误区。在生产环境中,两者往往配合使用——VPA 负责长期优化 Pod 的基准资源配置,HPA 负责应对突发流量峰值。这种组合拳能让集群的资源利用率提升 30%-50%,对于运行大规模集群的团队来说,节省的成本相当可观。
本文将深入讲解 VPA 的工作原理、部署配置、生产实践以及常见的坑,帮助你真正用好这个强大的自动扩缩容工具。
VPA 的核心架构与工作原理
VPA 不是一个单一的组件,而是由三个协同工作的子组件构成的系统。理解它们各自的分工,是正确使用 VPA 的前提。
Recommender:资源建议的大脑
Recommender 是 VPA 中最核心的组件。它持续监听集群中所有 Pod 的历史资源使用指标(通过 Metrics Server 获取),然后基于这些数据计算出最优的 Request 和 Limit 建议。Recommender 的计算逻辑包含以下几个关键步骤:
- 数据采集:从 Metrics Server 拉取 Pod 级别的 CPU 和内存使用历史,默认保留 8 天的数据窗口
- 百分位计算:根据使用量的 P50(中位数)、P90、P95 百分位值,结合你设定的安全边际(margin),计算出推荐值
- 上下界约束:将计算结果限制在用户配置的 minAllowed 和 maxAllowed 范围内
- 推荐输出:生成 VPA 对象的 Recommendation 状态字段
下面是一个 VPA 对象被 Recommender 填充后的典型状态:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Auto"
resourcePolicy:
containerPolicies:
- containerName: "*"
minAllowed:
cpu: 100m
memory: 256Mi
maxAllowed:
cpu: "4"
memory: 8Gi
status:
recommendation:
containerRecommendations:
- containerName: app
target:
cpu: 586m
memory: 1587Mi
lowerBound:
cpu: 380m
memory: 1024Mi
upperBound:
cpu: 1100m
memory: 2048Mi
uncappedTarget:
cpu: 720m
memory: 2100Mi
上面的 status 字段展示了 Recommender 的输出。target 是推荐的最终值,lowerBound 和 upperBound 用于 Updater 判断是否需要触发更新。
Updater:执行更新的调度器
Updater 负责将 Recommender 的建议转化为实际行动。它会定期扫描集群中所有开启自动更新模式的 VPA 对象,然后判断当前 Pod 的资源配置是否需要调整。
Updater 的触发逻辑是:如果当前 Pod 的实际资源配置低于 Recommender 给出的 lowerBound,或高于 upperBound,它就会触发 Pod 的优雅驱逐(Eviction)。被驱逐的 Pod 会被 Deployment 或 StatefulSet 重新创建,创建时 VPA 的 Admission Webhook 会自动注入新的资源值。
Updater 默认每 60 秒检查一次,可以通过 --updater-interval 参数调整。生产环境中建议保持这个默认值,过于频繁的检查会导致不必要的 Pod 重建。
Admission Controller:请求注入的守门员
Admission Controller 是一个 MutatingWebhook,它拦截所有 Pod 创建请求。当 Updater 驱逐了一个 Pod 后,新的 Pod 被调度时,这个 Webhook 会读取对应 VPA 对象的 Recommendation,然后将新的资源值注入到 Pod 的 spec 中。
这个组件的存在意味着:即使你手动删除了 Pod,新创建的 Pod 也会自动获得 VPA 推荐的资源配置——不需要任何人工干预。
VPA 的三种更新模式如何选择?
VPA 提供了三种更新模式(updateMode),适用于不同的运维场景:
| 模式 | 更新方式 | 适用场景 |
|---|---|---|
Off |
仅提供建议,不自动执行 | 新集群、观望期,只想看推荐值 |
Initial |
仅在 Pod 首次创建时设置 | StatefulSet、有状态应用,避免重建 |
Auto |
自动驱逐并重建 Pod | 无状态应用、Deployment |
让我用一个实际的配置示例来说明:
# Off 模式:仅查看推荐
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: recommendation-only
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Off"
# Initial 模式:Redis 等有状态应用
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: redis-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: StatefulSet
name: redis-cluster
updatePolicy:
updateMode: "Initial"
# Auto 模式:无状态 Web 服务
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: web-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: web-service
updatePolicy:
updateMode: "Auto"
选择建议:如果你刚接触 VPA,建议先用 Off 模式运行一周,观察 Recommender 给出的推荐值是否合理。当你确认推荐值稳定后,再对无状态应用切换到 Auto 模式。对于 StatefulSet 和数据库类应用,务必使用 Initial 模式——Auto 模式下的 Pod 驱逐可能导致数据丢失。
VPA + HPA 组合部署的最佳实践
前面提到,VPA 和 HPA 可以协同工作,但需要满足一个关键前提:两者不能同时基于同一指标对同一个 Pod 进行扩缩。这是因为如果 HPA 在增减副本数量,而 VPA 同时在调整每个副本的大小,两者会互相干扰,导致系统震荡。
正确的组合方式是:VPA 仅调整 CPU/内存的 Request,HPA 基于自定义指标(如 QPS、请求延迟)来扩缩副本。配置示例如下:
# VPA: 仅管理 CPU Request(不要管理 CPU 的 Limit)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: web-combined-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: web-service
updatePolicy:
updateMode: "Auto"
resourcePolicy:
containerPolicies:
- containerName: "*"
controlledResources: ["cpu", "memory"]
controlledValues: RequestsOnly # 只调整 Request
---
# HPA: 基于 QPS 进行水平扩缩
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-combined-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-service
minReplicas: 2
maxReplicas: 20
metrics:
- type: Object
object:
metric:
name: requests_per_second
describedObject:
apiVersion: v1
kind: Service
name: web-service
target:
type: Value
value: 500
这种配置模式有几个关键要点:
- controlledValues: RequestsOnly:VPA 只调整 Request,不碰 Limit。Limit 由你手动设置一个合理的倍数,比如 Request 的 2 倍
- HPA 避免使用 CPU/Memory:既然 VPA 在动态调整 Request,HPA 就不要再用 CPU 利用率作为扩缩依据。改用业务指标如 QPS、连接数等
- 需要 Kubernetes 1.25+:VPA ≥ 0.14 版本才完整支持 controlledValues 参数
这种组合方案在 Netflix、Spotify 等大型 K8s 生产环境中已经得到广泛验证。根据公开的案例数据,采用 VPA + HPA 组合后,集群的 CPU 平均利用率从 25% 提升到了 55%,内存利用率从 40% 提升到了 70%。
VPA 的部署与安装
VPA 不是 Kubernetes 内置组件,需要手动安装。官方推荐通过 Helm 或直接 apply YAML 清单的方式部署:
# 方式一:Helm 安装(推荐)
helm repo add cowboysysop https://cowboysysop.github.io/charts
helm upgrade --install vertical-pod-autoscaler cowboysysop/vertical-pod-autoscaler \
--namespace kube-system \
--set recommender.enabled=true \
--set updater.enabled=true \
--set admissionController.enabled=true
# 方式二:直接 apply 官方清单
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler
./hack/vpa-up.sh
安装完成后,验证组件是否正常运行:
kubectl get pods -n kube-system | grep vpa
# 输出应类似:
# vpa-admission-controller-7f8d9c6b-abcde 1/1 Running
# vpa-recommender-6b9f7c8d9-xyz12 1/1 Running
# vpa-updater-5c7d9e8f0-uvw34 1/1 Running
# 查看日志确认 Recommender 正常工作
kubectl logs -n kube-system -l app=vpa-recommender --tail=50
注意:VPA 依赖 Metrics Server 提供资源指标数据。如果你的集群还没有安装 Metrics Server:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
生产环境中常见的问题与避坑指南
在经过多个生产集群的实际部署后,我总结了一些容易踩的坑,分享出来供你参考:
OOMKill 循环风险
这是 VPA Auto 模式最常见的风险。当 VPA 推荐的内存值偏低时,Pod 可能因为内存不足被 OOM Killer 杀掉,然后被 Updater 驱逐重建,重建后又被 OOM Kill,形成死循环。
解决方法:始终设置合理的 minAllowed,不要低于应用实际运行所需的基础内存。推荐先以 Off 模式运行两周,观察 Recommender 的 lowerBound,然后以这个值的 1.2 倍作为 minAllowed。
resourcePolicy:
containerPolicies:
- containerName: "*"
minAllowed:
cpu: 200m
memory: 512Mi # 设置安全下限
滚动更新与 VPA 的冲突
当你手动触发 Deployment 滚动更新时,Updater 可能也在同时驱逐 Pod。这会导致新版本 Pod 刚启动就被 VPA 驱逐,造成更新延迟。
解决方法:在滚动更新前将 VPA 模式临时切换到 Off,更新完成并确认稳定后再切回 Auto。或者使用 updateMode: Initial,让 VPA 只在新 Pod 首次启动时设置资源值,后续即使推荐值变化也不触发重建。
多个容器在同一个 Pod 中
VPA 默认会为每个容器分别生成推荐值。如果你的 Pod 包含 Sidecar 容器(如 Istio Proxy),需要分别为它们配置 resourcePolicy。
resourcePolicy:
containerPolicies:
- containerName: "app"
mode: "Auto"
minAllowed:
cpu: 100m
memory: 256Mi
- containerName: "istio-proxy"
mode: "Initial" # 对 Sidecar 只做初始配置
minAllowed:
cpu: 50m
memory: 128Mi
VPA 对 burstable 和 guaranteed QoS 的影响
当 VPA 自动调整 CPU Request 后,Pod 的 QoS 等级可能发生变化。如果你的业务要求 Guaranteed QoS(Request == Limit),需要设置 controlledValues: RequestsAndLimits,并确保 VPA 同时调整两者:
resourcePolicy:
containerPolicies:
- containerName: "*"
controlledResources: ["cpu", "memory"]
controlledValues: RequestsAndLimits # 同时调整 Request 和 Limit
maxAllowed:
cpu: "4"
memory: 8Gi
监控 VPA 效果:如何判断优化是否成功
部署 VPA 之后,你需要持续监控它的实际效果。以下是几个关键指标:
推荐稳定性
使用以下命令查看 VPA 的推荐历史,判断推荐值是否已经收敛:
# 查看 VPA 当前的推荐状态
kubectl describe vpa my-app-vpa
# 使用 promql 查询推荐值的变化趋势
# 推荐值波动幅度 < 20% 视为稳定
Pod 重建频率
Auto 模式下 Updater 驱逐 Pod 的次数是一个重要指标:
# 查看 Updater 触发的驱逐事件
kubectl get events --field-selector reason=EvictedByVPA -n my-namespace
# 如果每天每个 Deployment 的驱逐次数超过 2 次
# 说明 Recommender 的推荐不够稳定,需要调整 minAllowed/maxAllowed
资源利用率变化
这是最终的 KPI。部署 VPA 前后,对比集群的资源利用率:
# 查看集群级别的资源分配情况
kubectl describe node | grep -A 5 "Allocated resources"
# 安装 Grafana 后使用 dashboard 11074 查看 VPA 专属面板
根据我的经验,VPA 上线后的第一个月效果最明显——集群的整体资源利用率通常能提升 20%-40%。三个月后趋于稳定,此时调整频率会大幅下降,进入”微调”阶段。
总结:什么时候该用 VPA?
最后做一个简单的决策指南:
- 适合用 VPA 的场景:微服务架构、无状态 Web 服务、批处理任务、CI/CD Runner——这些应用的资源消耗模式相对可控,VPA 能精准匹配
- 不适合用 VPA 的场景:数据库(MySQL/PostgreSQL)、缓存(Redis/Memcached)、消息队列——这些有状态应用的资源配置通常需要人工根据业务数据量精细调整,VPA 的自动驱逐可能造成服务中断
- 先用 Off 模式观察:对于不确定是否适合的应用,先用 Off 模式运行两周,看推荐值是否合理,再决定是否启用 Auto
VPA 不是银弹,但它确实是 Kubernetes 集群资源管理工具箱中不可或缺的一件利器。配合 HPA、Cluster Autoscaler 一起使用,才能构建出一个真正高效、自动化的 Kubernetes 集群。
汤不热吧