4. 负载预测与监控:监控指标、日志与指标采集、负载预测方法
自动扩缩容的本质是“用过去的观测预测未来的需求,并提前调整资源”。如果只依赖实时指标做反应式扩缩容,Pod 启动、模型加载、GPU 显存分配往往需要数十秒甚至数分钟,流量尖峰到来时资源早已被打满,产生大量超时与排队。因此,可靠的负载预测与监控体系是分布式 AI 推理服务自动扩缩容的“前哨”。本章聚焦三个核心问题:监控哪些指标、如何采集这些指标、以及如何基于历史数据预测未来负载。
4.1 核心监控指标:QPS、GPU 利用率、队列长度
AI 推理服务的负载特征与普通 Web 服务有显著差异:请求不仅包含网络开销,还涉及 GPU 计算、显存占用、批处理(batching)效果等。因此,监控指标必须覆盖流量入口、计算资源、排队状态三个层面。
| 指标类别 | 指标名称 | 典型采集方式 | 扩缩容关联性 |
|---|---|---|---|
| 流量入口 | QPS(Queries Per Second) | 网关(如 Nginx、Envoy)或服务自身埋点 | 直接驱动副本数增减 |
| 流量入口 | 请求延迟(P50/P95/P99) | 服务端中间件统计 | 延迟升高可能预示资源不足 |
| 计算资源 | GPU 利用率(%) | DCGM(NVIDIA Data Center GPU Manager) | 利用率高说明算力紧张 |
| 计算资源 | GPU 显存占用(MB) | DCGM 或 nvidia-smi | 显存不足会导致 OOM 或拒绝调度 |
| 排队状态 | 队列长度(Queue Depth) | 服务内部计数器或消息队列指标 | 队列积压是过载的早期信号 |
| 排队状态 | 批处理大小(Batch Size) | 推理引擎(如 Triton、vLLM)暴露 | 批处理大小影响 GPU 吞吐效率 |
需要特别强调的是,单一指标不足以准确判断扩缩容时机。例如,QPS 很高但请求均为短文本分类,GPU 利用率可能只有 30%,此时盲目扩容会浪费资源;反之,QPS 不高但请求为长序列生成(如 LLM 推理),GPU 利用率可能已接近 100%,此时需要扩容。因此,推荐将 QPS、GPU 利用率、队列长度三者联合作为扩缩容的触发条件。
4.2 日志与指标采集:从埋点到可视化
监控数据的采集链路通常分为四层:埋点 → 暴露 → 抓取 → 存储/展示。对于 AI 推理服务,需要同时采集系统级指标和应用级指标。
4.2.1 系统级指标采集
GPU 指标必须通过专用工具采集,不能依赖 CPU 监控工具。推荐使用 DCGM 配合 Prometheus 的 dcgm-exporter 组件。部署后,可通过以下方式验证采集是否正常:
# 查看 dcgm-exporter 暴露的指标
curl http://<node-ip>:9400/metrics | grep -E "DCGM_FI_DEV_GPU_UTIL|DCGM_FI_DEV_FB_USED"
# 输出示例(节选)
DCGM_FI_DEV_GPU_UTIL{gpu="0",uuid="GPU-xxx"} 87.5
DCGM_FI_DEV_FB_USED{gpu="0",uuid="GPU-xxx"} 14260
4.2.2 应用级指标采集
QPS 和队列长度属于应用级指标,需要在推理服务内部埋点。以 Python 编写的 FastAPI 服务为例,可使用 Prometheus 客户端库暴露自定义指标:
from prometheus_client import Counter, Gauge, Histogram, start_http_server
# 定义指标
REQUESTS = Counter('inference_requests_total', 'Total inference requests')
QUEUE_DEPTH = Gauge('inference_queue_depth', 'Current queue depth')
LATENCY = Histogram('inference_latency_seconds', 'Request latency')
@app.post("/predict")
async def predict(request: Request):
REQUESTS.inc()
QUEUE_DEPTH.inc()
start = time.time()
try:
result = model.predict(request.body)
return result
finally:
LATENCY.observe(time.time() - start)
QUEUE_DEPTH.dec()
采集端建议使用 Prometheus 作为指标存储,Loki 或 ELK 作为日志存储。日志与指标需要关联:例如,当队列长度超过阈值时,应能通过 trace ID 或 request ID 检索到对应的请求日志,以便定位是哪个模型、哪类输入导致了积压。
4.3 负载预测方法:从简单阈值到时间序列模型
负载预测的目标是提前一个决策周期(通常 1~5 分钟)预判未来的 QPS 或 GPU 利用率,从而让扩缩容动作“走在流量前面”。根据业务场景的复杂度,可选择不同层级的预测方法。
4.3.1 方法一:基于历史均值的滑动窗口预测
最简单的方法是取过去 N 个时间窗口的 QPS 平均值作为未来预测值。该方法适用于负载平稳、周期性不强的场景。
# 伪代码:滑动窗口平均预测
window_size = 10 # 10个采样点,每个采样点间隔15秒
predicted_qps = sum(history_qps[-window_size:]) / window_size
优点:实现简单,计算开销为零。缺点:无法捕捉趋势和突发,对尖峰反应滞后。
4.3.2 方法二:基于时间序列的指数平滑(Holt-Winters)
当负载具有明显的日周期或周周期规律时(例如白天高、夜间低),可使用 Holt-Winters 指数平滑法,它同时考虑水平、趋势、季节性三个分量。公式如下:
预测值 = (水平分量 + 趋势分量 × 预测步数) × 季节性分量
# 水平分量更新
level = α × (当前观测值 / 季节性分量) + (1 - α) × (level + trend)
# 趋势分量更新
trend = β × (level - 上一周期level) + (1 - β) × trend
# 季节性分量更新
season = γ × (当前观测值 / level) + (1 - γ) × season
在 Python 中可使用 statsmodels 库快速实现:
from statsmodels.tsa.holtwinters import ExponentialSmoothing
model = ExponentialSmoothing(
train_qps,
seasonal_periods=24*4, # 假设每15分钟一个点,一天96个点
trend='add',
seasonal='add'
).fit()
forecast = model.forecast(steps=4) # 预测未来1小时
4.3.3 方法三:基于机器学习的回归预测
当负载受多个因素影响(如时间、上游调用量、营销活动、模型版本发布)时,可使用梯度提升树(如 XGBoost、LightGBM)或简单的 LSTM 模型。特征工程是关键:
- 时间特征:小时、星期几、是否节假日
- 历史特征:过去 1 小时 QPS 均值、过去 24 小时同时刻 QPS
- 外部特征:上游服务调用量、当前在线用户数
import xgboost as xgb
# 特征矩阵:X = [hour, weekday, is_holiday, qps_lag_1h, qps_lag_24h]
model = xgb.XGBRegressor(n_estimators=200, max_depth=6)
model.fit(X_train, y_train)
predicted_qps = model.predict(X_test)
4.3.4 方法对比与选型建议
| 方法 | 适用场景 | 预测提前量 | 计算开销 | 对突发流量适应能力 |
|---|---|---|---|---|
| 滑动窗口平均 | 负载平稳 | 1~2 个窗口 | 极低 | 弱 |
| Holt-Winters | 有明显周期性 | 可预测多个周期 | 低 | 中 |
| XGBoost/LSTM | 多因素影响、非线性 | 30 分钟以上 | 中高(需训练) | 中(依赖特征) |
4.4 案例:基于预测的扩缩容实战
某 LLM 推理服务部署在 4 张 A100 GPU 上,业务方反馈每天上午 10:00~11:00 和下午 14:00~15:00 会出现明显的请求高峰,而夜间负载极低。原先使用基于 CPU 利用率的 HPA,导致 GPU 利用率长期低于 40%,且高峰时请求排队严重。
优化方案:
- 接入 DCGM 采集 GPU 利用率,并在服务内埋点统计 QPS 和队列长度。
- 使用 Holt-Winters 模型预测未来 5 分钟的 QPS,预测周期为 15 秒一个点。
- 配置 KEDA 触发器,同时监听预测 QPS 和实时队列长度:
# KEDA ScaledObject 配置示例
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus:9090
query: |
sum(predict_linear(inference_requests_total[1m], 300))
threshold: "200" # 预测 QPS 超过 200 时扩容
- type: prometheus
metadata:
serverAddress: http://prometheus:9090
query: inference_queue_depth
threshold: "50" # 队列长度超过 50 时立即扩容
效果:扩容动作提前约 3 分钟触发,高峰期间 GPU 利用率提升至 75% 以上,P95 延迟从 2.8 秒降至 1.2 秒,夜间副本数自动缩至 2 个,整体资源成本下降约 35%。
4.5 误区与复盘
复盘要点:在实施负载预测后,应建立“预测值 vs 实际值”的对比监控看板,并记录每次扩缩容决策的触发指标。当出现误扩容(扩容后负载下降)或漏扩容(预测不足导致超时)时,回溯对应时间点的指标和日志,持续迭代预测策略。