GPU 工作负载的扩容,往往不是把副本数从 2 调到 10 那么简单。节点启动、GPU 资源调度、镜像拉取和模型加载都需要时间。如果等队列已经堆积、GPU 利用率已经飙高,扩容动作可能已经来不及了。
一次生产事故很能说明问题:服务不是逐渐变慢,而是在流量上升后直接崩溃;大量 Pod 处于 Pending,用户错误率达到 15%~20%。这类事故的关键通常不是“有没有 autoscaling”,而是 autoscaler 是否提前为 GPU 工作负载争取了启动时间。
GPU 服务为什么更适合“提前扩容”
传统 HPA 通常根据 CPU 或内存利用率做反应式扩容。对普通 Web 服务,这种方式往往够用;对 GPU 推理服务,却可能存在几个明显延迟:
- 指标采集本身有窗口,流量变化不会立即反映到扩容决策中。
- 新 Pod 需要等待 GPU 节点有可用资源。
- GPU 节点可能需要从云平台启动,节点加入集群还要额外耗时。
- 容器启动后还要加载模型、建立 CUDA 上下文并完成预热。
- 当请求已经进入队列时,新增副本仍需要一段时间才能真正处理请求。
因此,扩容目标不应只是“当前 GPU 利用率低于某个阈值”,而应回答一个更实用的问题:
按照未来几分钟可能到达的请求量,当前容量是否足以覆盖服务启动和模型加载时间?
预测式扩缩容并不意味着一定要部署复杂的机器学习预测平台。一个可落地的起点,是用历史流量、队列长度和近期增长速度生成一个预测指标,再交给 Kubernetes 现有的 HPA、KEDA 或自研控制器执行扩容。
预测指标应该长什么样
可以把未来窗口内的请求量预测为 predicted_requests_5m,再结合单个副本的处理能力计算所需副本数:
required_replicas = ceil(predicted_requests_5m / capacity_per_replica_5m)
如果模型加载和节点启动总共需要 3 分钟,那么预测窗口至少应覆盖这段时间,并留出安全余量。例如:
forecast_window = startup_time + model_warmup_time + safety_margin
生产环境中的预测信号可以来自以下数据:
- API 网关的请求速率和请求类型。
- 推理队列长度、排队时间和超时数量。
- 每个模型版本的平均处理能力。
- 工作日、小时、活动日等周期性流量模式。
- 当前正在启动、Pending 或不可调度的 GPU Pod 数量。
预测指标必须有边界。预测偏低会导致扩容太晚,预测偏高则会浪费昂贵的 GPU。实践中通常需要设置最小副本数、最大副本数、增长速率限制,以及预测失败时的反应式兜底策略。
用 Kubernetes HPA 消费预测指标
下面是一个可以改造的最小示例。假设 Prometheus Adapter 或其他 external metrics adapter 已经向 Kubernetes 暴露了名为 predicted_queue_per_replica 的外部指标。该指标表示:在预测窗口内,每个副本预计需要承担的队列工作量。
在运行前需要替换镜像地址、GPU 数量和指标适配器配置。示例中的预测指标生成方式属于实践假设,不是 Kubernetes 内置能力。
apiVersion: apps/v1
kind: Deployment
metadata:
name: gpu-inference
labels:
app: gpu-inference
spec:
replicas: 2
selector:
matchLabels:
app: gpu-inference
template:
metadata:
labels:
app: gpu-inference
spec:
nodeSelector:
accelerator: nvidia
containers:
- name: server
image: registry.example.com/gpu-inference:2025-01
ports:
- name: http
containerPort: 8080
resources:
requests:
cpu: 2
memory: 8Gi
nvidia.com/gpu: 1
limits:
cpu: 4
memory: 16Gi
nvidia.com/gpu: 1
readinessProbe:
httpGet:
path: /ready
port: http
periodSeconds: 5
failureThreshold: 12
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: gpu-inference
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: gpu-inference
minReplicas: 2
maxReplicas: 20
behavior:
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 60
- type: Pods
value: 4
periodSeconds: 60
selectPolicy: Max
scaleDown:
stabilizationWindowSeconds: 600
policies:
- type: Percent
value: 25
periodSeconds: 60
metrics:
- type: External
external:
metric:
name: predicted_queue_per_replica
target:
type: AverageValue
averageValue: 20
应用配置前,可以用下面的命令检查集群是否能够读取外部指标:
kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1/namespaces/default/predicted_queue_per_replica" | jq .
kubectl apply -f gpu-inference-autoscaling.yaml
kubectl describe hpa gpu-inference
kubectl get pods -l app=gpu-inference -w
如果 external metrics API 返回 404,HPA 不会凭空产生预测能力,需要先配置指标适配器和对应的查询规则。指标值还必须与 HPA 的目标语义一致:如果它代表“每个副本的预测队列量”,使用 AverageValue;如果它代表整个服务的总量,则应重新设计指标或计算方式。
预测控制器和反应式兜底要分工
预测器不应该成为唯一的安全网。更稳妥的架构是“双通道”:
- 预测通道:根据未来窗口的流量或队列预测,提前提高
minReplicas或直接推动期望副本数。 - 反应通道:根据当前队列长度、请求延迟、错误率和 GPU 利用率,在预测失败或突发流量下快速扩容。
两条通道应使用同一个最大副本数和资源预算,避免预测器与 HPA 相互覆盖。对于节点供应也要单独考虑:Pod 副本数增加并不代表 GPU 节点已经可用。集群自动扩容器需要识别 nvidia.com/gpu 请求,并且云平台配额、节点组上限和镜像缓存都要纳入容量规划。
一个简单的预测任务可以先从移动平均开始,而不是立即引入复杂模型。下面的伪代码展示了指标生成逻辑;它假设已经能够读取过去若干分钟的请求速率,并把结果写入 Prometheus Pushgateway 或自定义指标服务。
from math import ceil
from statistics import mean
# 假设:samples 是过去 15 分钟、每分钟一个请求数样本
samples = [820, 860, 910, 980, 1040, 1100, 1180, 1230]
capacity_per_replica_per_minute = 180
forecast_minutes = 5
safety_factor = 1.25
recent_rate = mean(samples[-5:])
forecast_requests = recent_rate * forecast_minutes * safety_factor
required_replicas = ceil(forecast_requests / (capacity_per_replica_per_minute * forecast_minutes))
print({
"recent_rate_per_minute": round(recent_rate, 2),
"forecast_requests": round(forecast_requests),
"required_replicas": required_replicas,
})
这段代码只是可运行的计算示例,不是完整的生产预测器。生产实现还需要处理时间序列缺失、异常流量、不同模型的容量差异、预测置信区间和指标写入认证。
不要只盯着副本数:GPU 扩容的验证指标
扩缩容上线后,至少要同时观察以下指标:
- 从扩容决策到新 Pod Ready 的时间。
- Pod 从 Pending 到获得 GPU 的时间。
- 模型加载和预热耗时。
- 请求排队时间的 P50、P95 和 P99。
- GPU 利用率、显存使用率和每个副本的吞吐量。
- 预测值与实际请求量之间的误差。
- 扩容期间的错误率、超时率和被驱逐 Pod 数量。
可以通过历史回放验证预测策略:把过去一次流量尖峰重新输入控制器,比较“只使用反应式 HPA”和“预测加反应式兜底”两种方案的扩容时间、GPU 成本与错误率。不要只看平均利用率;如果 P99 延迟和队列长度改善了,即使 GPU 平均利用率略有下降,也可能是合理的容量交换。
上线前的检查清单
- 预测窗口覆盖节点启动、Pod 调度和模型预热的总耗时。
- 预测指标的单位、聚合方式与 HPA 目标完全一致。
- 有明确的
minReplicas、maxReplicas和 GPU 配额。 - 预测失败时,当前队列和错误率仍能触发反应式扩容。
- 缩容设置了足够长的稳定窗口,避免 GPU 副本频繁抖动。
- GPU 节点自动扩容器能够识别 GPU 资源请求。
- readiness 探针只有在模型真正可服务时才返回成功。
- 通过历史流量回放和突发流量压测验证策略。
预测式自动扩缩容的价值,不是让 Kubernetes 猜中每一次流量变化,而是把扩容动作提前到系统仍有余量的时候。对 GPU 服务而言,提前几分钟准备好节点和模型,往往比在错误率出现之后再追赶副本数更便宜,也更可靠。