Kubernetes 垂直扩展(VPA)深度解读:Pod 资源动态调整终于来了
为什么需要 VPA
Kubernetes 的资源管理长期面临一个尴尬的问题:
HPA(Horizontal Pod Autoscaler)解决了水平扩展的问题——流量大了加 Pod,流量小了减 Pod。但很多工作负载并不适合水平扩展:
| 不适合 HPA 的场景 | 原因 |
|-------------------|------|
| 单实例有状态服务 | 无法多副本 |
| 内存密集型批处理 | 增加副本不能减少单 Pod 内存需求 |
| 启动时间极长的服务 | 水平扩展响应太慢 |
| 许可限制(License) | 只允许固定实例数 |
| GPU 工作负载 | GPU 资源昂贵,需要精确分配 |
VPA(Vertical Pod Autoscaler)就是为这些场景设计的——它自动调整 Pod 的 CPU 和 Memory 的 request/limit,而不是增减 Pod 数量。
VPA vs HPA 对比
| 维度 | VPA | HPA |
|------|-----|-----|
| 扩展方向 | 垂直(调资源) | 水平(调副本数) |
| 调整对象 | CPU/Memory request & limit | Pod replicas |
| 是否需要重启 Pod | 取决于 updateMode | 不需要 |
| 依赖组件 | metrics-server + VPA Controller | metrics-server(或自定义指标) |
| 适用工作负载 | 单实例、有状态、GPU | 无状态、可水平扩展 |
| 响应速度 | 较慢(需重建 Pod) | 快(创建新 Pod) |
| 与 Cluster Autoscaler 配合 | 互补 | 配合紧密 |
| GA 状态 | 部分 GA(v1.0+) | GA |
VPA vs HPA 工作流对比:
HPA 流程:
指标上升 → HPA Controller 检测到 → 增加 replicas → Scheduler 调度新 Pod
指标下降 → HPA Controller 检测到 → 减少 replicas → 删除多余 Pod
VPA 流程:
指标持续偏高 → VPA Recommender 计算推荐值
→ Admission Controller 拦截 Pod 创建
→ 注入新资源值 → Pod 以新资源配置启动
(Auto 模式)
指标持续偏高 → VPA Updater 检测到偏差
→ 驱逐旧 Pod → Deployment 重建 Pod
→ Admission Controller 注入新值 → 新 Pod 以推荐资源配置启动
VPA 架构原理
VPA 由三个核心组件构成:
VPA 架构:
┌────────────────────────────────────────────────────────────┐
│ VPA 系统架构 │
├────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Recommender │ │ metrics-server │ │
│ │ (推荐器) │◄───│ (指标采集) │ │
│ │ │ └──────────────────┘ │
│ │ - 分析历史指标 │ │
│ │ - 计算推荐值 │ │
│ │ - 定期更新 │ │
│ └────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Admission │ ┌──────────────────┐ │
│ │ Controller │◄───│ API Server │ │
│ │ (准入控制器) │ │ (Mutating │ │
│ │ │ │ Webhook) │ │
│ │ - 拦截 Pod 创建 │ └──────────────────┘ │
│ │ - 注入资源值 │ │
│ └──────────────────┘ │
│ │
│ ┌──────────────────┐ │
│ │ Updater │ │
│ │ (更新器) │ │
│ │ │ │
│ │ - 检测偏差 │ │
│ │ - 驱逐旧 Pod │ │
│ │ - 触发重建 │ │
│ └──────────────────┘ │
│ │
└────────────────────────────────────────────────────────────┘
Recommender(推荐器)
Recommender 持续从 metrics-server 获取 Pod 的资源使用数据,基于历史数据计算出推荐的 CPU 和 Memory 值。
计算逻辑:
# Recommender 输出的推荐值示例
status:
recommendation:
containerRecommendations:
- containerName: app
target:
cpu: "500m" # 推荐目标值
memory: "512Mi"
lowerBound:
cpu: "250m" # 最低推荐值
memory: "256Mi"
upperBound:
cpu: "1000m" # 最高推荐值
memory: "1Gi"
uncappedTarget:
cpu: "500m" # 不受 min/max 限制的原始推荐值
memory: "512Mi"
Admission Controller(准入控制器)
当 Pod 被创建时,VPA 的 Mutating Webhook 拦截请求,将 VPA 推荐的资源值注入到 Pod 的容器 spec 中。
请求流:
kubectl apply → API Server → Mutating Webhook (VPA)
│
▼
注入 resources:
requests:
cpu: 500m
memory: 512Mi
│
▼
Scheduler → 新 Pod(带推荐资源值)
Updater(更新器)
Updater 只在 updateMode: Auto 时活跃。它检查正在运行的 Pod 的资源配置是否偏离了 VPA 的推荐值。如果偏离超过阈值,Updater 会驱逐(evict)旧 Pod,让 Deployment/StatefulSet 重建 Pod,新 Pod 在创建时被 Admission Controller 注入推荐值。
关键约束:Updater 不会同时驱逐所有 Pod,它尊重 PodDisruptionBudget(PDB),确保服务可用性。
安装部署
前提条件
安装 metrics-server
# 检查 metrics-server 是否已安装
kubectl top nodes
kubectl top pods
# 如果未安装,使用官方 Helm chart
helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
helm install metrics-server metrics-server/metrics-server \
--namespace kube-system \
--set args="{--kubelet-insecure-tls,--kubelet-preferred-address-types=InternalIP}"
安装 VPA
# 克隆 VPA 仓库
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler
# 使用安装脚本(会创建 CRD、ServiceAccount、Deployment 等)
./hack/vpa-up.sh
# 或者使用 Helm(推荐)
helm repo add cowboysysop https://cowboysysop.github.io/charts/
helm install vpa cowboysysop/vertical-pod-autoscaler \
--namespace kube-system \
--set admissionController.enabled=true
验证安装
# 检查 VPA 组件状态
kubectl get pods -n kube-system | grep vpa
# 期望输出:
# vpa-recommender-xxx 1/1 Running 0 5m
# vpa-updater-xxx 1/1 Running 0 5m
# vpa-admission-controller-xxx 1/1 Running 0 5m
# 检查 CRD
kubectl get crd | grep verticalpodautoscaler
# 期望输出:
# verticalpodautoscalercheckpoints.autoscaling.k8s.io
# verticalpodautoscalers.autoscaling.k8s.io
YAML 配置详解
基本 VPA 配置
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Auto" # 核心参数,见下文详解
resourcePolicy:
containerPolicies:
- containerName: "app"
minAllowed:
cpu: "100m"
memory: "128Mi"
maxAllowed:
cpu: "4"
memory: "8Gi"
controlledResources:
- cpu
- memory
controlledValues: "RequestsAndLimits" # 或 "RequestsOnly"
多容器 Pod 配置
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: multi-container-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Auto"
resourcePolicy:
containerPolicies:
# 主应用容器 - 完全自动调整
- containerName: "app"
minAllowed:
cpu: "200m"
memory: "256Mi"
maxAllowed:
cpu: "2"
memory: "4Gi"
# Sidecar 容器 - 只调整 CPU
- containerName: "istio-proxy"
controlledResources:
- cpu
minAllowed:
cpu: "50m"
maxAllowed:
cpu: "500m"
# 日志收集器 - 不调整(跳过)
- containerName: "fluent-bit"
mode: "Off"
updateMode 三种模式
VPA 的核心行为由 updatePolicy.updateMode 控制,有三种模式:
1. Off 模式(仅推荐,不执行)
updatePolicy:
updateMode: "Off"
# 查看推荐值
kubectl get vpa my-app-vpa -o yaml | grep -A 20 recommendation
2. Initial 模式(仅在创建时注入)
updatePolicy:
updateMode: "Initial"
3. Auto 模式(全自动)
updatePolicy:
updateMode: "Auto"
Auto 模式驱逐条件:
偏差计算: |当前值 - 推荐值| / 当前值
默认阈值:
- CPU: 偏差 > 10% 且绝对差 > 100m
- Memory: 偏差 > 15% 且绝对差 > 100Mi
满足任一资源超出阈值 → 触发驱逐
模式对比
| 模式 | 创建时注入 | 运行时更新 | 驱逐 Pod | 适用场景 |
|------|----------|----------|---------|---------|
| Off | ❌ | ❌ | ❌ | 观察、评估 |
| Initial | ✅ | ❌ | ❌ | Job、不可中断服务 |
| Auto | ✅ | ✅ | ✅ | Deployment 标准服务 |
生产环境配置建议
渐进式上线策略
推荐的 VPA 上线步骤:
Week 1-2: Off 模式 → 收集推荐数据,对比人工设定值
Week 3: Initial 模式 → 新 Pod 使用推荐值,观察效果
Week 4+: Auto 模式 → 全自动调整,持续监控
资源边界设置
生产环境中必须设置 minAllowed 和 maxAllowed,防止 VPA 推荐出极端值:
resourcePolicy:
containerPolicies:
- containerName: "app"
minAllowed:
cpu: "100m" # 不低于 0.1 核
memory: "256Mi" # 不低于 256MB
maxAllowed:
cpu: "4" # 不超过 4 核
memory: "8Gi" # 不超过 8GB
设置原则:
controlledValues 的选择
# 同时调整 requests 和 limits(保持比例)
controlledValues: "RequestsAndLimits"
# 只调整 requests,limits 保持不变
controlledValues: "RequestsOnly"
| 选择 | 行为 | 适用场景 |
|------|------|---------|
| RequestsAndLimits | 同时调整 R 和 L,保持 R:L 比例 | 大多数场景 |
| RequestsOnly | 只调 R,L 固定 | 需要固定 limit 的场景(如 Guaranteed QoS) |
与 PDB 配合
# PodDisruptionBudget 确保 VPA 驱逐时不影响可用性
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
spec:
minAvailable: 2 # 或 maxUnavailable: 1
selector:
matchLabels:
app: my-app
VPA Updater 在驱逐 Pod 时会尊重 PDB。如果驱逐会导致违反 PDB,Updater 会等待。
VPA 与 HPA 的冲突处理
VPA 和 HPA 不能同时作用于同一个指标。 这是 K8s 社区明确指出的约束。
为什么会冲突
冲突场景:
VPA: 检测到 CPU 使用率高 → 增加 Pod 的 CPU request
HPA: 检测到 CPU 使用率高 → 增加 Pod 副本数
两者同时响应 → 资源浪费(既增加了单 Pod 资源,又增加了副本数)
解决方案
方案 1:按指标分离
VPA 管理 Memory,HPA 管理 CPU:
# VPA 只管 Memory
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: "app"
controlledResources:
- memory # 只控制 Memory
---
# HPA 只管 CPU
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
metrics:
- type: Resource
resource:
name: cpu # 只关注 CPU
target:
type: Utilization
averageUtilization: 70
方案 2:VPA 使用 Off 模式为 HPA 提供参考
VPA 在 Off 模式下提供 Memory 推荐值,人工据此设定 Pod 的 Memory request,然后 HPA 基于 CPU 做水平扩展。
冲突检测
# 检查是否有冲突配置
kubectl get vpa, hpa -n production -o json | \
jq '.items[] | select(.spec.targetRef.name == "my-app") | {kind: .kind, metrics: .spec.metrics, resources: .spec.resourcePolicy}'
生产注意事项
1. Pod 驱逐导致短暂不可用
VPA Auto 模式通过驱逐 Pod 来实现资源调整。即使是 Rolling Update,也会有短暂的容量下降。
VPA 驱逐时序:
T0: VPA Updater 决定驱逐 Pod-A(资源配置过时)
T1: Pod-A 被驱逐(graceful termination)
T2: Deployment 检测到 Pod-A 缺失,创建 Pod-B
T3: Admission Controller 给 Pod-B 注入新的资源值
T4: Pod-B 调度、启动、就绪
影响: T1 到 T4 之间,服务容量减少 1 个 Pod
缓解: 确保 Deployment replicas >= 2,配合 PDB
2. 冷启动问题
新创建的 Pod 没有历史数据,VPA Recommender 需要积累足够的数据才能给出准确推荐。
# 配置 Recommender 参数
--min-replicas=2 # 至少 2 个副本才启用 VPA
--history-length=8d # 保留 8 天历史数据
--history-resolution=1h # 历史数据分辨率 1 小时
3. Java 应用的 Memory 特殊处理
JVM 的内存模型(堆 + 非堆 + 元空间)使得 Memory 推荐值需要特别关注:
# Java 应用 VPA 配置建议
resourcePolicy:
containerPolicies:
- containerName: "java-app"
minAllowed:
memory: "512Mi" # 不能低于 JVM 基础需求
maxAllowed:
memory: "4Gi"
# JVM 参数应使用容器感知配置:
# -XX:+UseContainerSupport
# -XX:MaxRAMPercentage=75.0
# -XX:InitialRAMPercentage=50.0
4. 监控和告警
# Prometheus 告警规则
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: vpa-alerts
spec:
groups:
- name: vpa
rules:
- alert: VPARecommendationExtreme
expr: |
vpa_recommendation{recommendation_type="target"}
/ vpa_recommendation{recommendation_type="lowerBound"} > 5
for: 1h
labels:
severity: warning
annotations:
summary: "VPA 推荐值偏离下界过大"
- alert: VPAPodEvictionStorm
expr: |
increase(vpa_eviction_count[10m]) > 5
for: 5m
labels:
severity: critical
annotations:
summary: "VPA 短时间内驱逐过多 Pod"
5. 与 Cluster Autoscaler 配合
VPA 和 Cluster Autoscaler(CA)是互补关系:
协作流程:
VPA 调大 Pod 资源 → Pod 需要更多节点资源 → CA 发现资源不足 → 扩容节点
VPA 调小 Pod 资源 → 节点资源利用率下降 → CA 发现节点空闲 → 缩容节点
确保 CA 的 --scale-down-delay-after-add 参数足够长(建议 30 分钟),避免 VPA 刚调大资源,CA 就开始缩节点。
实际效果数据
以某生产环境的 Java 微服务为例,上线 VPA 前后的对比:
| 指标 | 上线前(手动设定) | 上线后(VPA Auto) | 变化 |
|------|-------------------|-------------------|------|
| 平均 CPU Request 利用率 | 35% | 68% | +94% |
| 平均 Memory Request 利用率 | 42% | 75% | +78% |
| OOM Killed 次数/月 | 12 | 1 | -92% |
| CPU Throttling 时间占比 | 8% | 2% | -75% |
| 集群整体资源浪费率 | 55% | 22% | -60% |
| Pod 驱逐次数/周 | 0 | 3-5 | - |
常见问题排查
VPA 没有给出推荐值
# 检查 Recommender 日志
kubectl logs -n kube-system -l app=vpa-recommender --tail=50
# 检查 metrics-server 是否正常
kubectl top pods -n production
# 检查 VPA 是否能找到目标资源
kubectl describe vpa my-app-vpa -n production
# 关注 Conditions 中的 FetchingHistory 和 LowConfidence
VPA 推荐值过高或过低
# 查看详细推荐值和历史
kubectl get vpa my-app-vpa -o jsonpath='{.status.recommendation}' | jq .
# 检查是否有异常数据点影响推荐
kubectl get vpa my-app-vpa -o jsonpath='{.status.conditions}' | jq .
Pod 被频繁驱逐
# 查看驱逐事件
kubectl get events --field-selector reason=Evicted -n production
# 检查 VPA Updater 日志
kubectl logs -n kube-system -l app=vpa-updater --tail=50
# 如果频繁驱逐,考虑:
# 1. 切换为 Initial 模式
# 2. 增大 maxAllowed 范围
# 3. 增加 controlledValues 为 RequestsOnly
VPA 的价值不在于完全取代人工调优,而是提供一个持续优化的基线。在资源利用率和稳定性之间找到平衡点,才是 VPA 落地的核心目标。
原文链接:https://wenyiblog.top/2026/06/k8s-vpa-vertical-scaling/
首发于文艺技术笔记(wenyiblog.top),转载请注明出处。

浙公网安备 33010602011771号