第 16 章 LLM服务架构Triton

第 16 章 服务架构

第 16 章 服务架构

学习目标

  • 掌握主流模型服务器:Triton、TorchServe、KServe、Ray Serve、Seldon;
  • 理解 API 网关、负载均衡、限流、鉴权的职责;
  • 理解自动扩缩容:HPA、KEDA、自定义指标;
  • 理解缓存、队列、背压、超时、重试的可靠性原语;
  • 掌握灰度、蓝绿、金丝雀发布与 A/B 测试、影子流量、多臂老虎机。

16.1 模型服务器:推理引擎的工程外壳

第 14 章的推理引擎(vLLM/TensorRT-LLM)解决「算得快」;模型服务器(Model Server)在它外面套上「多模型管理、动态批处理、版本切换、指标暴露」的工程外壳。

服务器 定位 特点
Triton(NVIDIA) 通用高性能推理 多框架、动态批处理、多模型并发、GPU 利用率标杆
TorchServe PyTorch 官方 与 PyTorch 生态一体、模型打包
KServe K8s 原生 与 K8s 深度集成、自动扩缩、Serverless
Ray Serve 弹性 Python 服务 编排灵活、与 Ray 生态一体、多模型/多版本
Seldon K8s 模型部署 灰度、多框架、监控集成

选型逻辑:GPU 极致性能用 Triton;K8s 平台一体化用 KServe;Python 编排与多模型灵活路由用 Ray Serve;PyTorch 单模型快速上线用 TorchServe。LLM 场景常见组合是 vLLM(引擎)+ KServe/Ray Serve(平台)。

# Ray Serve 快速暴露一个 LLM 服务(多模型路由示例)
from ray import serve
from transformers import pipeline

@serve.deployment
class LLM:
    def __init__(self, model_id):
        self.pipe = pipeline("text-generation", model=model_id)
    async def __call__(self, request):
        return self.pipe(request.query["prompt"])[0]["generated_text"]

serve.run(LLM.bind("Qwen/Qwen2-7B"))

16.2 API 网关、负载均衡、限流、鉴权

服务前面还有一层网关(Gateway),四个职责:

  • 鉴权(AuthN/AuthZ):API Key / OAuth / 租户鉴权。LLM 场景还有内容鉴权(提示注入、敏感内容拦截,第 23 章);
  • 负载均衡:把请求分发到推理副本。LLM 特有的坑:KV Cache 与并发不均——均衡要考虑每实例的当前负载(显存余量、排队长度),而非简单的轮询;
  • 限流(Rate Limit):按 token 或请求数限制,防打爆与防滥用。LLM 的限流粒度是 token/min + 并发数(不只请求数);
  • 路由(第 15 章)+ 可观测性埋点(第 21 章)。
# 网关限流示例(token 粒度)
limits:
  - key: "api_key"
    actions: [{ "type": "request-count", "limit": 1000, "window": "1h" }]
    # LLM 场景再加 token 总量限制
  - key: "api_key"
    actions: [{ "type": "token-count", "limit": 1_000_000, "window": "1h" }]

16.3 自动扩缩容:HPA、KEDA、自定义指标

在线服务的容量要跟着负载走:

  • HPA(Horizontal Pod Autoscaler):K8s 原生,按 CPU/内存扩缩。对 LLM 是「错误的信号」——推理是 memory-bound(第 14 章),CPU 利用率不能反映真实瓶颈;
  • KEDA(Kubernetes Event-driven Autoscaling):按事件驱动(队列长度、请求数)扩缩,比 HPA 更适合;
  • 自定义指标:按 GPU 利用率 / KV 余量 / 排队长度 / 吞吐 扩缩——LLM 服务的正确信号。
%%{init: {"themeVariables": {"primaryTextColor": "#000000", "textColor": "#000000", "labelColor": "#000000", "nodeTextColor": "#000000", "labelTextColor": "#000000", "scaleLabelColor": "#000000"}}}%%
flowchart LR
    A[GPU 利用率/排队长度] -->|指标| B[KEDA 触发]
    B -->|扩容| C[新增推理副本]
    B -->|缩容| D[回收空闲副本]
    C --> E[负载均衡重新分配]

扩缩容的致命陷阱:LLM 模型加载慢(7B 权重加载 30s+,70B 分钟级)——扩容要「预热」(提前加载模型、提前分配 KV 资源),否则流量尖峰来了副本还没就绪。这就是「Serverless 不适合大模型」的根源之一(第 18 章展开)。

16.4 缓存、队列、背压、超时、重试

服务的可靠性五个原语:

  • 缓存:LLM 场景的缓存分三层——前缀缓存(第 14 章,KV 复用)、语义缓存(相同/相似问题的答案缓存)、结果缓存(确定性请求)。语义缓存用 embedding 相似度命中,能大幅省成本但要注意时效性;
  • 队列:削峰填谷。在线服务队列要短(背压),离线服务队列可深(第 15 章);
  • 背压(Backpressure):下游处理不过来时,上游要「慢下来」而非无限堆积。HTTP 层体现为限流 + 503;队列层体现为拒绝或降级;
  • 超时(Timeout):LLM 的生成时长波动大(长输出慢),超时设置要在「容忍长输出」与「防止拖死」间权衡——通常用「首 token 超时 + 每 token 空闲超时」双阈值;
  • 重试(Retry):网络抖动重试(指数退避 + 抖动);但对确定性模型输出要小心幂等性;重试会放大下游压力,要限制重试次数。
# LLM 服务调用的超时与重试(指数退避)
def call_with_retry(fn, max_retries=3, base_delay=0.5):
    for attempt in range(max_retries):
        try:
            return fn(timeout=30)          # 30s 总超时
        except TimeoutError:
            if attempt == max_retries - 1: raise
            time.sleep(base_delay * (2 ** attempt) + random.uniform(0, 0.2))

16.5 发布策略:灰度、蓝绿、金丝雀

模型发布与代码发布不同——新模型可能更差(离线指标好不等于在线好,第 9 章)。因此发布必须渐进:

策略 做法 优点 缺点
滚动发布 逐个替换实例 简单 出问题影响面渐进但难隔离
蓝绿 新旧两套环境,一键切 回滚极快 双倍资源
金丝雀 新模型只接小流量 风险最小、可灰度观察 观察周期长
A/B 测试 随机分流量比业务指标 统计可信 需实验设计
%%{init: {"themeVariables": {"primaryTextColor": "#000000", "textColor": "#000000", "labelColor": "#000000", "nodeTextColor": "#000000", "labelTextColor": "#000000", "scaleLabelColor": "#000000"}}}%%
flowchart LR
    A[新模型 v2] -->|5% 流量| B[金丝雀观察
延迟/质量/业务] B -->|达标| C[50% 流量] C -->|达标| D[全量] B -->|异常| E[回滚到 v1] C -->|异常| E

影子流量(Shadow Traffic):新模型接收线上真实请求的「副本」,只推理不返回用户,用来在零风险下观察新模型在生产流量上的表现(不占用户结果)。多臂老虎机(MAB):自动根据实时反馈动态分配流量给表现更好的模型,是 A/B 的自动化演进。

16.6 LLM 发布的特殊注意点

  • Prompt 与模型强耦合:新模型可能需要新 Prompt 模板(第 17 章 Prompt 版本化)——灰度时新旧 Prompt 要一起管;
  • 评估先于发布:第 9 章的在线评估(影子 + 小流量)是发布门禁;
  • 回滚 = 换权重,但 KV/缓存要同步:语义缓存、前缀缓存带模型版本标签,避免新模型命中旧缓存;
  • 成本突变:新模型可能更长/更贵,灰度期间要盯 token 成本(第 17、21 章)。

本章要点回顾

  1. 模型服务器是推理引擎的工程外壳;Triton 性能标杆、KServe K8s 原生、Ray Serve 编排灵活。
  2. 网关四职责:鉴权、负载均衡(按负载非轮询)、限流(token 粒度)、路由。
  3. 扩缩容信号要用 GPU/排队长度,不是 CPU;LLM 扩容要预热,Serverless 对 70B 不友好。
  4. 可靠性五原语:缓存(三层)、队列、背压、超时(双阈值)、重试(限次)。
  5. 发布必须渐进:金丝雀最小风险;影子流量零风险观察;MAB 是自动化 A/B。
  6. LLM 发布的独特点:Prompt 耦合、缓存带版本、盯成本。

习题

  1. 你的服务在流量尖峰时扩容,但新副本 2 分钟内不可用(模型加载慢)。设计预热方案。
  2. 为什么 LLM 用「首 token 超时 + 每 token 空闲超时」而不是单一总超时?
  3. 设计一个金丝雀发布流程:流量比例、观察指标、自动回滚条件、与语义缓存的协调。
  4. 语义缓存的命中会引入「模型版本不一致」风险,如何设计缓存键与版本策略?

延伸阅读

  • NVIDIA Triton Inference Server 文档
  • KServe / Seldon 官方文档
  • Ray Serve 文档
  • Kubernetes HPA / KEDA 文档
  • 各云厂商模型网关(API Gateway for LLM)白皮书

下一章预告

第 17 章 LLM 服务专题:Prefill/Decode/KV 管理的服务化、多租户隔离与配额、Token 成本与 GPU 利用率、RAG/Agent/工具调用服务、流式与结构化输出。