GPU 工作负载与节点自动扩缩容¶
AI 平台的扩缩容不是一个 HPA。一次流量变化可能依次触发推理副本扩容、Pending Pod、GPU 节点创建、驱动和设备组件就绪、镜像拉取、模型下载以及缓存预热。任何一层缺少时间预算,自动扩容都可能在容量到达前超时。
1. 四层弹性¶
请求层
并发、Token、优先级、排队和拒绝
│
Pod/Workload 层
HPA、KEDA、WVA、训练弹性、队列准入
│
节点层
Cluster Autoscaler、Karpenter、云厂商自动供给
│
容量层
预留、Spot、配额、区域库存和裸金属交付
Kueue 不创建节点,HPA 也不理解 GPU 是否能从云厂商获得。每层要有独立 SLO 和失败原因。
2. 扩容时间线¶
T0 流量或排队增长
T1 Autoscaler 生成新副本
T2 Pod Pending
T3 Node Autoscaler 决定创建节点
T4 云实例获得容量并启动
T5 kubelet、CNI、CSI、GPU Driver/Plugin Ready
T6 镜像和模型下载
T7 GPU 权重加载、图编译和预热
T8 Pod Ready,Gateway 开始送流量
记录每个阶段的时间。只监控 T0 到 T1 会严重低估大型模型的冷启动。
3. 工作负载扩缩容信号¶
传统 HPA¶
CPU 对 LLM 推理通常不是主要瓶颈。HPA 更适合:
- 网关、Tokenizer、Embedding 或 CPU 服务;
- 已通过 Metrics Adapter 暴露的自定义指标;
- 保持至少一个副本的稳定在线服务。
KEDA¶
KEDA 可以根据 Prometheus、消息队列等外部信号生成 HPA,并负责 0 到 1 的激活。适合:
- 批推理队列;
- 异步 Embedding;
- 请求队列或 Token 指标;
- 允许缩到零且能接受冷启动的模型。
推理专用信号¶
优先考虑:
- Waiting Requests / Queue Depth;
- Running Requests 与安全并发;
- Prompt/Generation Token Rate;
- TTFT、TPOT 和拒绝率;
- KV Cache 使用率;
- 模型加载状态;
- 预测的请求持续时间。
只使用 GPU Utilization 容易误判:Decode 可能显存带宽受限但 SM 利用率不高,长 Prompt 又会让 Prefill 出现短时尖峰。
4. 一个 KEDA 思路示例¶
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: llm-server
spec:
scaleTargetRef:
name: llm-server
minReplicaCount: 1
maxReplicaCount: 8
cooldownPeriod: 300
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus.monitoring.svc:9090
metricName: llm_waiting_requests
query: sum(llm_waiting_requests{model="example-model"})
threshold: "8"
指标名是平台契约示例,应根据实际引擎指标和聚合方式调整。扩容前需要处理缺失指标、Prometheus 不可用和高基数问题。
5. Cluster Autoscaler 与 Karpenter¶
| 能力 | Cluster Autoscaler | Karpenter |
|---|---|---|
| 节点来源 | 预定义 Node Group | 根据 NodePool 约束动态选择 Node |
| 云厂商覆盖 | 广 | 取决于 Provider 实现 |
| 节点生命周期 | 主要关注扩缩容 | 同时覆盖供给、过期、漂移和整合 |
| 实例选择 | 从已配置组中选择 | 根据 Pending Pod 和约束选择实例 |
| 适合 | 稳定节点组、多云一致模式 | 云上动态实例和快速迭代节点池 |
Kubernetes 官方将二者都列为 SIG Autoscaling 相关的 Node Autoscaler。具体能力和支持云以目标 Provider 文档为准。
参考:Kubernetes Node Autoscaling
6. GPU NodePool 约束¶
一个 GPU NodePool 至少限制:
- 允许的实例/GPU 型号;
- 区域和可用区;
- On-Demand、Spot 或预留容量;
- 总 GPU/CPU/内存上限;
- 节点 Taint 与平台标签;
- OS/AMI/节点镜像;
- 过期、整合和中断预算;
- DaemonSet 资源开销;
- 本地盘和网络能力。
Karpenter 概念示例:
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: gpu-inference
spec:
limits:
nvidia.com/gpu: 32
disruption:
consolidationPolicy: WhenEmpty
budgets:
- nodes: "1"
template:
spec:
taints:
- key: accelerator
value: gpu
effect: NoSchedule
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
Provider 还需要 NodeClass 等云资源配置,本例不能直接作为完整安装清单。
7. Scale from Zero 的前提¶
Node Autoscaler 必须在节点不存在时仍能推断:
- 节点将提供什么扩展资源;
- 节点标签、Taint 和可用区;
- DaemonSet 会消耗多少 CPU、内存和 Pod Slot;
- DRA Driver/Device Plugin 与自动扩容是否兼容;
- 多节点任务是否需要原子拓扑;
- 云配额和实例库存是否允许供给。
如果自动扩容器看不到未来 GPU 资源,Pending Pod 不会触发节点创建。厂商托管 Kubernetes 常通过节点模板或 NodePool 元数据补充这些信息。
8. 队列与节点供给¶
Kueue 通常只在配额可用时准入 Workload。常见策略:
先准入再扩节点¶
优点:简单,Pending Pod 触发 Node Autoscaler。风险:配额有但云容量不足,已准入任务长期等待。
ProvisioningRequest¶
先由 Kueue/Autoscaler 协调容量供给,再完成准入。适合昂贵、稀缺或需要预留的整组容量,但组件集成和状态机更复杂。
保留最小容量¶
在线推理保留 Warm Pool,训练和批处理再弹性扩容。它牺牲少量空闲成本换取可预测延迟。
必须让用户看见“等待配额”“等待云容量”“等待节点启动”和“等待模型预热”的区别。
9. 多节点任务与原子扩容¶
一个 16 节点任务不能接受只创建 9 个节点后无限等待。需要考虑:
- Gang/All-or-Nothing 准入;
- 云供应商是否能原子获得整组容量;
- 同一网络 Block/Placement Group/TPU Slice;
- 部分容量失败后的释放;
- 超时后是否换区域、型号或队列;
- 创建后未及时启动任务的成本泄漏。
对于 TPU Slice、UltraServer 或高带宽网络域,节点数量和拓扑是同一个资源请求的一部分。
10. 缩容与中断¶
节点缩容不应只看“GPU 利用率低”。先判断工作负载是否可安全移动:
- 在线推理是否有其他 Ready 副本;
- 模型缓存重建需要多久;
- 训练是否完成 Checkpoint;
- 本地 NVMe 是否只有唯一数据;
- PDB、Kueue 和终止宽限期是否一致;
- DRA Claim、RDMA 连接和多机组如何释放;
- Canary/发布期间是否允许扰动。
在线 GPU 节点建议使用保守整合策略,批处理节点可以在 Job 完成后更积极地缩容。
11. Spot 与抢占¶
适合 Spot:
- 有可靠 Checkpoint 的训练;
- 可重试的批推理和数据处理;
- 低优先级实验;
- 可以跨型号或区域迁移的任务。
不适合仅依赖 Spot:
- 没有冗余的在线模型;
- 长时间无法保存状态的训练;
- 严格拓扑且很难重新获得整组容量的任务;
- 冷启动远大于平均可用窗口的工作负载。
中断通知要同时触发停止准入、Checkpoint/Drain 和指标记录。
12. 防止弹性控制器打架¶
典型冲突:
- HPA 扩副本,KEDA 同时管理同一个 Scale Target;
- VPA 重建 Pod,影响推理可用性;
- Karpenter 整合节点,Kueue 刚准入训练;
- Canary 流量变化触发 HPA,破坏实验比例;
- Prefill/Decode 两个 Autoscaler 失去平衡;
- Model Cache DaemonSet 尚未完成,Pod 已被判定 Ready。
每个可扩对象只能有一个最终写入者。不同控制器之间通过明确指标和保护窗口协调。
13. 容量保护¶
建议分层:
当生产流量上升时,应先停止训练借用或新任务准入,而不是直接抢占已加载但低流量的推理副本。
14. 必须监控的指标¶
| 层级 | 指标 |
|---|---|
| 请求 | 到达率、排队、拒绝、TTFT/TPOT |
| Pod | Desired/Ready、扩容决策、Pending 原因 |
| 队列 | Admission、等待时间、配额、Flavor |
| 节点 | Provision 时间、失败、NotReady、整合 |
| 模型 | 下载、加载、预热和缓存命中时间 |
| 云容量 | 配额、库存不足、Spot 中断、价格 |
| 成本 | 空闲时间、冷启动浪费、单位 Token/Job 成本 |
端到端扩容 SLO 应从信号产生算到新增 Ready 容量,而不是只统计 Autoscaler Reconcile。
15. 故障模式¶
| 现象 | 常见原因 |
|---|---|
| HPA 已扩容但没有新容量 | GPU Pod Pending,节点未创建 |
| 节点创建后 Pod 仍 Pending | DaemonSet 开销、标签/Taint、Device Plugin 未就绪 |
| Pod Running 但未接流量 | 模型下载/加载、Readiness 或 Gateway 状态 |
| 扩缩容来回震荡 | 指标延迟、阈值过近、冷却不足 |
| 无法缩容 | PDB、本地数据、长训练、do-not-disrupt |
| 选择错误 GPU 型号 | NodePool 约束太宽或平台标签不一致 |
| 成本异常增长 | 创建后未准入、模型加载失败、整合被永久阻止 |
16. 生产检查清单¶
- 已记录从扩容信号到模型 Ready 的完整时间线。
- 推理扩容基于队列、Token 或延迟,而不是只看 CPU。
- Node Autoscaler 能识别未来 GPU 节点的资源和标签。
- Kueue 准入与节点容量不足有不同状态和告警。
- 多节点任务不会只供给部分容量后无限占用。
- 在线推理有 Warm Pool 和故障冗余。
- 缩容尊重 Checkpoint、PDB、本地缓存和 DRA Claim。
- Spot 中断路径经过真实演练。
- 每个 Scale Target 只有一个最终控制器。
- 端到端扩缩容 SLO 和成本可以持续回归。