8.2 自动扩缩容:HPA、VPA 与 KEDA


文档摘要

8.2 自动扩缩容:HPA、VPA 与 KEDA 流量白天是夜间的 5-10 倍,但 GPU 是 24 小时计费的——如果不能自动扩缩,要么白天扛不住、要么夜间浪费算力。自动扩缩容是云原生 AI 把「弹性」红利变现的关键,而 KEDA 是当前最强大的工具。 8.2.1 弹性的核心价值 回顾第 1 章 1.2 节,弹性是云原生的五大特性之一。对 AI 推理服务,弹性的价值尤其显著: 流量波动大:白天流量是夜间的数倍,固定容量必有一端浪费。 GPU 昂贵:一张 A100 每小时折旧约 1 美元,空载浪费显著。 冷启动慢:LLM 推理 Pod 启动要加载模型(数十秒到数分钟),扩容要提前。 混部机会:白天给推理、夜间给训练,弹性让资源流转。

8.2 自动扩缩容:HPA、VPA 与 KEDA

流量白天是夜间的 5-10 倍,但 GPU 是 24 小时计费的——如果不能自动扩缩,要么白天扛不住、要么夜间浪费算力。自动扩缩容是云原生 AI 把「弹性」红利变现的关键,而 KEDA 是当前最强大的工具。

8.2.1 弹性的核心价值

回顾第 1 章 1.2 节,弹性是云原生的五大特性之一。对 AI 推理服务,弹性的价值尤其显著:

  • 流量波动大:白天流量是夜间的数倍,固定容量必有一端浪费。
  • GPU 昂贵:一张 A100 每小时折旧约 1 美元,空载浪费显著。
  • 冷启动慢:LLM 推理 Pod 启动要加载模型(数十秒到数分钟),扩容要提前。
  • 混部机会:白天给推理、夜间给训练,弹性让资源流转。

K8s 提供了几种自动扩缩容机制:

  • HPA(Horizontal Pod Autoscaler):水平扩缩(增减副本数)。
  • VPA(Vertical Pod Autoscaler):垂直扩缩(增减单副本资源)。
  • KEDA(Kubernetes Event-Driven Autoscaling):事件驱动扩缩,AI 场景最强大。
  • CA(Cluster Autoscaler)/ Karpenter:节点级扩缩(增减节点)。

8.2.2 HPA:水平扩缩容

HPA(Horizontal Pod Autoscaler) 是 K8s 内置的水平扩缩容机制。它的核心逻辑:

  • 周期性(默认 15 秒)查询某个指标(如 CPU 利用率、QPS)。
  • 根据指标与目标值的比例,计算需要的副本数。
  • 调整 Deployment 的 replicas 字段,触发新 Pod 创建或删除。

HPA 支持的指标类型:

  • Resource Metrics:CPU、内存(默认支持)。
  • Custom Metrics:自定义指标(如 QPS、队列长度)。
  • External Metrics:外部系统的指标(如 Kafka lag)。
# HPA 伪声明(示意) apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: llm-inference-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: llm-inference minReplicas: 2 maxReplicas: 20 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70

HPA 的局限

HPA 对 AI 推理有几个局限:

  1. 指标采集延迟:HPA 默认 15 秒查询一次,对突发流量响应慢。
  2. 指标类型有限:Resource Metrics 只有 CPU/内存,AI 真正关心的是 GPU/队列长度。
  3. 扩缩策略粗糙:简单的「指标 > 阈值就扩」,不够智能。
  4. GPU 指标支持弱:原生 HPA 不直接支持 GPU 利用率指标。
  5. scale-to-zero 难:minReplicas 通常 >= 1,缩到 0 要额外配置。

8.2.3 VPA:垂直扩缩容

VPA(Vertical Pod Autoscaler) 调整单个 Pod 的资源请求(CPU/内存),而非副本数。

VPA 的用途:

  • 自动调整 Pod 的 resource requests,避免「申请过多浪费、申请不足 OOM」。
  • 推荐合适的资源配额。

但 VPA 对 AI 场景适用性有限:

  • GPU 是离散资源,VPA 调整 CPU/内存意义不大。
  • VPA 重启 Pod 才能应用新资源(影响可用性)。
  • 多数 AI 团队不用 VPA,靠手工配置 + HPA。

8.2.4 KEDA:事件驱动扩缩容

KEDA(Kubernetes Event-Driven Autoscaling) 是 CNCF 项目,是 AI 场景最强大的扩缩容工具。它的核心理念是「事件驱动」——不仅基于指标,还可以基于「外部事件」(如 Kafka 队列、Prometheus 指标、Redis 长度)扩缩。

KEDA 的核心抽象是 ScaledObject

# KEDA ScaledObject 伪声明(示意) apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: llm-inference-scaler spec: scaleTargetRef: name: llm-inference minReplicaCount: 0 # 支持 scale-to-zero maxReplicaCount: 50 pollingInterval: 5 # 5 秒查询(比 HPA 快) cooldownPeriod: 60 triggers: - type: prometheus # 从 Prometheus 查指标 metadata: serverAddress: http://prometheus:9090 metricName: request_queue_depth threshold: "10" # 队列深度阈值 query: vllm_request_queue_depth

KEDA 的工作回路:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 880 280" font-family="sans-serif" font-size="12"> <text x="440" y="22" text-anchor="middle" font-size="15" font-weight="bold">KEDA 自定义指标扩缩容回路</text> <!-- 外部指标源 --> <rect x="40" y="60" width="200" height="60" rx="6" fill="#fef9c3" stroke="#ca8a04"/> <text x="140" y="85" text-anchor="middle" font-weight="bold">Prometheus / 队列</text> <text x="140" y="105" text-anchor="middle" font-size="11">request_queue_depth</text> <!-- KEDA --> <rect x="290" y="60" width="200" height="60" rx="6" fill="#dbeafe" stroke="#2563eb"/> <text x="390" y="85" text-anchor="middle" font-weight="bold">KEDA Controller</text> <text x="390" y="105" text-anchor="middle" font-size="11">查询指标 + 决策扩缩</text> <!-- Deployment --> <rect x="540" y="40" width="280" height="100" rx="6" fill="#dcfce7" stroke="#16a34a"/> <text x="680" y="65" text-anchor="middle" font-weight="bold">推理 Deployment</text> <text x="680" y="90" text-anchor="middle" font-size="11">vLLM Pod × 3</text> <text x="680" y="110" text-anchor="middle" font-size="11">(按需扩到 N 个)</text> <text x="680" y="130" text-anchor="middle" font-size="11">支持 scale-to-zero</text> <!-- 流量 --> <rect x="290" y="180" width="280" height="50" rx="6" fill="#fce7f3" stroke="#db2777"/> <text x="430" y="210" text-anchor="middle">流量变化 → 指标变化</text> <!-- 箭头 --> <line x1="240" y1="90" x2="290" y2="90" stroke="#475569" stroke-width="2" marker-end="url(#arr9)"/> <line x1="490" y1="90" x2="540" y2="90" stroke="#475569" stroke-width="2" marker-end="url(#arr9)"/> <line x1="430" y1="180" x2="140" y2="120" stroke="#db2777" stroke-width="2" stroke-dasharray="3,2" marker-end="url(#arr9)"/> <line x1="430" y1="180" x2="390" y2="120" stroke="#db2777" stroke-width="2" stroke-dasharray="3,2" marker-end="url(#arr9)"/> <text x="440" y="255" text-anchor="middle" font-size="11" fill="#475569">指标驱动 → 自动扩缩 → 流量消化 → 指标回落 → 自动缩容(含缩到 0)</text> <defs> <marker id="arr9" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto"> <path d="M0,0 L8,3 L0,6 z" fill="#475569"/> </marker> </defs> </svg>

KEDA 的核心优势

  • 事件驱动:支持 60+ 种 trigger(Prometheus、Kafka、Redis、AWS SQS 等)。
  • scale-to-zero:minReplicaCount 可设 0,空闲时缩到 0,省钱。
  • 快轮询:默认 30 秒,可调到 5 秒,对突发流量响应快。
  • 多种扩缩策略:支持自定义扩缩公式。
  • 与 HPA 协作:KEDA 在 HPA 之上,可以同时用。

💡 判读:KEDA 是 AI 推理服务扩缩容的「最佳实践」。用「请求队列深度」作为 trigger 是经典做法——队列深度反映「请求积压程度」,比 CPU 利用率更直接反映「需要扩容」。当队列深度 > 阈值,立即扩容;队列空了,缩容甚至缩到 0。

8.2.5 GPU 弹性的特殊挑战

GPU 弹性比 CPU 弹性难得多,主要因为「冷启动慢」:

  • CPU Pod 启动:秒级(拉镜像 + 起进程)。
  • GPU Pod 启动:分钟级(拉镜像 + 加载模型到 GPU + 初始化推理引擎)。

一个 70B 模型的 vLLM Pod 冷启动可能要 1-5 分钟(加载模型权重到 GPU、初始化 PagedAttention、warmup)。这意味着:

  • 突发流量时,扩容请求到 Pod ready 要几分钟,期间用户可能超时。
  • 缩容后再扩容,要重新冷启动。

应对 GPU 冷启动的策略:

策略 说明 代价
预热副本 保持 minReplicas > 0,常备热副本 持续成本
缓冲扩容 流量预测提前扩容 预测难度
模型预热 镜像分层、模型预拉取到本地盘 镜像管理复杂
缩容到 1 而非 0 留 1 个热副本 单副本成本
模型并行预加载 多 Pod 共享本地缓存 存储架构
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 280" font-family="sans-serif" font-size="12"> <text x="360" y="22" text-anchor="middle" font-size="15" font-weight="bold">CPU vs GPU 冷启动对比</text> <!-- CPU --> <rect x="40" y="60" width="640" height="50" rx="6" fill="#dbeafe" stroke="#2563eb"/> <text x="60" y="85" font-weight="bold">CPU Pod:</text> <rect x="180" y="65" width="40" height="40" fill="#2563eb"/> <text x="200" y="90" text-anchor="middle" font-size="10" fill="#fff">拉镜像</text> <rect x="220" y="65" width="20" height="40" fill="#2563eb"/> <text x="280" y="90" text-anchor="middle" font-size="10">→ 秒级就绪</text> <!-- GPU --> <rect x="40" y="130" width="640" height="100" rx="6" fill="#fce7f3" stroke="#db2777"/> <text x="60" y="155" font-weight="bold">GPU Pod:</text> <rect x="180" y="135" width="80" height="40" fill="#db2777"/> <text x="220" y="160" text-anchor="middle" font-size="10" fill="#fff">拉镜像</text> <rect x="260" y="135" width="160" height="40" fill="#db2777"/> <text x="340" y="160" text-anchor="middle" font-size="10" fill="#fff">加载模型到 GPU(数十秒)</text> <rect x="420" y="135" width="120" height="40" fill="#db2777"/> <text x="480" y="160" text-anchor="middle" font-size="10" fill="#fff">初始化推理引擎</text> <rect x="540" y="135" width="80" height="40" fill="#db2777"/> <text x="580" y="160" text-anchor="middle" font-size="10" fill="#fff">warmup</text> <text x="360" y="200" text-anchor="middle" font-size="11" fill="#dc2626">→ 分钟级才就绪</text> <text x="360" y="220" text-anchor="middle" font-size="11" fill="#475569">突发流量期间用户可能超时</text> </svg>

8.2.6 推理服务的扩缩容策略

综合 KEDA 与 GPU 冷启动,LLM 推理服务的扩缩容最佳实践:

  1. 队列深度触发:用 KEDA 监控请求队列深度,队列 > 阈值立即扩。
  2. 最小热副本:minReplicas >= 1(不要 scale-to-zero,除非流量极稀疏)。
  3. 快速扩、慢速缩:扩容 pollingInterval 短(5s),缩容 cooldownPeriod 长(300s)。
  4. 多指标组合:队列深度 + TTFT + GPU 利用率,组合判断。
  5. 预热与缓冲:高峰前(如早上 8 点)定时预热多副本。
  6. 模型预拉取:把模型镜像分层、本地缓存,加速冷启动。
  7. 节点级弹性:配合 Cluster Autoscaler / Karpenter 提前预备节点。
策略 配置 说明
队列深度触发 KEDA + Prometheus 主力触发器
最小热副本 minReplicas=1-3 应对冷启动
快扩慢缩 polling=5s, cooldown=300s 避免抖动
多指标组合 queue + TTFT + GPU 综合判断
定时预热 Cron trigger 高峰前预热
模型预拉取 镜像分层 + 本地缓存 加速冷启动
节点弹性 Karpenter 节点级预备

8.2.7 节点级弹性:Cluster Autoscaler 与 Karpenter

Pod 扩容需要节点资源。如果集群节点不够,Pod 会 Pending。Cluster Autoscaler(CA)Karpenter 是 K8s 的节点级扩缩工具:

  • Cluster Autoscaler:K8s 官方,监听 Pending Pod,自动从云厂商 node group 加节点。
  • Karpenter:AWS 开源(现跨云),更灵活、更快的节点供应,按 Pod 需求选择最优实例类型。

对 AI 场景,Karpenter 通常更合适——它能根据 GPU Pod 的需求(型号、显存、拓扑)选择最优实例,扩容速度快。但 GPU 实例的云供应常常紧张,节点级扩容可能要数分钟(等待云厂商分配),这是 AI 弹性的另一瓶颈。

工具 来源 特点 适合
Cluster Autoscaler K8s 官方 标准兼容、稳定 通用场景
Karpenter AWS 开源 快速、灵活、智能选实例 AI、GPU 场景

8.2.8 训练任务的弹性:与推理的混部

前面讲的都是推理弹性。训练任务(回顾第 5 章)也可以弹性——配合第 3 章 3.4 节的弹性训练与 Spot 实例,训练 Pod 可以动态加入/退出。

混部场景的弹性策略:

  • 白天:推理流量高,推理副本多,训练副本少(被抢占)。
  • 夜间:推理流量低,缩容推理,扩容训练,让 GPU 满载。
  • 优先级:推理高优先级、训练低优先级,抢占时训练让位。

这种「训练-推理弹性混部」是大模型团队降本的关键工程实践——把同一批 GPU 在不同时段用于不同负载,利用率从 30% 推到 80%+。第 3 章的优先级抢占与第 8.2 的扩缩容机制,是混部的基础。

8.2.9 弹性的工程实践

落地弹性的几个工程实践:

  1. 从小开始:先固定副本,观察流量模式,再开自动扩缩。
  2. 明确 SLO:扩缩目标要与 SLO 对齐(如 TTFT < 1s 决定何时扩)。
  3. 冷启动测量:实测 Pod 冷启动时间,决定 minReplicas 与缓冲策略。
  4. 混沌测试:模拟流量突增,验证扩缩是否及时。
  5. 成本监控:弹性要省钱,但也要监控总成本,避免过度扩容。
  6. 跨层协同:Pod 扩缩 + 节点扩缩 + 训练混部,整体设计。

⚠️ 常见坑:弹性的最大坑是「扩缩震荡」——指标在阈值附近波动,副本数反复增减,Pod 反复创建销毁。解决:调宽阈值(如扩容 70%、缩容 30%)、加 cooldown、用预测式扩缩。

本节小结

  • K8s 扩缩容机制:HPA(水平)、VPA(垂直)、KEDA(事件驱动)、CA/Karpenter(节点级)。
  • HPA 是内置基础但对 AI 局限大:指标采集慢、GPU 指标弱、scale-to-zero 难。
  • KEDA 是 AI 场景最佳实践:事件驱动、支持 60+ trigger、scale-to-zero、5 秒轮询。
  • KEDA 经典用法:用「请求队列深度」作为 trigger,队列 > 阈值扩容、队列空缩容到 0。
  • GPU 弹性的核心挑战是冷启动慢(分钟级),需要预热、缓冲、模型预拉取。
  • LLM 推理扩缩容最佳实践:队列深度触发 + minReplicas >= 1 + 快扩慢缩 + 多指标 + 定时预热。
  • 节点级弹性:Karpenter 比 Cluster Autoscaler 更适合 GPU 场景(智能选实例、快速供应)。
  • 训练-推理弹性混部:白天推理、夜间训练,GPU 利用率从 30% 推到 80%+。
  • 工程实践:从小开始、明确 SLO、测冷启动、混沌测试、监控成本、跨层协同。
  • 避坑:扩缩震荡(阈值调宽 + cooldown + 预测式)。

下一节《8.3 大流量稳定性:限流、降级与过载保护》将讲清大流量冲击下的保命策略。


发布者: 作者: 灏天文库 转发
评论区 (0)
U