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

K8s 终于原生支持 Pod 垂直扩展了。VPA 和 HPA 有什么区别?什么场景该用 VPA?从原理到配置实操,拆解垂直扩展的落地要点和生产注意事项。

HPA 解决了水平扩展,但垂直扩展一直是痛点

做过 K8s 运维的人都有体感:大多数性能问题不是靠加 Pod 能解决的。

水平扩展(HPA)解决的是"请求量大了,多开几个 Pod 分担"的问题。但对于单线程计算密集型任务、大内存缓存服务、数据库连接池这类场景,瓶颈不在请求并发量,而在单个 Pod 的 CPU 和内存不够用。这时候你需要的是垂直扩展——给现有的 Pod 分配更多的 CPU 或内存。

长期以来,K8s 对垂直扩展的支持一直是个半成品。VPA(Vertical Pod Autoscaler)作为 incubator 项目存在了好几年,但生产环境中用的人不多,原因有几个:

  • VPA 调整资源需要重启 Pod(修改 requests/limits 必须重建容器)
  • 跟 HPA 同时使用可能冲突(一个调副本数,一个调资源量)
  • 推荐值更新有延迟,不能应对突发流量
  • 社区关注度远不如 HPA

但 K8s 1.35 带来了重要变化——**In-place Pod Vertical Scaling(原地垂直扩展)**进入了 Beta 阶段。这个特性允许在不重启容器的情况下动态调整 Pod 的 CPU 和内存资源。虽然 VPA 本身还不是 K8s 内置组件,但这个底层能力的成熟意味着垂直扩展终于可以在生产环境中认真使用了。

VPA vs HPA:不是替代关系,是互补关系

先把两个概念的区别理清楚,很多人搞混了。

维度HPA(水平扩展)VPA(垂直扩展)
扩展方向增减 Pod 副本数调整单个 Pod 的 CPU/内存
触发条件CPU/内存利用率、自定义指标历史资源使用数据
生效方式创建/销毁 Pod(秒级)需要重启 Pod(Off 模式)或原地调整(In-place)
适用场景无状态服务、可水平扩展的服务单线程任务、大内存缓存、数据库
对业务影响新 Pod 需要启动和预热时间重启模式有短暂中断,原地模式几乎无感
资源效率可能产生资源碎片精确匹配实际需求
配置复杂度中(需要调优推荐算法参数)

核心原则:能用 HPA 的场景优先用 HPA,VPA 解决的是 HPA 解决不了的问题。

典型的 VPA 适用场景:

  • 批处理任务:一个 Pod 跑一个大数据处理 Job,CPU 需求波动大,但不能水平扩展(任务不可拆分)
  • 内存密集型服务:Redis 集群的单个节点、Elasticsearch 的单个分片、Java 应用的 JVM 堆
  • 开发/测试环境:不想为每个服务手动调资源,让 VPA 自动推荐合适的 requests/limits
  • 资源浪费严重的集群:大量 Pod 的 requests 设得过高(怕 OOM),实际使用率不到 30%

VPA 的工作原理

VPA 由三个组件组成:

1. Recommender(推荐器) 监控 Pod 的历史资源使用数据(从 Metrics Server 获取),基于统计算法计算推荐的 CPU/内存值。核心算法考虑了:

  • 历史使用量的分位数(默认取 95% 分位数作为推荐值)
  • 使用趋势(增长/稳定/下降)
  • 最小资源限制(避免推荐过低的值)

2. Updater(更新器) 检查当前 Pod 的资源配置和 Recommender 的推荐值之间的差异。如果差异超过阈值,标记 Pod 需要更新。

3. Admission Controller(准入控制器) 当 Pod 被重建时(由 Updater 触发或正常滚动更新),拦截 Pod 创建请求,自动将 resources.requests 修改为 Recommender 的推荐值。

这三个组件的协作流程:

1
Metrics Server → Recommender(计算推荐值)→ Updater(检测差异)→ 标记 Pod 需要更新 → Pod 重建时 → Admission Controller(注入推荐值)

注意一个关键点:在 Off 和 Auto 模式下,VPA 需要 Pod 重启才能生效。 这是 VPA 在生产环境中被吐槽最多的地方。In-place 扩展解决了这个问题,但目前还在 Beta 阶段。

VPA 的三种模式

模式行为适用场景
Off只计算推荐值,不自动执行资源规划、容量评估
Initial仅在 Pod 首次创建时注入推荐值不希望运行时被中断的服务
Auto自动更新 Pod 资源(通过重建)可以容忍短暂中断的服务

生产环境中,Off 模式用得最多。原因是:你让 VPA 算出推荐值,然后人工审核后手动调整,比让 VPA 自动重启你的 Pod 安全得多。

配置实操

安装 VPA

VPA 不是 K8s 内置组件,需要单独安装:

1
2
3
4
5
6
# 克隆 VPA 仓库
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler

# 安装(会自动创建 namespace、部署三个组件)
./hack/vpa-up.sh

安装完成后验证:

1
2
kubectl get pods -n kube-system | grep vpa
# 应该看到 vpa-recommender、vpa-updater、vpa-admission-controller 三个 Pod

创建 VPA 资源

以一个 Java Web 服务为例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: order-service-vpa
  namespace: production
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: order-service
  updatePolicy:
    updateMode: "Off"  # 只推荐,不自动执行
  resourcePolicy:
    containerPolicies:
    - containerName: order-service
      minAllowed:
        cpu: 100m
        memory: 256Mi
      maxAllowed:
        cpu: 4
        memory: 8Gi
      controlledResources: ["cpu", "memory"]
      controlledValues: RequestsAndLimits

关键参数解释:

  • updateMode: “Off”:只计算推荐值,不自动重启 Pod。生产环境建议先用 Off 模式观察一段时间。
  • minAllowed / maxAllowed:推荐值的上下限。防止 VPA 推荐过低的值导致 OOM,或推荐过高的值浪费资源。
  • controlledResources:控制哪些资源类型。如果只想让 VPA 调 CPU 不动内存,设为 ["cpu"] 即可。
  • controlledValuesRequestsAndLimits 表示同时调整 requests 和 limits,RequestsOnly 只调整 requests。

查看推荐值

VPA 创建后,Recommender 需要几分钟收集数据才会给出推荐值:

1
kubectl get vpa order-service-vpa -n production -o yaml

在 status 里会看到类似这样的输出:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
status:
  recommendation:
    containerRecommendations:
    - containerName: order-service
      lowerBound:
        cpu: 200m
        memory: 512Mi
      target:
        cpu: 500m
        memory: 1Gi
      upperBound:
        cpu: 800m
        memory: 2Gi
      uncappedTarget:
        cpu: 450m
        memory: 900Mi

其中:

  • target:推荐的目标值(考虑了 minAllowed/maxAllowed 约束)
  • lowerBound:资源使用量的下界(低于这个值服务可能不稳定)
  • upperBound:资源使用量的上界(历史峰值加上安全余量)
  • uncappedTarget:不考虑约束的原始推荐值

根据这些推荐值,你可以手动调整 Deployment 的 resources 配置。

In-place 垂直扩展:不重启容器就能调资源

K8s 1.35 的 In-place Pod Vertical Scaling 是一个底层能力突破。它允许通过 kubectl patch 直接修改运行中 Pod 的资源配置,而不需要重建容器。

使用方式:

1
2
3
# 原地调整 Pod 的 CPU 和内存
kubectl patch pod order-service-abc123 --subresource='resize' --type='merge' \
  -p '{"spec":{"containers":[{"name":"order-service","resources":{"requests":{"cpu":"500m","memory":"1Gi"}}}]}}'

限制条件:

  • CPU 可以原地调整,因为 CFS(Completely Fair Scheduler)支持动态修改 CPU 配额
  • 内存原地调整有限制:只能调整 requests(用于调度决策),limits 的修改可能仍需要重启(因为涉及 cgroup 内存限制)
  • 需要 kubelet 和容器运行时支持(containerd 1.7+ 已支持)
  • 需要开启 InPlacePodVerticalScaling feature gate

In-place 扩展和 VPA 的结合:理论上,未来的 VPA 可以利用 In-place 能力来避免 Pod 重启。但目前 VPA 还没有内置对 In-place 的支持,需要等后续版本。

生产环境的注意事项

VPA 和 HPA 不能同时用在同一个指标上

如果 HPA 基于 CPU 利用率扩展副本数,VPA 也在调整 CPU requests,两者会互相干扰。VPA 调大了 requests,CPU 利用率下降,HPA 缩容;HPA 缩容后单 Pod 负载上升,VPA 又调大 requests……形成振荡。

解决方案:

  • VPA 和 HPA 使用不同的指标维度(VPA 管内存,HPA 管 CPU 副本数)
  • 或者 VPA 用 Off 模式,只做资源规划参考

Java 应用的特殊处理

Java 应用的 JVM 堆大小是启动时确定的,VPA 调整了 Pod 的内存 limits 但 JVM 不知道。如果 VPA 推荐了更大的内存但 JVM 堆没变,等于白调。

解决方案:

  • 使用 -XX:+UseContainerSupport(JDK 8u191+),JVM 会自动感知容器的内存限制
  • 配合 -XX:MaxRAMPercentage=75.0,让 JVM 堆占容器内存的 75%
  • VPA 调整内存后需要重启 Pod 才能生效(除非 In-place 成熟)

不要对关键服务开 Auto 模式

VPA 的 Auto 模式会在"合适的时候"重启 Pod 来应用新资源。但"合适的时候"是 VPA 自己判断的,可能在你的业务高峰期触发。

最佳实践:生产环境用 Off 模式,开发/测试环境用 Auto 模式。

推荐值的滞后性

VPA 的 Recommender 基于历史数据计算推荐值,默认取过去 8 天的数据。如果你的服务有周期性负载波动(比如工作日和周末差异很大),推荐值可能不准确。

调优参数:

  • --memory-saver:减少内存使用,但推荐值精度降低
  • --history-length:调整历史数据窗口长度(默认 8 天)
  • --pod-label-selector:只对特定标签的 Pod 计算推荐值

VPA 在集群资源优化中的价值

很多企业的 K8s 集群存在严重的**资源超额分配(Over-provisioning)**问题。开发者在申请资源时总是"多要一点"以防万一,导致:

  • Pod 的 CPU requests 总和远超节点实际 CPU 能力
  • 内存 requests 设置过高,大量内存被预留但从未使用
  • 集群利用率只有 20-30%,但管理层觉得"资源不够"要买更多机器

VPA 的 Off 模式配合一个简单的脚本,就能生成资源优化报告

1
2
3
4
5
6
7
# 获取所有 VPA 推荐值和当前配置
kubectl get vpa --all-namespaces -o json | jq -r '
  .items[] |
  .metadata.name as $name |
  .status.recommendation.containerRecommendations[]? |
  [$name, .containerName, .target.cpu, .target.memory] | @tsv
'

把推荐值和当前 requests 做个对比,就能发现哪些服务超额分配了、浪费了多少资源。这个报告拿给管理层看,比任何 PPT 都有说服力。

写在最后

K8s 的垂直扩展能力正在走向成熟。VPA 虽然不是新东西,但配合 In-place 扩展的底层能力,它终于有机会在生产环境中发挥真正的价值。

几个核心建议:

  • 先用 Off 模式:让 VPA 跑几周收集推荐数据,人工审核后手动调整
  • VPA 和 HPA 分开管:一个管资源量,一个管副本数,别混在一起
  • 关注 In-place 扩展的进展:等 In-place 稳定后,VPA 的 Auto 模式才真正适合生产环境
  • VPA 最大的价值是资源规划:帮你发现超额分配、优化集群利用率,这比自动扩展更实际

工具的价值不在于自动化程度有多高,而在于它帮你看到了之前看不到的东西。VPA 让你看到了每个 Pod 到底需要多少资源——这个信息本身就值回票价。

广告
广告位预留中 (728x90)