Kubernetes 垂直扩展(VPA)深度解读:Pod 资源动态调整终于来了

为什么需要 VPA

Kubernetes 的资源管理长期面临一个尴尬的问题:

  • 设多了:浪费资源,集群利用率低下
  • 设少了:OOM Killed、CPU Throttling、服务不可用
  • 不设:BestEffort QoS,随时可能被驱逐

HPA(Horizontal Pod Autoscaler)解决了水平扩展的问题——流量大了加 Pod,流量小了减 Pod。但很多工作负载并不适合水平扩展:

不适合 HPA 的场景原因
单实例有状态服务无法多副本
内存密集型批处理增加副本不能减少单 Pod 内存需求
启动时间极长的服务水平扩展响应太慢
许可限制(License)只允许固定实例数
GPU 工作负载GPU 资源昂贵,需要精确分配

VPA(Vertical Pod Autoscaler)就是为这些场景设计的——它自动调整 Pod 的 CPU 和 Memory 的 request/limit,而不是增减 Pod 数量。

VPA vs HPA 对比

维度VPAHPA
扩展方向垂直(调资源)水平(调副本数)
调整对象CPU/Memory request & limitPod replicas
是否需要重启 Pod取决于 updateMode不需要
依赖组件metrics-server + VPA Controllermetrics-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 值。

计算逻辑:

  • CPU 推荐值:取历史 CPU 使用率的某个百分位数(默认 95th percentile)
  • Memory 推荐值:取历史 Memory 使用峰值 + 安全余量(默认 15%)
  • 安全范围:提供 Lower Bound 和 Upper Bound,表示推荐值的置信区间
# 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),确保服务可用性。

安装部署

前提条件

  • Kubernetes 1.25+(推荐 1.27+)
  • metrics-server 已安装并正常工作
  • 集群有 Mutating Webhook 支持

安装 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"
  • VPA 只计算推荐值,不修改 Pod 资源配置
  • 推荐值写入 VPA 的 status 字段,可供人工参考
  • 适用场景:初期观察阶段、生产环境谨慎上线
# 查看推荐值
kubectl get vpa my-app-vpa -o yaml | grep -A 20 recommendation

2. Initial 模式(仅在创建时注入)

updatePolicy:
  updateMode: "Initial"
  • 只在 Pod 首次创建时注入推荐资源值
  • 已运行的 Pod 不会被驱逐或修改
  • 适用场景:Job、一次性任务、不允许中断的服务

3. Auto 模式(全自动)

updatePolicy:
  updateMode: "Auto"
  • 新 Pod 创建时注入推荐值
  • 已运行的 Pod 如果偏差超过阈值,会被驱逐重建
  • 适用场景:Deployment 管理的无状态/有状态服务
Auto 模式驱逐条件:

偏差计算: |当前值 - 推荐值| / 当前值

默认阈值:
- CPU: 偏差 > 10% 且绝对差 > 100m
- Memory: 偏差 > 15% 且绝对差 > 100Mi

满足任一资源超出阈值 → 触发驱逐

模式对比

模式创建时注入运行时更新驱逐 Pod适用场景
Off观察、评估
InitialJob、不可中断服务
AutoDeployment 标准服务

生产环境配置建议

渐进式上线策略

推荐的 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

设置原则

  • minAllowed:服务能正常运行的最低配置(做过压测验证)
  • maxAllowed:不超过单节点资源的 50%,防止 VPA 把整个节点的 Pod 都调上去

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 次数/月121-92%
CPU Throttling 时间占比8%2%-75%
集群整体资源浪费率55%22%-60%
Pod 驱逐次数/周03-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 落地的核心目标。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

软件工程师文艺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值