打包好的模型,接下来要问的是:用什么跑起来、怎么对外提供服务。这一步最容易走偏的,不是记错产品名,而是把职责不同的东西放在一起比较。一次计算怎么在 GPU 上跑完、请求怎么接进来、集群里副本怎么扩,其实是三件独立的事。有的产品会跨层——引擎顺便带一个 HTTP 服务,编排声明也能拉起一套推理进程——名字叠在一起了,职责并没有合并。不先把层分清,后面无论换成哪家实现,都还是鸡同鸭讲。
打个比方:运行环境是发动机,服务框架是柜台,编排平台是连锁门店。 发动机决定算得多快,柜台决定怎么接待客人、怎么把零散的小单攒成一锅,门店网络决定开几家分店、新店怎么灰度。三者是叠着用的,不是三选一。
运行环境管算,服务框架管接,编排平台管活。 分清层,再在每一层里填实现。
Table of contents
Open Table of contents
一、先立三层
选型混乱,是因为算、接、活被当成同一件事。拆开之后,产品名才有地方放:
| 层 | 管什么 | 改什么算动这一层 | 可以没有吗 |
|---|---|---|---|
| 运行环境 | 一次计算怎么在硬件上走完 | 换卡、换精度、换 IR / engine | 不能 |
| 服务框架 | 请求怎么进、怎么攒批、超时怎么降级 | 改协议、排队、batch 窗口 | 模型很轻时可以内嵌,不单独起服务 |
| 编排平台 | 几个副本、流量怎么切 | 改副本、HPA、金丝雀 | 「模型专用那一层」可以没有;进程总得有人拉起来 |
接口也很窄:运行环境吃张量、吐张量;服务框架对外是 RPC;编排对外是稳定入口 + 副本 + 流量比例。很多 LLM 引擎自己既算又接请求——那是把前两层装进同一进程,不是服务框架消失了。
所以「vLLM 和 Triton 哪个好」没有答案:一个常占运行环境+服务框架,一个是服务框架,肚子里还可以再挂运行环境。先问各占哪层,再比同层。
二、运行环境:怎么算
运行环境只关心计算。训练时 model(x) 能出数,不等于线上有可部署的运行环境。线上要执行计划稳定、精度确定、显存能事先估出来。
一次前向和 LLM 不能当同类引擎做容量规划:
| 一次前向(CTR / embedding / CV) | 自回归(LLM) | |
|---|---|---|
| 批处理 | 多条拼成更大的矩阵运算 | 生成过程中插入新请求(连续批处理) |
| 显存 | 权重 + 激活 | 权重 + KV cache(随并发 × 上下文涨) |
| 和服务框架 | 可以分开:引擎当库 | 几乎总是绑在一起 |
| 容量怎么估 | QPS × 单次耗时 | 先问 KV 还塞得下多少人 |
选这一层,先定取向,再填名字(和上一篇的格式对上:ONNX 吃可移植运行环境,engine 吃编译型,GGUF 吃端上):
| 取向 | 填什么 | 别用在 |
|---|---|---|
| 可移植,CPU / GPU 都要跑 | ONNX Runtime;Intel 上可看 OpenVINO | 要单卡打满 Tensor Core 的深度模型 |
| 单卡峰值,接受换卡重编 | TensorRT(一次前向);TensorRT-LLM(生成) | 当可移植包提交,换卡就挂 |
| LLM 在线高吞吐 | vLLM(默认起点);前缀很重 / Agent 看 SGLang | 用「QPS × 平均耗时」估机器数 |
| 本地 / 端上 | llama.cpp 一类(吃 GGUF) | 当服务端 GPU 池的主力 |
这一层选完,还没有对外服务——除非把引擎直接链进业务进程。
三、服务框架:怎么接
运行环境不开端口,也不会把同时到来的小请求攒成一次大计算。服务框架管:请求怎么进、排队等多久、何时开算、超时怎么降级。
线上变慢,第一反应常常是换引擎。更常见的其实是在排队:GPU 只算了 3ms,有 8ms 在等凑批。这两段要拆开——排队长了动服务框架或加副本;计算本身慢了才动运行环境。
攒批是吞吐和尾延迟的旋钮:等太短,GPU 吃不饱;等太长,队尾先超时。LLM 的连续批处理是同一件事:边生成边插新请求,所以必须和引擎长在一起,很难用普通 HTTP 网关代替。
比选哪个框架更要紧的,是独立成服务还是嵌进业务进程:
| 姿势 | 何时用 | 典型填法 |
|---|---|---|
| 独立服务 | 深度模型要 GPU 池化、要跨请求攒批、换版不跟业务发版绑死 | 一次前向用 Triton(挂 TensorRT / ONNX);LLM 用引擎自带的 server,或再收进 Triton |
| 内嵌业务进程 | LR、树模型、粗排,CPU 就能跑;RPC 一跳比计算还贵 | ONNX Runtime 当库链进 Bidder |
独立服务能在死预算里活下来,靠的不是 RPC 免费,而是调用方和推理服务同一区域、同一可用区,内网往返只有一点点,超时则退回粗排。跨区的话,这一跳会变成延迟大头,引擎再快也救不回来。广告深度 CTR 做成独立服务,是因为 GPU 不能塞进每台 Bidder,不是因为编排产品叫什么。
协议尽量稳:一次前向用 gRPC;LLM 用流式接口。换引擎不该逼调用方改客户端。一张卡跑多个模型能省钱,但要限制实例数和显存,否则互相拖慢。
四、编排平台:怎么在集群里活着
一台机器上把服务框架起起来,只说明「这一台能调」。生产还要:几份副本、按什么信号加机器、新模型切 10% 流量、挂了谁拉起来。
两样东西不要混成一个词:
| 通用容器编排 | 模型推理编排 | |
|---|---|---|
| 是什么 | K8s:进程、副本、探针、HPA | 底座之上:把「一个模型服务」收成一条声明 |
| 典型 | Deployment + Service + HPA | KServe(InferenceService);多步应用看 Ray Serve |
| 不上会怎样 | 没地方跑容器 | 扩缩、灰度、推理入口得自研 |
KServe 不是 K8s 的替代品,是架在上面的一层:声明里写模型在哪、什么格式、要几张卡,它再展开成 Deployment / 路由。只用 K8s 跑 Triton 完全合法,大厂很常见;上 KServe 是在买「算法能自助上线」这条能力,代价是多一套组件。
在线服务里几条硬习惯:
- GPU 服务按并发或队列扩,不要按 CPU——卡打满时 CPU 可以仍然很低。
- 延迟敏感的服务留热副本。缩到零能省钱,加载权重可能要几十秒,第一次请求会超时。
- 灰度既看进程活着,也看模型有没有变差(CTR 掉了、降级变多了)。
- 副本和调用方放同一可用区。
五、一张表填完选型
把三层填成可执行的组合(名字是各层的常见实现,不是必须绑定):
| 场景 | 运行环境 | 服务框架 | 编排 |
|---|---|---|---|
| LLM 在线对话 | vLLM / SGLang | 引擎自带 API(与左合一) | KServe;多步链路用 Ray Serve |
| LLM 要压延迟 | TensorRT-LLM | 常再收进 Triton | KServe,按 KV 留热副本 |
| 传统模型、多框架共卡 | TensorRT 或 ONNX Runtime | Triton | KServe,或裸 K8s Deployment |
| 广告 / 推荐深度 CTR | TensorRT / ONNX | Triton,独立服务,同 AZ gRPC | GPU 池;K8s 或 KServe;minReplicas ≥ 1 |
| 粗排 / LR / 树模型 | ONNX Runtime | 内嵌 Bidder | 跟着业务服务走 |
| RAG / Agent | vLLM + embedding 引擎 | Ray Serve 把几步串起来 | 偏应用编排 |
| 离线批量打分 | 任意 | Spark / Ray 批处理,不是在线框架 | 作业调度 |
三个问题按顺序问,就能落到表里的某一行:
- 算一次就结束,还是边生成边算?要不要 GPU? → 定运行环境,也定它要不要和服务框架绑在一起。
- 模型重不重? → 要 GPU 的深度模型走独立服务、同可用区、超时降级;很轻才内嵌。
- 要不要「模型自助上线」? → 模型多、要灰度 / 按并发扩 → KServe;已有发布体系或只有一两个模型 → 裸 K8s 托管 Triton 即可。
KServe 和 Triton 不是二选一:KServe 在外面管副本和灰度,Triton 在 Pod 里攒批、共卡、挂 TensorRT / ONNX。怎么接到注册表和 GitOps,见落地篇。
六、两条链路:表上的两行长什么样
LLM 对话(表第一行,要吞吐)
计算和接请求不必拆开。加机器按 KV 还够不够用,不要按「QPS × 平均耗时」乘台数。不到瓶颈,不必换成 TensorRT-LLM。
广告深度 CTR / CVR(表第四行,要死延迟,仍独立成服务)
业界主流不是把深度模型编进 Bidder:
必须独立:GPU 要池化、换版要和 Bidder 解耦、多条竞价要攒在一起才喂满卡。延迟靠三件事:别跨区、排队单独限、超时退回粗排(见竞价链路架构)。LR / 树模型才走第五行,嵌进 Bidder。
七、变慢了动哪一层
按这个表查,避免三层的人同时开工:
| 你看到什么 | 先动 |
|---|---|
| P99 高,GPU 很闲,队列在涨 | 服务框架:缩短攒批等待,或加队列上限 |
| P99 高,GPU 已满,队列很长 | 编排:加副本(按并发扩,不是按 CPU) |
| 队列很短,GPU 忙,单次仍慢 | 运行环境:精度、编译、模型结构 |
| 只有冷启动 / 第一次请求超时 | 编排:热副本,别缩到零 |
| 延迟整体垫高一截,还在抖动 | 拓扑:是不是跨可用区了 |
| LLM 并发一高就 OOM 或拒绝 | 运行环境的 KV 容量,不是再加「QPS 机器」 |
对应的坑,都是查反了层:拿引擎和编排比快慢;以为上了 KServe 就可以不要 K8s;推理服务跨区;按 CPU 给 GPU 做 HPA;深度模型复制到每台业务机器,或很小的模型也单独起服务;把排队算进「模型耗时」;把 TensorRT engine 当可移植包;LLM 容量只看 QPS。
一句话总结
先分三层,再填实现。 运行环境负责算(一次打分还是边生成边算,决定换哪类引擎、要不要和服务框架绑在一起);服务框架负责接(深度模型独立成服务,轻量模型嵌进业务);编排负责活着(要自助上线用 KServe,已有发布体系用裸 K8s)。KServe 托管 Triton,不是平替;vLLM 常常一个人干两层。
变慢时:先查跨区,再看队列,队列短了才动引擎。怎么接到注册表和 GitOps,见推理服务业界落地。