LLM 推理服务搭建:vLLM、TGI 与 Ollama

从本地原型到线上服务,理解主流推理框架的定位差异、部署方式、监控指标与生产化注意事项。

难度

进阶

阅读时长

约 120 分钟

更新日期

2026/03/24

主题

推理优化 / 部署 / vLLM / TGI / Ollama

先修知识

KV Cache 与推理性能优化Linux 或容器基础HTTP API 基本概念

学习目标

读完这篇教程后,你应该能:

  1. 区分 vLLM、TGI、Ollama 各自更适合什么部署场景。
  2. 理解一个推理服务从“模型加载”到“接口返回”要经过哪些关键环节。
  3. 知道线上部署至少要监控哪些延迟、吞吐和稳定性指标。
  4. 设计一条从本地验证到生产上线的渐进式部署路线。

如果你已经看过 KV Cache 与推理性能优化,这篇文章会把那些系统概念进一步落到部署实践。

先回答一个根本问题:你部署服务是为了什么

很多人一上来就问“vLLM 和 TGI 哪个更快”,但真正应该先问的是:

我现在需要的是本地快速试验、团队内共享接口,还是线上高并发服务?

因为不同目标,对部署框架的要求完全不一样:

  • 本地验证更看重易用性和模型兼容性
  • 内部工具更看重稳定接口和运维成本
  • 生产服务更看重吞吐、监控和资源利用率

如果目标没想清楚,再多 benchmark 对决也很难帮你做决定。

一个最小推理服务到底包含什么

从系统视角看,一个推理服务至少包含以下部分:

  1. 模型权重加载
  2. tokenizer 与请求预处理
  3. batch 调度与 KV Cache 管理
  4. token 解码与采样策略
  5. HTTP 或 OpenAI-compatible API 暴露
  6. 日志、监控、错误恢复

很多框架真正拉开差距的地方,并不是“会不会生成文字”,而是第 3、5、6 部分做得是否成熟。

先看一次请求在服务里的生命周期,会更容易理解后面为什么要盯住吞吐、延迟和监控:

推理服务请求生命周期图 用户请求先进入 API 层,再经过模板与 tokenizer 预处理、调度与批处理、模型推理和 KV Cache 管理、解码采样,最终返回响应,底部由日志、监控、限流、健康检查和回滚共同支撑。 一次请求的服务化生命周期
  <g>
    <rect x="28" y="88" width="140" height="88" rx="18" fill="#e8f1ff" stroke="#98b7e1" />
    <text x="98" y="122" text-anchor="middle" font-size="19" font-weight="700">API 请求</text>
    <text x="98" y="148" text-anchor="middle" font-size="14" fill="#4b5563">HTTP / OpenAI</text>
  </g>
  <g>
    <rect x="184" y="88" width="140" height="88" rx="18" fill="#fef3c7" stroke="#e2c36f" />
    <text x="254" y="120" text-anchor="middle" font-size="18" font-weight="700">模板与预处理</text>
    <text x="254" y="146" text-anchor="middle" font-size="14" fill="#4b5563">tokenizer / prompt</text>
  </g>
  <g>
    <rect x="340" y="88" width="150" height="88" rx="18" fill="#e0f2e5" stroke="#8bc09b" />
    <text x="415" y="120" text-anchor="middle" font-size="18" font-weight="700">调度与批处理</text>
    <text x="415" y="146" text-anchor="middle" font-size="14" fill="#4b5563">queue / batching</text>
  </g>
  <g>
    <rect x="506" y="88" width="160" height="88" rx="18" fill="#fce7ef" stroke="#e2a8bd" />
    <text x="586" y="116" text-anchor="middle" font-size="18" font-weight="700">模型推理</text>
    <text x="586" y="140" text-anchor="middle" font-size="14" fill="#4b5563">权重加载 / KV Cache</text>
    <text x="586" y="162" text-anchor="middle" font-size="14" fill="#4b5563">prefill / decode</text>
  </g>
  <g>
    <rect x="682" y="88" width="132" height="88" rx="18" fill="#ece8ff" stroke="#b5abef" />
    <text x="748" y="120" text-anchor="middle" font-size="18" font-weight="700">解码采样</text>
    <text x="748" y="146" text-anchor="middle" font-size="14" fill="#4b5563">temperature / top-p</text>
  </g>
  <g>
    <rect x="830" y="88" width="122" height="88" rx="18" fill="#dff4f0" stroke="#8dc7bd" />
    <text x="891" y="122" text-anchor="middle" font-size="19" font-weight="700">响应返回</text>
    <text x="891" y="148" text-anchor="middle" font-size="14" fill="#4b5563">stream / metrics</text>
  </g>

  <line x1="168" y1="132" x2="184" y2="132" stroke="#5b6b7f" stroke-width="3" marker-end="url(#infer-arrow)" />
  <line x1="324" y1="132" x2="340" y2="132" stroke="#5b6b7f" stroke-width="3" marker-end="url(#infer-arrow)" />
  <line x1="490" y1="132" x2="506" y2="132" stroke="#5b6b7f" stroke-width="3" marker-end="url(#infer-arrow)" />
  <line x1="666" y1="132" x2="682" y2="132" stroke="#5b6b7f" stroke-width="3" marker-end="url(#infer-arrow)" />
  <line x1="814" y1="132" x2="830" y2="132" stroke="#5b6b7f" stroke-width="3" marker-end="url(#infer-arrow)" />

  <rect x="28" y="224" width="924" height="62" rx="20" fill="#f4f7fb" stroke="#d7e2f1" />
  <text x="490" y="248" text-anchor="middle" font-size="18" font-weight="700">服务化护栏</text>
  <text x="490" y="272" text-anchor="middle" font-size="15" fill="#4b5563">日志 | 健康检查 | 超时取消 | 限流队列 | 监控告警 | 配置回滚</text>
</g>
真正的推理服务不只是“把模型跑起来”,而是把调度、缓存、解码和运维护栏连成完整系统。

Ollama:最适合本地原型和个人工作流

Ollama 最大的优势是上手快。对很多开发者来说,它解决的是:

  • 本地跑模型太繁琐
  • 模型文件管理不统一
  • 想快速起一个可调用接口

它比较适合:

  • 个人电脑本地实验
  • 小团队 demo
  • 与编辑器、脚本、桌面应用快速联调

它相对不那么适合的场景是:

  • 复杂多租户并发
  • 大规模 GPU 服务集群
  • 对吞吐与调度能力要求很高的生产环境

也就是说,Ollama 很像一个非常顺手的“本地推理工作台”。

vLLM:高吞吐在线服务的热门选择

vLLM 之所以火,核心原因在于它把很多推理系统里最难做好的问题处理得比较成熟,例如:

  • PagedAttention 管理 KV Cache
  • 连续批处理提升 GPU 利用率
  • OpenAI-compatible API 便于快速接入
  • 长上下文与并发场景下表现稳定

所以 vLLM 很适合:

  • 在线 API 服务
  • 团队共享推理网关
  • 多请求并发较高的服务型场景

如果你的重点是“同一台 GPU 承担尽可能多的请求”,vLLM 通常是非常值得优先尝试的路线。

TGI:偏向成熟服务化与 Hugging Face 生态

TGI(Text Generation Inference)在团队化部署里也很常见。它的优势通常体现在:

  • 与 Hugging Face 模型生态衔接紧密
  • 服务化组件和可运维性较成熟
  • 适合标准化部署和团队协作

它往往比较适合:

  • 已经深度使用 Hugging Face Hub 的团队
  • 希望更标准化管理模型服务的组织
  • 需要较完善观测与部署流程的生产环境

从定位上说,TGI 和 vLLM 都能走向生产,但二者的工程重心和性能路径有所不同。

该怎么选:一个够用的判断框架

你可以先用下面这张简单判断表:

场景更优先考虑
本地个人实验Ollama
小团队快速开放 APIvLLM 或 Ollama
高并发在线服务vLLM
Hugging Face 生态深度集成TGI
需要长期标准化运维TGI 或 vLLM

这不是绝对规则,但足够覆盖大多数第一轮决策。

从本地到线上,建议走一条渐进路线

一个务实的部署节奏通常如下:

  1. 先在本地用 Ollama 或单机 vLLM 跑通模型。
  2. 用固定评测样本建立基线,记录延迟和回答质量。
  3. 再迁移到容器化环境,暴露标准 API。
  4. 加入日志、监控、限流和健康检查。
  5. 最后再优化并发、量化和多实例调度。

这样做的价值在于:每次只引入一层复杂度,出了问题也容易定位。

一个最小 vLLM 启动示例

如果你想快速起一个兼容 OpenAI API 的服务,常见启动方式会类似:

python -m vllm.entrypoints.openai.api_server \
  --model Qwen/Qwen2.5-7B-Instruct \
  --dtype bfloat16 \
  --max-model-len 8192 \
  --port 8000

起来之后,你就可以用普通 HTTP 请求访问:

curl http://127.0.0.1:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Qwen/Qwen2.5-7B-Instruct",
    "messages": [{"role": "user", "content": "请解释一下什么是 FlashAttention"}],
    "temperature": 0.2
  }'

这一步的目标不是追求性能最优,而是先把“模型到接口”的闭环打通。

一个最小 Ollama 工作流

如果你更想快速验证本地开发流程,Ollama 的体验通常会更直接:

ollama run qwen2.5:7b

或者先启动服务,再通过应用或脚本访问它。它特别适合下面这种节奏:

  • 本地试模型
  • 改 prompt
  • 对接一个小工具
  • 很快得到一个能演示的原型

它的短板并不是“不能用”,而是当你进入复杂并发和生产运维阶段后,很可能需要转向更专业的服务框架。

推理服务上线后,哪些指标必须盯

推理服务最容易出现的问题不是“完全不能用”,而是“偶尔特别慢、长请求拖垮整体、模型输出开始不稳定”。因此上线后建议至少监控:

  • TTFT(首 token 延迟)
  • tokens/s(生成速度)
  • p95 / p99 延迟
  • GPU 利用率
  • 显存占用
  • 请求排队时间
  • 错误率与超时率

如果没有这些指标,你很难判断问题出在:

  • 模型太大
  • batch 调度不合理
  • KV Cache 管理不佳
  • 上下文长度失控

量化和框架选择要一起考虑

框架并不是孤立选择。很多部署决策都和量化路线绑在一起:

  • 你的框架是否原生支持 INT8 / INT4
  • 是否支持 GPTQ / AWQ 等模型格式
  • 量化后吞吐是否真的提升

所以一个更完整的部署判断是:

  1. 先选模型
  2. 再看量化路线是否成熟
  3. 最后看哪套服务框架最适配

如果你还没有把量化思路理清,可以先读 模型量化入门:INT8、INT4、GPTQ 与 AWQ

生产环境里最常见的非模型问题

很多线上事故并不是模型能力本身造成的,而是下面这些基础设施细节:

  • 没有请求超时和取消机制
  • 长 prompt 没有限制,拖垮显存
  • 没有健康检查,坏实例还在接流量
  • 日志不完整,无法回溯问题请求
  • 错误重试策略设计不当,形成雪崩

这也是为什么“能跑起来”和“可上线”之间差着一整套工程化工作。

一个适合小团队的最小部署清单

如果你是第一次把 LLM 服务交付给别人使用,建议至少补齐这些基本项:

  • 固定模型版本与配置文件
  • 标准输入输出协议
  • 健康检查接口
  • 请求日志与错误日志
  • 一套固定回归样本
  • 简单限流或队列控制

这些东西看起来没有模型论文酷,但它们决定了服务是否可靠。

常见误区

1. 上来就比较框架 benchmark,却没有固定测试条件

不同 prompt 长度、batch 方式、采样参数都会显著影响结果。没有统一基线,比较很容易失真。

2. 只关心平均吞吐

如果 p99 很差,用户体验仍然会很糟。线上服务必须关注长尾延迟。

3. 忽略 tokenizer 和模板一致性

同一个模型如果前处理模板不一致,服务效果可能会看起来“突然变差”,但问题并不在框架本身。

4. 没有保留回滚方案

新模型、新量化格式、新框架上线时,一定要有可快速切回旧版本的路径。

练习与思考题

  1. 为什么说部署框架的差异,很多时候体现在调度和缓存管理,而不是文本生成本身?
  2. 如果你现在的目标是本地快速做原型,为什么不一定应该直接上最复杂的线上框架?
  3. 在什么情况下,平均吞吐很好但用户仍会抱怨“服务很慢”?
  4. 如果业务请求普遍很长,你在框架选择和监控设计上应该优先关注什么?

延伸阅读

相关阅读

从相近主题继续深入,建立连续学习链路。