深色模式
VPA 垂直伸缩实战
摘要:本文面向生产 SRE / 平台工程师,解释 VerticalPodAutoscaler(VPA)如何通过三个独立组件自动推荐并(可选地)应用容器资源 requests/limits。覆盖
autoscaling.k8s.io/v1的updateMode、resourcePolicy上下界、status.recommendation的 target/lowerBound/upperBound,以及 VPA 与 HPA 同资源冲突的实战规避。适用 Kubernetes v1.28+。
适用版本与前提
- Kubernetes:v1.28+(VPA 的 CRD
apiVersion: autoscaling.k8s.io/v1;VPA 项目整体在官方仍标注为 Beta,但 CRD v1 已长期稳定 ——[版本相关]) - 工具:
kubectl、cluster-admin(安装需创建 CRD、MutatingWebhook、ClusterRole) - 前提:metrics-server 已部署且
kubectl top pods可用;目标工作负载为多副本(默认--min-replicas=2,单副本 Pod 不会被 updater 驱逐)
背景与问题
"资源该申请多少"是生产里最常被拍脑袋决定的事:拍高则节点利用率低、浪费钱;拍低则 OOMKilled / CPU throttle。VPA 的诉求是让 requests 跟随真实用量自动调整,从而把"正确的资源"放到调度器面前,提升装箱率与可用性。
核心概念
VPA 不是单个控制器,而是三个共享状态(仅通过 VerticalPodAutoscaler 对象)的组件:
| 组件 | 职责 | 是否破坏性 |
|---|---|---|
| recommender | 消费各 Pod 用量样本,维护带衰减的直方图(约 8 天历史,近期权重更高),产出 target/lowerBound/upperBound/uncappedTarget | 否 |
| updater | 若运行中 Pod 的 requests 落在 [lowerBound, upperBound] 之外,通过驱逐 API 删除该 Pod | 是(会重启 Pod) |
| admission-controller | 对新创建 Pod 做 MutatingWebhook,把 requests/limits 改写为当前推荐值 | 否(仅新建时) |
推荐值含义:
target:推荐的目标 requests。lowerBound:低于此值即欠配(under-provisioned)。upperBound:高于此值即浪费。uncappedTarget:忽略resourcePolicy限制时的原始推荐。- 安全边际由
--recommendation-margin-fraction(默认 0.15)叠加在观测用量之上。
架构与原理
updateMode 四档:
| 模式 | 行为 |
|---|---|
Off | 仅计算推荐,完全不动 Pod(推荐首选模式) |
Initial | 仅在 Pod 创建时应用推荐,不驱逐运行中 Pod |
Recreate | 主动驱逐 Pod 以应用新推荐 |
Auto | 当前等价于 Recreate(未来版本可能变化 —— [版本相关]) |
就地 resize 仍不成熟
在绝大多数集群上 Auto 意味着 updater 驱逐 Pod、由新 Pod 带回新 requests——这是一次滚动重启,不是原地静默调整。InPlace Pod Resize 仍在演进,InPlaceOrRecreate 模式需特性门控(feature gate)——[版本相关],生产请按"会发生重启"来规划。
生产实践
- 永远从
Off起步:先收集 1~2 周推荐值,对比实际 requests,再决定何时切Initial/Auto。 - 设
minAllowed/maxAllowed上下界:防止 VPA 给出极端值(如把某次尖峰误判为常态)。 - 配 PodDisruptionBudget:updater 驱逐尊重 PDB,PDB 是你的"刹车"。
- 避开单副本工作负载:默认
--min-replicas=2,单副本 Pod 永远不会被 updater 处理,推荐值会一直挂着。
操作步骤
步骤 1:安装 VPA(自建集群)
bash
# 官方安装脚本(需 cluster-admin)
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler
./hack/vpa-up.sh
kubectl get pods -n kube-system | grep vpa1
2
3
4
5
2
3
4
5
生产危险
托管集群(GKE/AKS/EKS)通常自带 VPA 功能或独立安装方式。在已启用托管 VPA 的集群上再装上游 VPA,会出现两个 recommender 争抢同一对象。安装前先确认集群是否已有 VPA(GKE 用 gcloud container clusters update --enable-vertical-pod-autoscaling)。[厂商特定]
步骤 2:先以 Off 模式采集推荐
yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: web-vpa
namespace: default
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: web
updatePolicy:
updateMode: "Off" # 仅推荐, 不重启
resourcePolicy:
containerPolicies:
- containerName: web
minAllowed:
cpu: 100m
memory: 128Mi
maxAllowed:
cpu: "2"
memory: 2Gi
controlledResources: ["cpu", "memory"]
controlledValues: RequestsAndLimits1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
步骤 3:查看推荐并人工应用
bash
kubectl describe vpa web-vpa
# 关注 Recommendation -> Container Recommendations 中的 Target / LowerBound / UpperBound
kubectl get vpa web-vpa -o jsonpath='{.status.recommendation.containerRecommendations}'1
2
3
2
3
确认推荐合理后,再切到 Initial(随发布自然生效)或 Auto(主动驱逐)。
验证
bash
kubectl get vpa web-vpa
kubectl describe vpa web-vpa | sed -n '/Recommendation/,/^$/p'
# 切到 Auto/Recreate 后, 观察 Pod 是否被重启并带上新 requests
kubectl get pods -o custom-columns=NAME:.metadata.name,CPU_REQ:.spec.containers[0].resources.requests.cpu1
2
3
4
2
3
4
回滚与清理
bash
# 切回 Off 可停止进一步驱逐, 但已应用的 requests 不会自动还原
kubectl patch vpa web-vpa --type merge -p '{"spec":{"updatePolicy":{"updateMode":"Off"}}}'
# 彻底清理
kubectl delete -f web-vpa.yaml1
2
3
4
2
3
4
生产危险
VPA 改过 requests 后不会回退到原始值。若 Auto/Recreate 引发问题,切回 Off 只能停止继续变更,已运行的 Pod 仍是新规格。要还原需手动改 Deployment 的 resources 并触发滚动发布。
故障排查
| 现象 | 根因 | 处理 |
|---|---|---|
status.recommendation 为空 | metrics-server 不可用 / 历史不足 | 先 kubectl top pods;等数小时积累样本 |
| Pod 一直不被重启 | 单副本 / PDB 阻止 / 用量在界内 | 检查副本数、PDB、min-replicas |
| Pod 创建失败 | admission-controller 设的 limit 超过 Pod 级上限 | [版本相关] Pod 级 resources 与 VPA 容器级冲突(AEP-7571 进行中),移除 Pod 级 resources 或设 controlledValues: RequestsOnly |
| VPA 与 HPA 互殴 | 同资源(CPU)双控 | 见下节 |
HPA 与 VPA 冲突(重点)
VPA 与 HPA 不可同时管理同一资源。若 HPA 按 CPU 扩缩、VPA 又调 CPU requests,二者会相互驱动、系统不收敛(详见 hpa.md 与 capacity.md)。规避组合:
- HPA 管 CPU + VPA 仅管内存(
controlledResources: [memory])。 - 或 VPA 保持
Off,把推荐值作为人工/CI 调整 requests 的依据。 - 或 HPA 用自定义/外部指标、VPA 管 CPU/内存。
安全与合规
- VPA 安装会注入 MutatingWebhook,对所有匹配 Pod 改写 resources,属高权限组件。应限定
namespaceSelector与资源范围,避免影响控制面组件。 controlledValues: RequestsAndLimits会同步改 limits;若 limit 设得过小,可能放大 OOM 风险。对内存敏感负载建议先RequestsOnly。
性能、容量与成本
- VPA 提升装箱率,通常可回收 40%~60% 被过度申请的资源(社区经验值,
[未实测]需结合你的负载验证)。 - 但
Auto模式重启 Pod 有可用性代价,对严格 SLA 的 singleton / 有状态服务应避开,或仅用Off+ 人工治理。
替代方案与权衡
| 方案 | 适用 | 不适用 |
|---|---|---|
VPA Off | 资源规格治理、出推荐值 | 需要自动自愈伸缩 |
VPA Auto | 无状态、可重启的服务 | 单副本 / 有状态 / 严格 PDB |
| 手动调 requests | 完全可控 | 人力成本高、易过时 |
| HPA | 负载波动靠副本数吸收 | 无法修正单 Pod 资源偏差 |
FAQ
Q:VPA 能和 HPA 一起用吗? A:能,但不能同资源。常见安全组合是 HPA 管 CPU、VPA 管内存,或 VPA 仅 Off 出推荐。
Q:VPA 会改 limit 吗? A:取决于 controlledValues;RequestsAndLimits 会改两者,RequestsOnly 只改 requests。
参考资料
- Kubernetes Autoscaler - Vertical Pod Autoscaler README,访问日期:2026-10-08。
- Kubernetes Autoscaler - VPA FAQ,访问日期:2026-10-08。
- kubernetes-recipes - VPA Resource Right-Sizing,访问日期:2026-10-08(社区实践,作为经验判断参考,未实测)。