☰
模型部署全链路:从单模型服务到LLM推理平台的关键机制
2026/11/3 5:01:27 网站建设 项目流程

做模型部署这行,有个现象我观察了好几年:不少人是在“测试环境能跑”这个阶段,就被直接叫去上生产环境的。后面的故事就变成一串经典事故——接口偶尔超时、显存偶尔OOM、并发稍微涨一点延迟就崩。最近我陆续帮几支团队梳理正式环境的部署链路,从最传统的单模型服务(TensorFlow Serving、TorchServe 那条路)一路折腾到 LLM 推理平台(vLLM、Triton 那套),发现一个很有意思的事实:工具换了一茬又一茬,核心要解决的问题其实非常稳定——资源怎么管、请求怎么路由、模型怎么升级、异常怎么兜底。这篇文章就按我自己的理解,把整条链路的全景、关键设计点和踩过的坑串一遍,适合刚接手正式环境的算法工程师,也适合准备把单个模型服务升级成稳定平台的后端同学。

1. 先想清楚:正式环境部署到底在解决什么问题

1.1 测试环境能跑,不代表能上线

这句话说了无数遍,但几乎每个团队都要亲手踩一遍才真正服气。测试环境里你拿几十条样本、一两个并发去验证,模型能返回结果,接口延迟在可接受范围,就以为大功告成了。等到了正式环境,流量一进来,问题全冒出来:GPU显存分配策略不对导致并发一高就 OOM;模型没有预热,前几十个请求慢到超时;动态 shape 没处理好,输入长度一变化推理引擎就要重新构图。

归根到底,正式环境部署要解决的是三件事:稳定(服务不能随便挂)、可控(资源、版本、流量都能管)、可观测(出了问题能快速定位)。测试环境的标准是“通”,正式环境的标准是“稳”。这两者之间的差距,就是部署框架存在的意义。

所以我不建议一上来就挑一个花哨的推理框架。先把部署目标量化:QPS 要多少、P95 延迟容忍到多少、模型多大、显存多少、预算多少。这些数字没定,后面所有选型都是拍脑袋。

1.2 从单模型到 LLM,部署范式发生了什么变化

早期单模型服务,比如一个 BERT 分类模型、一个目标检测模型,输入是固定 shape 或者接近固定 shape,推理过程是前向传播,一次算完立刻返回。部署框架要管的东西相对简单:模型加载、请求封装、批处理、版本管理。

到了 LLM 时代,情况完全不同。LLM 是自回归生成,一个请求要反复调用模型 decoder 很多次,每次生成一个 token;显存里还要存 KV Cache,而且 KV Cache 大小随并发和序列长度动态增长。你可以把传统模型部署理解为“一次性计算”,LLM 部署理解为“流水线工厂”:原料(请求)要排队、半成品(KV Cache)要暂存、产线(decode 阶段)要持续运转,任何一个环节卡住,整体吞吐就上不去。

这也是为什么 LLM 推理平台的架构明显更重:要有动态批处理、显存分页管理、流式输出、Preallocation 和调度策略。它不是把单模型服务放大几倍,而是在底层换了一套逻辑。

1.3 一个正式环境部署框架的最小组成

我见过的最小可用闭环,至少包含五个部分。模型仓库负责管理模型文件、格式转换和版本;推理引擎负责真正跑模型,可以是 TensorFlow Serving、TorchServe,也可以是 vLLM、Triton;网关层负责路由、限流、鉴权和多模型切换;可观测性负责日志、指标、追踪三件套;资源调度负责 GPU 分配、弹性伸缩和服务编排。

在实际团队里,这五块往往不是一步到位建成的。很多人是从“一个模型挂一个服务”开始的,然后发现服务越来越多、脚本越来越乱,才被迫上网关和模型仓库。这就引出一个很实际的问题:演进路线怎么走,既能解决问题,又不过度设计。我的建议是先把推理引擎和可观测性做扎实,网关和模型仓库在服务数量超过三个之后再引入,否则运维成本会反噬业务效率。

2. 单模型服务的核心设计与工具选型

2.1 模型格式是第一个分岔路口:ONNX、TensorRT 与原始权重

做单模型服务时,很多人纠结框架选哪个,其实第一个决策点不是框架,是模型格式。这个决定直接影响后续的性能上限和部署复杂度,属于牵一发动全身的事。

如果你手里的模型来自 PyTorch,最常碰到的三个选项是:.pt原始权重、.onnx中间格式、TensorRT 的.engine格式。原始权重最省事,由 TorchServe 这类框架直接加载,但推理性能和框架绑定比较深,不同版本 PyTorch 的算子实现差异会影响线上行为。ONNX 是中间表示,好处是换硬件、换后端灵活,比如同一个.onnx文件能跑在 CPU(ONNX Runtime 的 MLAS 后端)和 GPU(CUDA Execution Provider)上,甚至可以转到 TensorRT。TensorRT 是在 NVIDIA 显卡上做算子融合和精度校准的终极优化,性能最好,但转换流程长、动态 shape 处理麻烦,小版本迭代时非常痛。

我的建议很简单:追求快速落地选原始权重 + 对应原生框架;追求通用性和一定性能选 ONNX Runtime;追求极致性能和稳定的正式环境,再上 TensorRT。这里也回应一个真实困惑:ONNX 能不能部署 LLM?能,ONNX Runtime 对 transformer 类的支持已经很成熟,HuggingFace 的 Optimum 可以导出 LLM 到 ONNX,但坦白讲,LLM 的高并发场景性能不如 vLLM 这类专门优化过的引擎。ONNX 更适合边缘部署、CPU 部署和嵌入式场景,正式环境的高吞吐 LLM 服务,vLLM 是更主流的答案。

2.2 部署框架怎么选:TF Serving、TorchServe、Triton 的定位差异

模型格式定了,再来看框架。传统单模型服务时代,我见得最多的是这三个:TensorFlow Serving、TorchServe、NVIDIA Triton Inference Server。

TensorFlow Serving 的核心价值是服务化和版本管理做得好。模型目录结构像models/mymodel/1、models/mymodel/2,框架自动管理多版本加载和切换,配合--model_control_polling_interval就能实现热更新。如果你主要跑 TensorFlow 的模型,TF Serving 依然是个稳妥选择,但它在 PyTorch 模型上基本帮不上忙。TorchServe 是 PyTorch 官方的服务框架,它用.mar打包模型、推理代码和依赖,handler机制允许你自定义预处理和后处理。灵活是灵活,但批量推理性能比较一般,动态批处理要靠自己调。

Triton 是三者里最像“平台”的一个。它内置了多模型管理、并发调度、动态批处理、模型集成 Pipeline,而且能同时加载 TensorFlow、PyTorch、ONNX、TensorRT 甚至自定义 Python 后端。所以 2024 年之后,我看到越来越多团队把存量模型统一迁到 Triton,不是为了单一模型,而是为了“一个入口管所有模型”。

选型上我建议这样判断:只有一类模型、且量很小,原生框架最省事;存在多框架混合、想统一管理,优先考虑 Triton;想兼顾 GPU 和 CPU 推理,TRITON + ONNX Runtime 的组合比 TorchServe 更平衡。记住,框架选型要跟着团队维护能力走,别因为它“支持一切”就选它,能不能接得住才是关键。

2.3 版本管理与模型预热:正式环境最容易忽略的两个细节

单模型服务的版本管理,TF Serving 给了很好的示范:目录后缀数字代表版本号,框架支持加载指定版本或者所有版本,配合服务注册中心可以做灰度。TorchServe 里则要把模型打包成.mar文件,靠不同文件名区分版本——这就不太优雅,升级时容易覆盖旧版本。Triton 的做法更细致,每个模型都有config.pbtxt,里面可以指定version_policy,控制保留多少个版本。

另一个容易被忽略的事是模型预热。很多人上线后发现,线上服务第一次请求特别慢,之后恢复正常,于是用“新模型冷启动”一句话带过。实际上这里面的慢,一部分原因是推理框架在初始化时要做图优化、算子 autotune、显存分配,另一部分是 cuDNN 和 cuBLAS 的 benchmark 要跑真实 shape 才能选出最优算法。解决方式不复杂,但一定要做:服务启动后主动打几轮和线上真实分布一致的请求,把框架“热”起来,再放流量进来。我在正式环境里见过一个服务,本来 P95 是 80ms,预热前跑了三分钟冷请求,P95 直接飙到 400ms,预热之后恢复正常。这个坑几乎每天都有团队在踩。

3. LLM 推理平台的三大核心机制

3.1 为什么老办法拖不动 LLM:KV Cache 与显存瓶颈

聊 LLM 推理平台,必须先理解一个关键概念:KV Cache。传统模型部署,显存占用基本是“模型权重 + 计算临时 buffer”,相对固定。LLM 不一样,每个请求在解码过程中,每一步都要把历史的 Key 和 Value 缓存下来,避免重复计算 attention。可以理解为:你每写一个字,都要把前面写得所有内容抄一遍,抄下来的纸就是 KV Cache。

这个缓存有多大?我算一笔账。假设一个 7B 模型的 decoder 是 32 层,隐藏层 4096 维,用 FP16 存 KV Cache。并发 16 个请求,每个序列长度 2048,那么需要的 KV Cache 大约是:每层 32 个 attention head 乘以 head 维度 128,再乘以序列 2048 乘以 batch 16 乘以 2(K 和 V),每一层大概是 512MB,32 层加起来接近 16GB。这还只是 KV Cache,不算模型权重和 CUDA context。换句话说,并发和序列长度稍微涨一点,显存就爆了。

理解了这一点,就明白为什么 LLM 部署框架要专门研究显存管理。权重可以靠量化压缩,但 KV Cache 是动态的、按请求分配的,处理不好就是一台 80G 的 A100 只能塞几个并发。而且注意,序列长度越长、并发越大,显存压力是指数级上升的,这就是 LLM 高并发部署的第一个硬约束。

3.2 Continuous Batching 与 PagedAttention:LLM 吞吐的胜负手

传统批处理是“静态批处理”:一批请求收集齐了,一起前向传播,等最慢的请求结束,整批一起释放。LLM 场景下这个策略非常浪费。因为每个请求生成速度不一样,有的生成 10 个 token 就结束,有的生成长文,静态批处理会让短请求等长请求,GPU 空转。

连续批处理(Continuous Batching)解决了这个问题:每个 step 结束后,把已经结束的请求移出 batch,把新的请求插进来,让 GPU 在每一个 decode step 上都保持满负荷。这个机制听起来简单,但工程实现很难,因为要管理不同请求的 KV Cache、注意力掩码和调度状态。vLLM 是在这个方向做得最早也最激进的项目,配套的 PagedAttention 更是把 KV Cache 切成固定大小的块,像操作系统虚拟内存一样按需分配,基本消灭了 KV Cache 碎片化。

所以你会看到 vLLM 在主流的 LLM serving benchmark 上吞吐数据特别亮眼,核心就是这两件事:连续批处理保证 GPU 不闲着,PagedAttention 让显存能塞下更多请求。实际跑起来,类似的硬件条件下,vLLM 的吞吐往往能比 naive 的 FastAPI + Transformers 高出 5 到 10 倍。这不是魔法,是批调度和显存调度全部重写了。

3.3 预处理与解码拆开看:从 Attention 的 QKV 到 Token 生成流程

再往细了拆,LLM 推理请求内部其实是两段任务:Prefill(预处理)和 Decode(解码)。Prefill 阶段,输入 prompt 的所有 token 一次性并行计算,生成初始的 KV Cache;Decode 阶段,每次只生成一个 token,是一个逐步串行的过程。所以那些说 LLM 输出快的应用,基本都是优化了 Prefill;真正耗时的大头在 Decode,因为每一步生成都依赖前面的结果,无法并行。

这里的 Attention 机制有个经典对话:Q(Query)是在问“我要找什么”、K(Key)是在标注“我有什么”、V(Value)是在回答“我能提供什么”。网上流传的说法偶尔会把这几个角色弄混,实际在注意力计算里,Query 来自当前生成的位置,Key 和 Value 来自之前所有位置的缓存,通过 Query 和 Key 的相似度决定注意力权重,再用权重去加权 Value。这三者配合,模型才能决定“下一个 token 到底是什么”。

对部署工程师来说,这两段任务的资源需求差异也很大:Prefill 是计算密集,GPU 利用率高、延迟由 prompt 长度主导;Decode 是访存密集,瓶颈在显存带宽和 KV Cache 读取速度。所以推理平台最好能把 Prefill 和 Decode 做差异化调度,比如分阶段调整 batch size 上限,避免互相挤占资源。这也是为什么越来越多的框架开始做“PD 分离”,把两段任务拆到不同实例上,各用各的优化策略。

4. 从单模型服务到 LLM 推理平台的落地路径

4.1 一条可复制的演进路线

很多团队问我要一个“标准路径”,我通常给的建议是分三步走,别一步跨太大。

第一步,先把存量单模型服务规范化。统一推理引擎、统一可观测性、模型文件进 Git LFS 或者对象存储,部署脚本用版本号管理。这个阶段的目标是让每次上线都是可回滚的,而不是“上线就慌”。第二步,引入网关层和服务编排。当服务数量到三五个的时候,你必然需要在它们前面加一层路由,把请求按模型名、版本、业务线分发到不同后端。第三步,再上 LLM 推理平台。此时你已经有规范的模型管理流程和流量调度能力,把 vLLM 或 Triton 接进来只是新增一种后端,不会破坏已有体系。

这个路线的好处是把风险摊薄了。我看到有些团队一上来就铺 vLLM + K8s + 网关全家桶,结果模型管理和灰度机制没建好,平台再先进也救不了流程混乱。另一个容易被忽视的点是模型选型阶段的决策依据:Open LLM Leaderboard 等公开榜单能帮你快速筛一批候选模型,但最终上线评估一定要用自己的业务数据做评测,自己测出来的效果才是真的。

4.2 网关层:路由、限流与多模型混合服务

LLM 网关是当前热词没错,但核心不是“代理”,而是把模型服务变成可管可控的业务能力。网关层要解决的问题包括路由(同一语义的请求分到部署在不同 GPU 的模型副本)、限流(按用户、按项目配额)、熔断(后端实例故障时快速失败或降级)、多 Provider 切换(在自建模型和外部模型之间做兜底)。

这层最容易被忽略的是“协议统一”。目前 LLM 推理平台基本上都兼容 OpenAI 的 Chat Completions 接口,这带来的好处是 SDK 统一、工具生态丰富。代价是,你在接 function calling / tool use 的时候,schema 校验会变得很严格,一个常见的报错是llm request failed: provider rejected the request schema or tool payload——意思就是请求体里的工具定义和模型后端要求的 schema 对不上,或者工具参数 payload 格式不合法。遇到这个,先别怀疑模型坏了,去检查网关层的工具协议转换有没有做。

网关也承担着多模型混合服务的职责。你需要同时部署一个 7B 的快速模型和 70B 的深度模型,网关按业务规则把简单请求路由到小模型、复杂请求路由到大模型。这样既保住了业务效果,又控制住了成本。没有网关层,这种拆分就变成工程灾难——每一个业务方都要知道底层模型部署在哪,逻辑散得到处都是。

4.3 RAG 知识库场景下的多模型配合

LLM 平台很少是孤立跑的,最常见的一个配套场景就是 RAG 知识库,这也是 llm wiki 知识库这类项目在大模型落地时绕不开的一环。RAG 的完整链路其实包含好几个模型:Embedding 模型把文档切成块转成向量,向量数据库做近似检索,大模型根据检索结果生成回答,可能还要一个 Rerank 模型做精排。

这给部署带来的挑战是:不同模型对资源的需求差异极大。Embedding 模型通常比较小,CPU 都能跑,但请求量大,需要批量推理;Rerank 模型对延迟敏感,必须低延迟;主 LLM 则吃 GPU 显存和吞吐。所以部署 RAG 服务时,不能笼统地把所有模型塞进一个服务,而要根据算力需求拆分,再把它们串成一个 Pipeline。你可以用 Triton 的 Model Ensemble 功能把“Embedding → 检索 → Rerank → LLM”串成一条流水线,也可以在各服务之间用消息队列打通,根据数据量和响应时间要求选择不同方案。

我实测下来,RAG 场景里最容易翻车的不是 LLM 本身,而是 embedding 服务和向量库的并发瓶颈。很多团队给主模型配了高规格 GPU,Embedding 服务却跑在很弱的机器上,结果短 query 一多,embedding 服务先扛不住了,整个 RAG 链路响应时间一下就上去了。正式环境部署 RAG 时,一定要给每个环节都做压测和容量规划,哪怕 embedding 模型很小,也要算清楚并发上限。

5. 实操中的常见问题与排查经验

5.1 显存 OOM 与并发估算公式

LLM 部署里最常遇到的问题就是 OOM。我建议每个团队都把自己的部署环境做一个显存基线表:模型权重尺寸(FP16/BF16/INT8/INT4)、KV Cache 上限、CUDA context 大概多少、推理框架预留多少。然后写一个简单的并发估算公式:

总显存 = 模型权重 + KV Cache(随并发和序列长度增长)+ CUDA context / 框架预留 buffer

举例,7B 模型 FP16 权重约 14GB,CUDA context 加框架 buffer 大约留 1~2GB,剩下的显存就是 KV Cache 的可用空间。前面算过,在 A10(24GB)上,不量化的情况下,留给 KV Cache 的只有 8~10GB。如果你把最大序列长度限制在 2048,KV Cache 算下来每并发约 1GB,那并发上限就是 8~10。如果你想要 24 个并发,要么上量化(INT8 或 INT4),要么用 GQA 架构的模型(KV head 少,KV Cache 直接降到原来的 1/4 甚至 1/8),要么直接上显存更大的 A100/H 系列。

排查 OOM 有个实用技巧:先看监控里的显存曲线。如果显存是稳步爬升到顶的,大概率是 KV Cache 分配策略有问题;如果是某个请求进来瞬间爆掉,大概率是请求序列长度超过了你设置的上限,或者框架的 max_num_seqs 配置过高。别一上来就拍脑袋加显存,先判断是配置问题还是容量规划问题。

5.2 推理慢、响应卡顿怎么排查

LLM 服务的延迟指标拆开看是两个:TTFT(Time to First Token,首 token 延迟)和 TPOT(Time Per Output Token,每个输出 token 的时间)。用户在聊天时感知到的“卡”,大部分是 TPOT 太慢;首页显示的“正在思考”时间过长,则是 TTFT 问题。

排查 TTFT 偏慢:先看是否是排队导致(并发超过引擎容量),再看输入的 prompt 过长导致 Prefill 耗时增加,最后看网络链路有没有额外跳数。排查 TPOT 偏慢:先看 GPU 利用率是不是打满了,再看是不是 batch 里挤了太多的长序列请求,导致解码带宽被分走。还有一条容易被忽略:如果你同时跑 Prefill 和 Decode 混在同一个 batch 里,一个超长 prompt 的 Prefill 请求会显著拖慢其他所有请求的 Decode 速度。所以很多正式环境就是靠“限制单个请求的 max_input_len + max_tokens”把最坏情况控制住的。

压力测试是必须做的,但压测结果要看准。服务端返回的tokens/s是平均吞吐,不代表用户体验。用户感知的是首 token 等待 + 每秒生成速度,这两个指标要单列出来看。实际项目里我见过一个服务,平均吞吐 1200 tokens/s 很漂亮,但用户实际感受是等了 6 秒才看到第一个字,因为排队时间没被算进瓶颈。TTFT 的 P95 比平均吞吐重要得多。

5.3 第一版上线时的自检清单

最后整理一份自检清单,我每次带团队上线 LLM 服务都会过一遍。

检查项具体内容常见失败信号
显存规划权重、KV Cache、CUDA context 有明确的容量公式并发一高就 OOM
并发限制max_num_seqs、max_seq_len 与显存匹配请求排队时间无限增长
模型预热上线前用真实 shape 打热引擎前几十个请求超时
版本回滚旧版本模型文件保留、入口可切换模型升级失败无法回退
协议兼容对 OpenAI 接口的 schema 做验证function calling 报 schema 错误
可观测性TTFT、TPOT、吞吐、显存曲线都进监控出问题无法定位
限流预案网关层有配额和熔断流量突增把后端打挂
压测报告明确 QPS、P95 延迟、资源上限拍脑袋说“能扛住”

这 8 项做完,我不敢说线上一定不出问题,但至少出了问题你能快速判断是配置问题、容量问题还是代码问题。实际操作上,我把压测这步放在最前面做,因为它能逼你把显存公式、并发限制和引擎参数一起调试清楚,后面上线就是照着压测时的最佳配置来。


我个人在实际操作中最大的体会是:模型部署这事,框架永远在变,边界条件永远在变,但“把不确定性控住”这个底层能力是不变的。今天你学会了 TF Serving 的版本管理,明天迁到 vLLM 一样用得上;今天你理解了 KV Cache 的显存模型,后面换 SGLang、TensorRT-LLM 也还是这套账。所以别执着于追最新的部署工具,先把自己手里的模型、资源、链路摸透,再谈平台化。真到了那一步,你会发现升级到 LLM 推理平台不是什么惊心动魄的事,就是水到渠成。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询