8.2 自动扩缩容:HPA、VPA 与 KEDA 流量白天是夜间的 5-10 倍,但 GPU 是 24 小时计费的——如果不能自动扩缩,要么白天扛不住、要么夜间浪费算力。自动扩缩容是云原生 AI 把「弹性」红利变现的关键,而 KEDA 是当前最强大的工具。 8.2.1 弹性的核心价值 回顾第 1 章 1.2 节,弹性是云原生的五大特性之一。对 AI 推理服务,弹性的价值尤其显著: 流量波动大:白天流量是夜间的数倍,固定容量必有一端浪费。 GPU 昂贵:一张 A100 每小时折旧约 1 美元,空载浪费显著。 冷启动慢:LLM 推理 Pod 启动要加载模型(数十秒到数分钟),扩容要提前。 混部机会:白天给推理、夜间给训练,弹性让资源流转。
流量白天是夜间的 5-10 倍,但 GPU 是 24 小时计费的——如果不能自动扩缩,要么白天扛不住、要么夜间浪费算力。自动扩缩容是云原生 AI 把「弹性」红利变现的关键,而 KEDA 是当前最强大的工具。
回顾第 1 章 1.2 节,弹性是云原生的五大特性之一。对 AI 推理服务,弹性的价值尤其显著:
K8s 提供了几种自动扩缩容机制:
HPA(Horizontal Pod Autoscaler) 是 K8s 内置的水平扩缩容机制。它的核心逻辑:
HPA 支持的指标类型:
# 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 对 AI 推理有几个局限:
VPA(Vertical Pod Autoscaler) 调整单个 Pod 的资源请求(CPU/内存),而非副本数。
VPA 的用途:
但 VPA 对 AI 场景适用性有限:
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 是 AI 推理服务扩缩容的「最佳实践」。用「请求队列深度」作为 trigger 是经典做法——队列深度反映「请求积压程度」,比 CPU 利用率更直接反映「需要扩容」。当队列深度 > 阈值,立即扩容;队列空了,缩容甚至缩到 0。
GPU 弹性比 CPU 弹性难得多,主要因为「冷启动慢」:
一个 70B 模型的 vLLM Pod 冷启动可能要 1-5 分钟(加载模型权重到 GPU、初始化 PagedAttention、warmup)。这意味着:
应对 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>
综合 KEDA 与 GPU 冷启动,LLM 推理服务的扩缩容最佳实践:
| 策略 | 配置 | 说明 |
|---|---|---|
| 队列深度触发 | KEDA + Prometheus | 主力触发器 |
| 最小热副本 | minReplicas=1-3 | 应对冷启动 |
| 快扩慢缩 | polling=5s, cooldown=300s | 避免抖动 |
| 多指标组合 | queue + TTFT + GPU | 综合判断 |
| 定时预热 | Cron trigger | 高峰前预热 |
| 模型预拉取 | 镜像分层 + 本地缓存 | 加速冷启动 |
| 节点弹性 | Karpenter | 节点级预备 |
Pod 扩容需要节点资源。如果集群节点不够,Pod 会 Pending。Cluster Autoscaler(CA) 与 Karpenter 是 K8s 的节点级扩缩工具:
对 AI 场景,Karpenter 通常更合适——它能根据 GPU Pod 的需求(型号、显存、拓扑)选择最优实例,扩容速度快。但 GPU 实例的云供应常常紧张,节点级扩容可能要数分钟(等待云厂商分配),这是 AI 弹性的另一瓶颈。
| 工具 | 来源 | 特点 | 适合 |
|---|---|---|---|
| Cluster Autoscaler | K8s 官方 | 标准兼容、稳定 | 通用场景 |
| Karpenter | AWS 开源 | 快速、灵活、智能选实例 | AI、GPU 场景 |
前面讲的都是推理弹性。训练任务(回顾第 5 章)也可以弹性——配合第 3 章 3.4 节的弹性训练与 Spot 实例,训练 Pod 可以动态加入/退出。
混部场景的弹性策略:
这种「训练-推理弹性混部」是大模型团队降本的关键工程实践——把同一批 GPU 在不同时段用于不同负载,利用率从 30% 推到 80%+。第 3 章的优先级抢占与第 8.2 的扩缩容机制,是混部的基础。
落地弹性的几个工程实践:
⚠️ 常见坑:弹性的最大坑是「扩缩震荡」——指标在阈值附近波动,副本数反复增减,Pod 反复创建销毁。解决:调宽阈值(如扩容 70%、缩容 30%)、加 cooldown、用预测式扩缩。
下一节《8.3 大流量稳定性:限流、降级与过载保护》将讲清大流量冲击下的保命策略。