从学习到生产:本地 AI 服务的正式部署
前两篇搭好的服务——本地 AI 服务 和 Agent——都是"学习形态":单用户、无鉴权、手动启动、挂了重启。这一篇回答:把它正式部署出去,交给很多用户持续使用时,应该怎么做。
学习环境与生产环境的差别
维度 |
学习环境 |
生产环境 |
|---|---|---|
用户 |
你自己 |
多人并发,可能全天候 |
并发 |
一次一个请求 |
数十上百请求同时 |
可用性 |
挂了重启就是 |
SLA,如 99.9% |
安全 |
无鉴权,绑 localhost |
鉴权、内网、审计 |
性能目标 |
"能用" |
TTFT/TPOT 有明确指标 |
变更 |
随便折腾 |
灰度发布、可回滚 |
监控 |
看终端日志 |
指标、报警、可视化 |
生产化的核心矛盾只有一个:GPU 一次只能算一份活,而用户是同时来的。 后面所有架构设计,本质都是在回答"怎么把有限的 GPU 算力公平、高效、可靠地分给所有用户"。
为什么学习用的服务不能直接上生产
llama.cpp / Ollama 默认一次只服务一个请求(可以开并行参数,但只是简单的请求级调度)。第二个用户到达时只能排队:
请求1: |-------- 生成中 --------|
请求2: |-------- 生成中 --------|
请求3: |-------- 生成中 --------|
排队本身还能忍,真正浪费的是:decode 阶段 GPU 计算单元大部分时间在等显存搬运数据,算力利用率只有个位数。一个请求慢悠悠地吐 token 时,GPU 绝大部分算力闲置着。
重要
生产推理引擎的核心思路:让几十个请求同时 decode,用并发的请求把空闲算力填满。这叫 continuous batching(连续批处理)——新请求随到随加,完成的请求立刻让位,不互相等待。
continuous batching 与传统批处理
传统批处理等一批请求全部完成才换下一批(短的等长的);continuous batching 则:
时间轴 → t1 t2 t3 t4 t5
请求A: [======================]
请求B: [====================]
请求C: [========]
↑ C 完成立即让出位置, 新请求 D 立刻加入
图示:请求 C 完成后它的显存和算力马上让给新来的请求,不需要等 A、B 结束。批的边界消失了,"批"变成了一个持续流动的池子。
生产推理引擎:vLLM
vLLM 是当前生产部署最主流的开源引擎,内置 continuous batching,还解决了另一个生产问题——KV cache 的显存管理。
PagedAttention:KV cache 的分页管理
回忆第一篇:每个请求的 KV cache 大小由上下文长度决定。如果每个请求都按最大上下文预分配,大量显存被浪费,而且频繁分配/释放会产生碎片。
PagedAttention 的解法类似操作系统的虚拟内存分页:
把 KV cache 切成固定大小的 page;
请求需要多少就分配多少页(不按最大上下文预分配);
释放时按页回收,不产生碎片。
这样显存利用率显著提高,--gpu-memory-utilization 也敢安全地拉高。
启动 vLLM
pip install vllm
vllm serve Qwen/Qwen2.5-7B-Instruct \
--host 0.0.0.0 \
--port 8000 \
--max-model-len 8192 \
--gpu-memory-utilization 0.90 \
--max-num-seqs 128 \
--api-key sk-prod-xxxxxxxx
参数逐个解释:
参数 |
含义 |
|---|---|
|
允许的最大上下文长度。它和 |
|
显存可用比例(0.90 = 90%)。PagedAttention 让这个值可以安全设高 |
|
最大并发请求数。超过则排队 |
|
鉴权密钥,请求时必须带 |
|
模型切分到几张卡(单卡 1,双卡 2)。大模型(70B+)必须多卡 |
vLLM 同样暴露 OpenAI 兼容接口,上一篇的 Agent 代码把 base_url 换过来即可无缝切换——这正是"OpenAI 兼容"标准的价值。
引擎选型对比
引擎 |
定位 |
适合场景 |
|---|---|---|
llama.cpp / Ollama |
单机、低门槛、消费级硬件 |
学习、个人工具、边缘设备 |
vLLM |
高吞吐生产推理,生态最成熟 |
标准生产环境(本篇主线) |
SGLang |
与 vLLM 同档,调度更激进 |
极致吞吐场景 |
TGI(HuggingFace) |
老牌生产推理 |
深度绑定 HF 生态的团队 |
备注
技术选型不用一上来就纠结:学习/单机用 Ollama,生产默认 vLLM,遇到吞吐瓶颈再对比 SGLang。三者接口几乎一致,切换成本主要是部署成本,不是代码成本。
生产架构
生产环境的分层架构:
flowchart TB
subgraph 客户端
U1[Web 应用]
U2[Agent 进程]
U3[内部脚本]
end
subgraph 接入层
GW[API 网关<br/>nginx / LiteLLM<br/>鉴权 限流 路由]
end
subgraph 推理层
V1[vLLM 副本 1<br/>GPU 0]
V2[vLLM 副本 2<br/>GPU 1]
V3[vLLM 副本 3<br/>GPU 2]
end
subgraph 支撑层
MON[Prometheus + Grafana<br/>监控报警]
REG[模型仓库<br/>权重版本管理]
end
U1 --> GW
U2 --> GW
U3 --> GW
GW --> V1
GW --> V2
GW --> V3
V1 -. 指标 .-> MON
V2 -. 指标 .-> MON
V3 -. 指标 .-> MON
REG -. 权重只读挂载 .-> V1
REG -. 权重只读挂载 .-> V2
REG -. 权重只读挂载 .-> V3
图示:三层职责分明——网关管"谁能进来、进来多少",推理层管"生成 token",支撑层管"看得见、换得了"。推理层无状态(权重只读挂载),所以可以随意扩缩容。
各层职责:
层 |
职责 |
|---|---|
接入层 |
鉴权(API key)、限流、路由、重试、TLS 终结 |
推理层 |
纯推理。无状态,可水平扩展 |
支撑层 |
监控报警、模型版本管理、日志 |
容量规划:一台 GPU 能服务多少用户
显存怎么分
第一篇给过公式,生产场景把它细化:
显存 = 模型权重 + 单请求 KV cache × 并发数 + 激活值/碎片
以 24GB 显卡 + 7B 模型(fp16)为例:
权重: 14 GB
KV cache 可用: 24 × 0.90 - 14 - 约2GB(激活/碎片) ≈ 5.6 GB
单请求 KV cache(8192 上下文): 约 450 MB
最大并发 ≈ 5.6 GB / 450 MB ≈ 12
所以 --max-num-seqs 不是拍脑袋设的,是显存算出来的。想提高并发,三个方向:
权重量化(AWQ/GPTQ 等 4bit 方案):权重从 14GB 压到 ~4.4GB,KV cache 可用量从 5.6GB 涨到 ~15GB,并发直接翻三倍;
减小
--max-model-len:短问答场景很有效;换大显存卡:A100/H100 的 80GB 相比 24GB,并发翻几倍。
吞吐估算
吞吐 ≈ 单请求生成速度 × 并发数
7B 模型 fp16 在 24GB 卡上,单请求约 30~50 token/s。按 10 个并发、平均每次回答 1000 token 算:
10 × 40 token/s = 400 token/s
400 / 1000 = 0.4 次对话/秒 ≈ 每天 3.5 万次对话
这是"能不能扛住"的粗算。真实容量必须压测——用并发请求打满服务,实测 TTFT、TPOT、吞吐,而不是信估算。
TTFT 与 TPOT:生产环境的两个 SLA 指标
第一篇埋过这两个概念,生产环境里它们是服务等级目标:
TTFT(Time To First Token):从请求到第一个 token,由 prefill 决定,受输入长度影响最大。典型目标:< 1 秒(2K token 输入);
TPOT(Time Per Output Token):生成阶段每个 token 的间隔,由并发压力决定。典型目标:< 100ms。
小技巧
流式输出在生产环境不只是体验优化:TTFT 达标时,用户立刻看到内容在生成,"在动"本身就是可用性的一部分。非流式的等待体感是 1+1=3。
安全
生产 AI 服务的安全有几层,从外到内:
网络:推理服务只绑内网,不直接暴露公网。TLS 终结在网关;
鉴权:vLLM
--api-key是最后一道闸,生产一般由网关统一做 API key 管理、配额;提示词注入:Agent 场景特有(上篇讲过原理)。生产对策:工具最小权限 + 沙箱 + 高危操作人审 + 工具输出校验;
数据:本地部署的最大价值就是数据不出内网。仍然要做的:日志脱敏(用户输入进入日志前先过滤敏感信息)、保留审计记录。
监控与可观测性
vLLM 自带 Prometheus 格式的 /metrics 端点,配一套 Prometheus + Grafana 即可看到服务全貌:
指标 |
含义 |
建议报警条件 |
|---|---|---|
|
TTFT |
P95 > 2s |
|
TPOT |
P95 > 100ms |
|
排队中的请求 |
持续 > 0 |
|
正在处理的请求 |
持续等于上限 |
|
KV cache 使用率 |
> 90% |
|
输入/输出流量 |
容量规划依据 |
网关层(nginx)再补上请求数、错误率、P99 延迟。两层数据放一起,出问题时才能分清:是模型慢了(TTFT/TPOT 上升),还是网关挡了(限流、连接问题)。
备注
报警阈值是压测出来的,不是抄来的。上线前先打一轮压测,记录饱和点,再把报警设在饱和点的 70%~80%。
高可用与发布
多副本:推理服务无状态,天然适合多副本 + 负载均衡。一台 GPU 挂了,网关自动踢掉它,流量走其余副本;
健康检查:vLLM 提供
/health端点,容器编排用它做存活/就绪探测;灰度发布:换新模型时,先切 10% 流量到新版本,对比 TTFT/TPOT 和输出质量,没问题再逐步全量。模型的"质量回归"靠指标看不太出来,灰度期要人工抽查;
回滚:权重以只读方式挂载、模型名版本化。回滚 = 换回旧挂载,一分钟的事。永远不要在发布新模型时删除旧模型文件。
Kubernetes 部署示例
生产环境一般跑在 Kubernetes 上,最小部署长这样:
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-qwen
spec:
replicas: 2 # 两个 GPU 副本
selector:
matchLabels:
app: vllm-qwen
template:
metadata:
labels:
app: vllm-qwen
spec:
containers:
- name: vllm
image: vllm/vllm-openai:latest
args:
- "Qwen/Qwen2.5-7B-Instruct"
- "--max-model-len"
- "8192"
- "--gpu-memory-utilization"
- "0.90"
- "--max-num-seqs"
- "128"
env:
- name: VLLM_API_KEY
valueFrom:
secretKeyRef:
name: vllm-secret
key: api-key
ports:
- containerPort: 8000
resources:
limits:
nvidia.com/gpu: 1 # 每副本一张卡
readinessProbe:
httpGet:
path: /health
port: 8000
---
apiVersion: v1
kind: Service
metadata:
name: vllm-qwen
spec:
selector:
app: vllm-qwen
ports:
- port: 8000
targetPort: 8000
要点:
权重由镜像内置或启动时拉取到共享存储,推理层不落任何可变数据;
扩容方式与普通无状态服务相同(replicas +1)。区别是 GPU 机器贵且少,扩缩容通常手动做——小规模用 HPA 按 GPU 利用率自动扩容反而危险;
网关、监控、模型仓库作为独立组件部署,不随推理副本变动。
成本
生产部署的账要算清楚。以 7B 模型为例:
方案 |
硬件 |
单卡约 |
适合 |
|---|---|---|---|
消费级 GPU 自建 |
RTX 4090(24GB) |
购买约 1.5~2 万(一次性) |
小团队内部使用 |
云 GPU 按需 |
A10/A100(24~80GB) |
每小时数元~数十元 |
波动负载、快速验证 |
云 GPU 包月/年 |
A100/H100 |
每月数千~数万 |
稳定生产负载 |
省钱的两个方向:
量化:4bit 权重(AWQ/GPTQ)把 7B 权重从 14GB 压到 ~4.4GB,同样的卡能服务更多并发,相当于省钱;
投机解码(speculative decoding):小模型"起草"、大模型"校对",生成速度可翻倍。vLLM/SGLang 都支持,作为优化项后置。
警告
消费级显卡(4090 等)与数据中心卡(A100/H100)的差距不只是算力:数据中心卡显存更大、支持虚拟化、厂商稳定性更好,且云厂商对消费卡的 SLA 几乎为 0。内部小规模使用消费卡没问题,对外服务请用数据中心卡。
上线检查清单
鉴权开启,服务只绑内网
监控与报警就位,阈值来自压测
至少两个副本,验证过故障切换
灰度发布流程演练过,旧模型未删除
压测出真实的 TTFT/TPOT/吞吐,与容量规划核对
Agent 工具权限已按最小权限收口,沙箱就位
日志脱敏、审计开启
成本预估与预算对齐
总结
三篇文章串起来是一条完整的路:
从0搭建本地 AI 服务:理解服务是什么——模型文件、推理引擎、HTTP 接口;
搭建 Agent:给模型装上工具和循环,让它从"会说话"到"会做事";
本篇:把学习形态推向生产——换连续批处理引擎、分层架构、监控、安全、成本。
原理上,生产环境没有新魔法:还是那套"权重 + 引擎 + OpenAI 兼容接口",变化的只是并发、可靠性和治理。