K8s 系列 | 第 18 天:VPA 与 Cluster Autoscaler:资源与集群层的自动扩缩


tags:
– Kubernetes
– K8s系列
– DevOps
– VPA
– Cluster Autoscaler
– 自动扩缩
– 弹性伸缩


K8s 系列 | 第 18 天:VPA 与 Cluster Autoscaler:资源与集群层的自动扩缩

第 18/30 天

引言

前两篇文章我们深入探讨了 HPA(水平 Pod 自动扩缩)的机制与实践。HPA 通过增减 Pod 副本数来应对负载变化,但有些场景下,HPA 并不是最佳选择——比如单体应用难以水平扩展,或者 Pod 的资源请求设置不合理导致资源浪费或性能瓶颈。这时,我们需要另外两个强大的自动扩缩组件:VPA(Vertical Pod Autoscaler,垂直 Pod 自动扩缩器)Cluster Autoscaler(集群自动扩缩器)

VPA 负责在 Pod 级别自动调整 CPU/内存的请求与限制,让每个 Pod 获得”刚好够用”的资源;Cluster Autoscaler 则在集群级别自动增减 Worker 节点数量,确保集群资源池与工作负载需求相匹配。三者结合(HPA + VPA + Cluster Autoscaler)构成了 Kubernetes 生产环境中完整的弹性伸缩体系。

本文将从原理到实战,详细讲解 VPA 和 Cluster Autoscaler 的工作机制、部署配置与最佳实践。


核心概念

什么是 VPA?

VPA(Vertical Pod Autoscaler)自动调整 Pod 中容器的 CPU 和内存资源请求(Requests)与限制(Limits),避免资源浪费或不足。与 HPA 增加/减少副本数不同,VPA 改变的是每个副本的资源规格

VPA 的三种更新模式:

模式 行为 适用场景
Auto 自动更新 Pod 资源,并在需要时驱逐重建 生产环境推荐
Recreate 与 Auto 类似,始终通过驱逐重建来应用新资源值 需要严格保证生效时
Initial 只在 Pod 创建时设置资源值,之后不再更改 新建工作负载的基线调优
Off 仅提供资源推荐,不自动应用任何更改 仅做资源分析审计

核心原理: VPA 部署为三个组件:

  1. Recommender — 基于历史指标(来自 Metrics Server 或 Prometheus)计算推荐资源值
  2. Updater — 检查 Pod 的当前资源是否偏离推荐值,驱逐需要更新的 Pod
  3. Admission Controller — 拦截新建 Pod 的请求,重写其资源 Requests/Limits 为推荐值

什么是 Cluster Autoscaler?

Cluster Autoscaler(CA)自动调整集群的 Worker 节点数量。当 Pod 因资源不足而 Pending(挂起等待调度)时,CA 会扩容新节点;当节点长期低负载且其上所有 Pod 都可被调度到其他节点时,CA 会缩容释放节点。

工作流程:

Pod Pending(无可用节点)
    ↓
Cluster Autoscaler 检测到不可调度的 Pod
    ↓
调用云厂商 API 创建新节点(或内部资源池分配)
    ↓
节点就绪,Pod 调度成功
    ↓
长期低负载时,CA 排空(Drain)节点并缩容

云厂商支持: AWS(ASG)、GCP(MIG)、Azure(VMSS)、阿里云(ESS)、华为云(AS)等均有原生集成。裸金属或私有云可通过 clusterapi 或自定义 cloud provider 实现。


实战步骤

环境准备

确保集群已安装 Metrics Server:

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
kubectl get deployment metrics-server -n kube-system

第一步:部署 VPA

VPA 不是核心组件,需要单独安装:

# 下载 VPA 部署清单
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler

# 部署所有组件(Recommender + Updater + Admission Controller)
./hack/vpa-up.sh

# 验证部署
kubectl get pods -n kube-system | grep vpa

预期输出类似:

vpa-admission-controller   1/1     Running
vpa-recommender            1/1     Running
vpa-updater                1/1     Running

第二步:创建示例应用并配置 VPA

创建一个 Deployment 用于演示:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vpa-demo
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: vpa-demo
  template:
    metadata:
      labels:
        app: vpa-demo
    spec:
      containers:
      - name: stress
        image: polinux/stress
        resources:
          requests:
            cpu: 50m
            memory: 50Mi
        command: ["stress"]
        args:
          - "--cpu"
          - "1"
          - "--vm"
          - "1"
          - "--vm-bytes"
          - "80M"
          - "--timeout"
          - "300s"

创建对应的 VPA 对象,让 VPA 为其提供资源推荐:

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: vpa-demo-vpa
  namespace: default
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: vpa-demo
  updatePolicy:
    updateMode: "Auto"
  resourcePolicy:
    containerPolicies:
    - containerName: "*"
      minAllowed:
        cpu: 50m
        memory: 50Mi
      maxAllowed:
        cpu: 2
        memory: 2Gi
      controlledResources: ["cpu", "memory"]

注意: minAllowedmaxAllowed 设置了资源调整的上下界,防止 VPA 过度调整导致 Pod 无法启动或资源浪费。

应用配置并观察推荐值:

kubectl apply -f vpa-demo-deploy.yaml
kubectl apply -f vpa-demo-vpa.yaml

# 等几分钟后查看 VPA 推荐值
kubectl describe vpa vpa-demo-vpa

Status.Recommendation.ContainerRecommendations 下会看到类似输出:

Container Recommendations:
  stress:
    Lower Bound:
      Cpu:     50m
      Memory:  524288k
    Target:
      Cpu:     280m
      Memory:  587202k
    Upper Bound:
      Cpu:     685m
      Memory:  1048576k
  • Target — VPA 建议使用的资源值(将写入 Pod)
  • Lower Bound — 资源下限,跌破此值应重新评估
  • Upper Bound — 资源上限,超出此值应重新评估

第三步:部署 Cluster Autoscaler

以阿里云(ACK)为例,其他云厂商的配置参数类似但 API 不同:

# 假设集群 ID 为 c2345678901234567,区域为 cn-hangzhou
kubectl apply -f https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/alicloud/examples/cluster-autoscaler.yaml

# 修改 Deployment 的环境变量,指定节点池 ID 和区域

如果是 AWS EKS 环境:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cluster-autoscaler
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: cluster-autoscaler
  template:
    metadata:
      labels:
        app: cluster-autoscaler
    spec:
      serviceAccountName: cluster-autoscaler
      containers:
      - image: registry.k8s.io/autoscaling/cluster-autoscaler:v1.30.0
        name: cluster-autoscaler
        command:
        - ./cluster-autoscaler
        - --v=4
        - --stderrthreshold=info
        - --cloud-provider=aws
        - --skip-nodes-with-local-storage=false
        - --expander=least-waste
        - --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/<cluster-name>
        - --balance-similar-node-groups=true
        - --skip-nodes-with-system-pods=false

关键参数说明:

参数 作用
--expander=least-waste 选择扩容时资源浪费最少的节点组
--skip-nodes-with-local-storage=false 允许缩容有本地存储的节点
--balance-similar-node-groups 保持相似节点组实例数量平衡
--max-nodes-total 集群最大节点数,防止失控扩容
--scale-down-delay-after-add 新节点加入后的缩容等待时间(默认 10 分钟)

第四步:模拟触发 Cluster Autoscaler

创建一个大量请求资源的 Deployment,迫使 Pod Pending:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ca-trigger-demo
  namespace: default
spec:
  replicas: 10
  selector:
    matchLabels:
      app: ca-trigger
  template:
    metadata:
      labels:
        app: ca-trigger
    spec:
      containers:
      - name: busybox
        image: busybox
        command: ["sleep", "3600"]
        resources:
          requests:
            cpu: "1"
            memory: "1Gi"
kubectl apply -f ca-trigger.yaml

# 观察 Pending 状态
kubectl get pods -w | grep Pending

# 查看 Cluster Autoscaler 日志
kubectl logs -n kube-system deployment/cluster-autoscaler --tail=50

CA 日志中会显示类似信息:

I0704 08:00:00.000000       1 static_autoscaler.go:239] 10 unschedulable pods
I0704 08:00:00.000000       1 scale_up.go:394] Scale-up: setting group ess-k8s-nodes size to 3->4

常见问题

1. VPA 与 HPA 能否同时使用?

不建议对同一指标同时使用 VPA 和 HPA。 如果 HPA 基于 CPU 使用率扩缩副本,同时 VPA 也在调整 CPU Request,二者会互相干扰,导致振荡行为。合理做法是:

  • HPA 基于自定义指标(如 QPS、请求延迟)+ VPA 调整 基础资源 Requests
  • 或者:HPA + Cluster Autoscaler 组合使用,VPA 用于非 HPA 的工作负载

2. VPA 驱逐 Pod 导致服务中断怎么办?

VPA 在 Auto 模式下通过驱逐重建来更新资源值。应对策略:
– 与 PodDisruptionBudget(PDB) 配合使用,确保最小可用副本数
– 对于无状态应用影响较小,有状态应用需谨慎
– 可以先使用 Initial 模式观察推荐值,确认合理后再切换为 Auto

3. Cluster Autoscaler 缩容太慢或误缩容?

  • 调整 --scale-down-delay-after-add / --scale-down-delay-after-delete 等延迟参数
  • 使用 --scale-down-unneeded-time 控制节点低负载后等待多久才缩容(默认 10 分钟)
  • 通过 --max-node-provision-time 控制扩容超时

4. VPA 推荐值不准确?

  • 确保 Metrics Server 或 Prometheus 采集正常
  • VPA Recommender 需要至少 8 小时的历史数据才能给出稳定推荐
  • 短期突发的流量峰值会导致推荐值偏高,可人工设定 maxAllowed 上限

5. 私有云或裸金属如何使用 Cluster Autoscaler?

对于非云环境,有两种方案:
Cluster API:通过声明式方式管理裸金属/虚拟化节点
自定义 Cloud Provider:实现 cloud provider 接口对接内部资源管理系统


三剑客协同架构

在生产环境中,HPA、VPA 和 Cluster Autoscaler 三者协同工作,构成完整的弹性伸缩体系:

用户请求 → [HPA: 增减 Pod 副本数] → 副本数变化 → [Cluster Autoscaler: 增减节点数]
                              ↕
                    [VPA: 调整 Pod 资源规格]

推荐的分层策略:

层面 组件 职责 响应速度
应用层 HPA 基于业务指标增减副本 秒级~分钟级
Pod 资源层 VPA 优化单个 Pod 的资源请求 分钟级
集群资源层 Cluster Autoscaler 动态调整节点数量 分钟级~十分钟级

总结

VPA 和 Cluster Autoscaler 是 Kubernetes 弹性伸缩体系中不可或缺的两大组件:

  • VPA 解决的是”每个 Pod 该用多少资源”的问题,通过历史数据推荐最优资源值,减少资源浪费和性能瓶颈。适合单体应用、资源需求波动有规律的工作负载。
  • Cluster Autoscaler 解决的是”集群需要多少节点”的问题,当资源不足时自动扩容节点,资源过剩时自动缩容节支。
  • 三者结合(HPA + VPA + Cluster Autoscaler)可以实现从应用层到基础设施层的全栈自动弹性伸缩,是生产环境 K8s 集群的标配。

最佳实践速查表:
– ✅ 所有生产集群都应部署 Cluster Autoscaler,设定节点数上下限
– ✅ 无状态应用优先使用 HPA,有状态或单体应用考虑 VPA
– ✅ VPA 先用 Off 模式观察推荐值,分析合理后再开启 Auto
– ✅ 为关键服务配置 PDB,避免 VPA 驱逐导致服务中断
– ✅ Cluster Autoscaler 的 --expander 使用 least-wasteprice 策略节省成本


下期预告

第 19 天:Job 与 CronJob:批处理与定时任务实战

在 K8s 中,并不是所有工作负载都需要 7×24 小时运行。批处理、数据导出、定时备份、日志清理等任务用 Job 和 CronJob 来处理再合适不过。下一篇文章我们将深入讲解 Job 的并行机制、CronJob 的调度陷阱,以及大规模批处理任务的资源管理策略。敬请期待!


📚 K8s 系列目录导航
第 1 天:Kubernetes 核心概念与架构全景解析
第 2 天:kubeadm 搭建第一个 K8s 集群
第 3 天:Pod 详解与生命周期管理
第 4 天:Deployment 与 ReplicaSet
第 5 天:Service 与网络基础
第 6 天:Namespace 与资源配额
第 7 天:ConfigMap 与 Secret
第 8 天:Volume 与 PersistentVolume
第 9 天:StorageClass 与动态存储供给
第 10 天:StatefulSet 有状态应用管理
第 11 天:Ingress 与 Ingress Controller
第 12 天:NetworkPolicy 网络安全策略
第 13 天:Headless Service 与服务发现
第 14 天:CSI 存储插件与生产存储选型
第 15 天:污点与容忍度
第 16 天:Node Affinity 与 Pod Affinity
第 17 天:HPA 水平自动扩缩
第 18 天:VPA 与 Cluster Autoscaler(本篇)
– 更多更新中……


本文为 K8s 30 天系列的第 18 篇,由自动化博客工作流生成。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容