跳到主要内容
推理优化

10.3 自动扩缩容与负载均衡:多副本下的请求分发

HPA/KEDA 扩缩容策略(CPU/QPS/队列/KV缓存)、前缀感知路由与缓存感知负载均衡、API Key 认证与安全加固

HPAKEDA负载均衡前缀感知路由扩缩容安全

单副本服务不了真实流量。多副本之后出现两个新问题:副本数怎么跟着负载变(扩缩容)请求怎么分到副本(负载均衡)。LLM 服务的特殊之处:副本不是无状态的——每个副本有自己的 KV 缓存,请求分错了地方,缓存全 miss。这一节解决这两个问题,外加一个绕不开的安全话题。

📑 目录


1. 扩缩容:HPA 与 KEDA

1.1 HPA:K8s 内置

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: vllm-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: vllm
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 80   # CPU 利用率(vLLM 场景有坑,见第 2 节)

1.2 KEDA:指标驱动扩缩容

KEDA 支持任意 Prometheus 指标,是 LLM 服务的推荐方案:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: vllm-keda
spec:
  scaleTargetRef: { name: vllm }
  minReplicaCount: 1
  maxReplicaCount: 10
  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.monitoring.svc:9090
      query: |                       # 按队列长度扩缩容
        vllm:num_requests_waiting / (vllm:num_requests_running + vllm:num_requests_waiting)
      threshold: "0.5"

📌 关键点:HPA 与 KEDA 的差别是指标源:HPA 只认 K8s 内置指标(CPU/内存/自定义),KEDA 能直接吃 Prometheus 查询——LLM 场景的扩缩容信号(队列深度、KV 水位)都是 Prometheus 指标,KEDA 是正解。Production Stack 也内置了 KEDA 支持。


2. LLM 扩缩容的正确指标:为什么 CPU 不够

2.1 CPU 指标的失效原因

指标LLM 场景的问题
CPU 利用率算力在 GPU——CPU 满载前 GPU 早就饱和了;且长请求占着 GPU 但 CPU 空闲(等待中)
内存分配后基本不变,反映不了负载
QPS有用,但区分不了”大请求”(长 prompt 长输出)与”小请求”

2.2 LLM 场景的有效信号(按优先级)

信号PromQL含义
队列深度num_requests_waiting / (running + waiting)排队比 > 阈值 → 扩容(最直接)
KV 缓存水位kv_cache_usage_perc > 0.9容量打满 → 扩容/降负载
TTFT 分位数TTFT p95 超 SLO服务变慢 → 扩容(SLO 驱动,最稳)
请求量rate(prompt_tokens[5m])负载预测(配合 cron 扩缩容)

2.3 扩缩容的两个陷阱

陷阱 1:模型加载 1-10 分钟 → 扩容来不及
  对策:预留"预热副本"(minReplicas 覆盖日常高峰)+ 预下载(10.1 第 4 节)
        KEDA 支持"按时刻表预扩容"(cron 触发器),午高峰前 10 分钟提前扩

陷阱 2:缩容太快 → 缓存全丢
  对策:缩容冷却期拉长(KEDA cooldownPeriod,默认 300s)
        销毁前优雅排空(terminationGracePeriodSeconds 覆盖正在跑的请求)

📌 关键点LLM 扩缩容的本质是”提前量”博弈:扩容有 1-10 分钟加载延迟,缩容会丢 KV 缓存。所以生产策略是慢扩慢缩:KEDA 触发条件用”持续 N 分钟超过阈值”,cooldown 给足,配合 cron 预扩容覆盖已知高峰。


3. 负载均衡:从随机到缓存感知

3.1 普通 LB 的问题

普通 LB(round_robin / random / 最小连接):
  请求均匀分散到所有副本
  → 相同 System Prompt 的请求落在不同副本 → 每个副本都 miss → 前缀缓存 0% 命中
  → 全量 prefill,TTFT 高企,吞吐打折

3.2 vLLM 官方 router:缓存感知负载均衡

vLLM 官方 router 项目(Production Stack 的默认路由)提供 5 种策略:

策略特点适用
round_robin顺序分发通用、最均匀
random随机选择简单场景
consistent_hash同一 session/用户 → 同一副本(会话亲和)多轮对话、KV 复用
power_of_two随机挑 2 个取负载小的负载敏感场景
cache_aware按缓存命中优化(前缀感知)重复 prompt、few-shot 场景

3.3 一致性哈希(consistent_hash)的价值

多轮对话场景:同一用户连续请求命中同一副本 → 该副本保留其对话 KV 缓存 → 后续轮次跳过 prefill。

缺点:副本缩容 → 哈希环变化 → 缓存失效
对策:hash key 用"用户 ID 的稳定部分";缩容尽量整体替换而非逐个

💡 提示负载均衡策略与业务形态强相关——对话服务用 consistent_hash,RAG(重复大前缀)用 cache_aware,纯吞吐压测用 round_robin。默认值没有最优,先测量请求的前缀分布再选。


4. 前缀感知路由:把同类请求导到同一副本

4.1 原理

前缀感知路由(prefix-aware routing):
  按请求 prompt 的前缀哈希(如 64 token 前缀)路由
  → 相同前缀的请求 → 同一副本 → 该副本缓存直接命中

  效果示例(Production Stack 官方教程):
  第 1 个请求   → 副本 A(冷,全 prefill)
  第 2 个请求(同前缀)→ 副本 A(缓存命中,仅 decode)← 日志可见 "cache hit"

4.2 什么时候有效

场景前缀重复度收益
聊天应用(固定 System Prompt)
RAG(共享文档前缀)中高
代码补全(文件头重复)
随机 prompt 压测

4.3 与单副本前缀缓存的关系

单副本内:--enable-prefix-caching(v0.26 默认开)处理请求间共享前缀。 多副本:普通 LB 把同前缀请求打散 → 单副本缓存失效 → 前缀感知路由是”跨副本”层面的缓存协同

两者是分层关系:路由层保证同前缀进同副本,副本内前缀缓存保证命中。缺一层都白搭。

📌 关键点多副本 + 前缀缓存的完整链路 = 前缀感知路由 + 副本内 prefix caching。只有副本内缓存没有路由协同,缓存命中率被副本数稀释(N 副本,同前缀请求 1/N 概率同副本);只有路由没有副本内缓存,命中也无处可落。


5. 安全加固:别让 API 裸奔

5.1 现状与风险

vLLM 的 OpenAI 兼容服务默认无认证。直接暴露公网 = 开放算力(被薅羊毛)+ 数据泄露(prompt 全裸)+ 潜在 DoS(无限长请求)。

5.2 加固清单(按优先级)

层级措施说明
网关层API Key 校验网关(Envoy/nginx/自研)校验 Authorization: Bearer <key>
网关层TLS 终结HTTPS 终结在网关,后端内网 HTTP
网络层网络隔离服务只在内网可达;公网只暴露网关
vLLM 层--api-keyvLLM 内置 API Key 校验(简单场景可用)
应用层配额与限流按 key 限 QPS/token 数(防止单 key 打爆)
应用层请求大小限制max_model_len 上限、max_tokens 上限
# vLLM 内置 API Key(最简方案)
vllm serve meta-llama/Llama-3.1-8B-Instruct --api-key sk-xxx

# 请求必须带
curl -H "Authorization: Bearer sk-xxx" http://localhost:8000/v1/chat/completions ...
# Production Stack 原生支持
servingEngineSpec:
  vllmApiKey: sk-xxx          # 或引用 Secret
  # vllmApiKey: { secretKeyRef: { name: vllm-secret, key: apiKey } }

📌 关键点安全的最低标准 = 网关 API Key + TLS + 内网隔离(三件套)。vLLM 内置 --api-key 只挡”裸奔”,挡不住暴力破解与滥用——配额、限流、审计必须做在网关。


📝 总结

  • 扩缩容:KEDA(Prometheus 指标驱动)优于 HPA(只认 CPU);信号优先级 = 队列深度 > KV 水位 > TTFT SLO > 请求量
  • 两个陷阱:加载慢(cron 预扩容 + 预热副本)、缩容丢缓存(cooldown + 优雅排空)
  • 负载均衡 5 策略:round_robin / random / consistent_hash / power_of_two / cache_aware,按业务形态选
  • 前缀感知路由:与副本内前缀缓存分层协同,缺一不可
  • 安全三件套:网关 API Key + TLS + 内网隔离;配额限流在网关

🎯 自我检验清单

  • HPA 与 KEDA 的本质差别?LLM 场景为什么选 KEDA?
  • 为什么 CPU 利用率不适合做 LLM 扩缩容信号?
  • 扩缩容的两个陷阱与对策?
  • 普通 LB 为什么会让前缀缓存失效?
  • 5 种路由策略各自适用什么业务?
  • 前缀感知路由与副本内缓存的层次关系?
  • 安全加固清单的优先级排序?
  • --api-key 能解决什么问题、不能解决什么?

📚 参考资料