本地部署 LLM 服务:从硬件到后端的完整基建指南
本地部署 LLM 提供服务,基础设施分硬件、软件、运维三层。本文以「27B 模型 + 8K 上下文 + 最大 10 并发」的具体场景为线索,从显存估算、硬件选型、推理引擎、服务架构到后端业务代码,给出完整可落地的方案。
一、硬件基建
1. GPU / 算力
- 核心瓶颈:显存(VRAM)决定能跑多大的模型、多大的并发。
- 显存估算:参数量 × 精度字节数 ≈ 权重占用。
- 7B 模型 FP16 ≈ 14GB,INT8 ≈ 7GB,INT4 ≈ 3.5GB。
- 70B 模型 FP16 ≈ 140GB,需多卡(如 4×A100 80G)。
- 常见卡型:
- 消费级:RTX 4090(24GB)、RTX 5090(32GB)——适合小模型/实验。
- 专业级:A100/H100(80GB)、L40S(48GB)、H20——适合正式服务。
- 国产替代:昇腾 910B、寒武纪、沐曦等(需适配国产软件栈)。
- KV Cache 也要占显存,长上下文 + 高并发会显著抬高需求。
2. 主机与扩展
- CPU:多核(≥16 核)用于数据预处理、调度、tokenizer。
- 内存:≥ 256GB 起步,用于加载权重到 CPU、KV cache 溢出、数据集。
- 存储:NVMe SSD(Gen4/Gen5),模型加载和权重读取速度关键。
- 扩展能力:选购带 8×PCIe 或 NVLink 的服务器,便于多卡改造。
3. 网络
- 多卡互联:NVLink / PCIe 直连,跨卡张量并行时带宽决定性能。
- 机房网络:万兆网卡,服务对外吞吐、集群通信。
- 对外:负载均衡、CDN、带宽按需求估算。
4. 机房与供配电
- 散热:GPU 发热量大,需液冷或高功率风冷。
- 供电:单卡 350–700W,多卡需 UPS + 冗余电源。
- 机架、制冷、消防、监控。
二、软件基建
1. 推理引擎(核心)
- vLLM:业界主流,PagedAttention 高吞吐,支持连续批处理。
- TGI(Hugging Face Text Generation Inference):工业级,功能全。
- SGLang:RadixAttention,长上下文/多轮对话性能好。
- TensorRT-LLM:NVIDIA,性能极致但部署复杂。
- llama.cpp:CPU/边缘端轻量,适合小模型。
- Ollama / LM Studio:快速上手,适合测试,生产慎用。
2. 量化与优化
- 工具:GPTQ、AWQ、GGUF(llama.cpp)、bitsandbytes。
- 目的:降显存、提吞吐,代价是精度损失。
3. 服务框架
- API 层:OpenAI 兼容接口(vLLM/TGI 原生支持)。
- 网关:Nginx / Kong / Envoy 做反向代理、限流、负载均衡。
- 编排:Docker / Kubernetes(K8s + GPU 插件 nvidia-device-plugin)。
4. 模型获取与存储
- 模型来源:Hugging Face、ModelScope(国内镜像)、本地私有化权重。
- 管理:Model Registry、版本管理。
5. 前后端应用层
- 框架:FastAPI / LangChain / LlamaIndex。
- RAG:向量数据库(Milvus、Qdrant、pgvector)+ Embedding 模型。
- 前端:Web UI(如 Open WebUI、Gradio)。
6. 监控与日志
- 观测:Prometheus + Grafana、MLflow。
- 指标:吞吐(tokens/s)、延迟(TTFT、TPOT)、GPU 利用率、显存占用。
三、运维与合规
- 鉴权:API Key 管理、认证、租户隔离。
- 治理:内容安全、敏感词过滤、审计日志。
- 备份与容灾:模型权重备份、数据快照。
- 安全:容器隔离、防火墙、密钥管理(Vault)。
四、按规模选型建议
| 场景 | 参考配置 |
|---|---|
| 私域小服务(7B) | 1 张 RTX 4090 / A10,vLLM + Ollama |
| 中型生产(13B–70B) | 双卡 A100/H100,vLLM + K8s |
| 大型生产(70B+ 高并发) | 4×A100/H100 + NVLink,TensorRT-LLM + K8s + 网关 |
| 超大规模/多租户 | GPU 集群 + 调度(Ray/K8s)+ 弹性伸缩 |
关键提醒:显存是最大的成本与决策点;先明确模型规模、并发量、上下文长度,再反推硬件。生产环境优先选 vLLM + Docker/K8s + 监控 这套成熟组合。
五、27B 模型 10–20 并发:显存重算
针对 27B 模型 + 10–20 并发 这个具体场景,关键变量是量化精度和上下文长度。这个规模处于「单卡/双卡」交界处。
以 27B 模型为例(如 Qwen2.5-27B / Llama-3-27B):
| 项 | 计算 | 占用 |
|---|---|---|
| 权重 FP16 | 27B × 2B | ~54GB |
| 权重 INT8/FP8 | 27B × 1B | ~27GB |
| 权重 INT4 | 27B × 0.5B | ~13.5GB |
| KV Cache(每并发@8K) | 约 0.9MB/token × 8192 | ~7GB/序列 |
| KV Cache 20 并发 @8K | 7GB × 20 | ~140GB |
关键点:20 并发 × 8K 上下文时,KV Cache 会吃掉 140GB,比权重还大。所以并发数和上下文长度是显存的最大变量,务必先定死。
推荐配置(生产主力,INT8 权重 + FP8 KV)
假设:8K 上下文、峰值 20 并发、连续批处理(vLLM 平均实际占用远低于保守值)。
| 组件 | 推荐 | 说明 |
|---|---|---|
| GPU | 2× L40S 48GB 或 1× H20 96GB | 权重 27GB + KV 余量充足,单实例即可跑满 20 并发 |
| 内存 | 256GB DDR5 | 权重加载 + KV 溢出缓冲 |
| CPU | 32 核(如 EPYC 7443) | 调度、tokenizer、预处理 |
| 存储 | 2× NVMe SSD 1TB(Gen4) | 模型权重 + 数据 |
| 网络 | 万兆网卡 | 对外吞吐 |
| 供电/散热 | 双电源冗余 + 常规风冷 | 不选液冷(L40S/H20 功耗可控) |
为什么不用 H100/A100 80G:能吃下,但 80G 单卡跑 27B+20 并发比较紧,H20 96G 或双 L40S(96G 合计)更从容,且性价比高。
软件栈
- 推理引擎:vLLM(开放 Discourse,高吞吐,原生 OpenAI 兼容接口)
- 重量级可选:SGLang(长上下文/多轮更优)
- 量化:FP8/INT8 权重 + FP8 KV cache(vLLM 内置,几乎无损)
- 部署:Docker + Nginx(限流/负载均衡),不需要上 K8s(单实例规模)
- 监控:Prometheus + Grafana(vLLM 暴露 tokens/s、TTFT、TPOT、GPU 利用率)
- 模型:从 ModelScope 拉取(国内快)或 Hugging Face
预算配置(INT4 权重,单卡)
假设:缩短上下文到 4K、峰值并发降到 15。
| 硬件 | 配置 |
|---|---|
| GPU | 1× RTX 4090 24GB 或 1× L40S 48GB |
| 权重 INT4 | ~13.5GB |
| KV Cache 15 并发 @4K | ~37GB |
| 合计 | 24GB 卡偏紧,48GB 卡舒适 |
这档适合内部测试 / 低并发原型,不建议作为正式对外服务。
成本估算(人民币,含整机)
| 方案 | GPU | 裸机约价 | 说明 |
|---|---|---|---|
| 推荐 | 2× L40S 48GB | ¥12–16 万 | 单机即可,运维简单 |
| 推荐 | 1× H20 96GB | ¥10–14 万 | 国产可控,单卡省电 |
| 高配 | 1× H100 80G | ¥25–30 万 | 性能最强但贵 |
| 预算 | 1× RTX 4090 24GB | ¥3–5 万 | 仅原型 |
云上(阿里云/火山/华为云 GPU 实例)按需租用 H20 或 A10 即可,月租约 ¥1.5–3 万,适合先验证再自购。
六、收紧到 8K 上下文 + 最大 10 并发
参数收紧到 8K 上下文 + 最大 10 并发后,结论大幅简化:单卡即可搞定,成本比上一版降一半以上。原因是 KV Cache 从「20 并发 × 8K」的 ~140GB 降到了 ~10GB,不再是瓶颈。
显存重算(以 Qwen2.5-27B 为例)
27B 模型是 GQA 架构(64 层、4 个 KV 头),KV Cache 每 token 约 128KB(FP16):
| 项 | 计算 | 占用 |
|---|---|---|
| 权重 FP16 | 27B × 2B | ~54GB |
| 权重 FP8/INT8 | 27B × 1B | ~27GB |
| 权重 INT4 (AWQ) | 27B × 0.5B | ~13.5GB |
| KV Cache 10 并发 @8K FP16 | 128KB × 8192 × 10 | ~10.5GB |
| KV Cache 同上但 FP8 | 减半 | ~5GB |
结论:10 并发 × 8K 时 KV 只占 5–10GB,权重成了唯一大头 → 单卡路线成立。
推荐配置(生产,FP8 权重)
| 组件 | 配置 | 说明 |
|---|---|---|
| GPU | 1× L40S 48GB(或 1× H20 96GB) | 权重 27GB + KV ~5GB + 开销 ≈ 35GB,48GB 卡留 ~13GB 余量,可临时扩到 20 并发或 16K 上下文 |
| CPU | 16–32 核 | 调度、tokenizer,够用 |
| 内存 | 128–256GB DDR5 | 权重加载 + 数据缓冲 |
| 存储 | 1× NVMe SSD 1TB(Gen4) | 模型 + 日志 |
| 软件 | vLLM + Docker + Nginx + Prometheus/Grafana | 单实例,不需要 K8s |
预算配置(INT4 AWQ,单卡 4090)
| 组件 | 配置 | 说明 |
|---|---|---|
| GPU | 1× RTX 4090 24GB | 权重 13.5GB + KV(FP8) ~5GB + 开销 ≈ 20GB,能塞进 24GB,但余量小 |
| 前提 | 必须用 AWQ/GPTQ INT4 量化模型 + FP8 KV cache | 精度损失可接受(约 1–2 分) |
| 适用 | 内部工具、原型、低敏场景 | 10 并发满负荷时较紧,建议把 --max-num-seqs 限到 8 |
成本估算(人民币)
| 方案 | GPU | 整机约价 | 云上按需月租 |
|---|---|---|---|
| 生产 | 1× L40S 48GB | ¥5–8 万 | 约 ¥1–1.5 万/月 |
| 生产 | 1× H20 96GB | ¥8–12 万 | 约 ¥1.5–2 万/月 |
| 预算 | 1× RTX 4090 24GB | ¥2.5–4 万 | 约 ¥0.5–1 万/月 |
vLLM 启动命令(参数已定死,直接可用)
生产方案(FP8):
vllm serve Qwen/Qwen2.5-27B-Instruct-FP8 \ --max-model-len 8192 \ --max-num-seqs 10 \ --kv-cache-dtype fp8_e5m2 \ --gpu-memory-utilization 0.9 \ --port 8000 \ --served-model-name qwen27b预算方案(INT4 AWQ):
vllm serve Qwen/Qwen2.5-27B-Instruct-AWQ \ --max-model-len 8192 \ --max-num-seqs 10 \ --kv-cache-dtype fp8_e5m2 \ --gpu-memory-utilization 0.95 \ --port 8000 \ --served-model-name qwen27b模型从 ModelScope 拉取更快:
modelscope download --model Qwen/Qwen2.5-27B-Instruct,本地路径替换仓库名即可。
七、软件基建再细化
推理引擎选型
| 引擎 | 适配度 | 说明 |
|---|---|---|
| vLLM | ⭐ 首选 | 高吞吐 + OpenAI 兼容接口,8K/10 并发场景性能足够,生态最成熟 |
| SGLang | 备选 | 多轮对话/长上下文更优,但你这规模优势不明显,复杂度略高 |
| llama.cpp | 排除 | 偏 CPU/边缘,不适合服务化 |
| Ollama | 排除 | 测试用,生产不推荐 |
结论:vLLM,理由:单卡吞吐够、原生 OpenAI 接口、量化支持好、监控指标齐全。
服务架构(单机版)
客户端 │ HTTPS ▼Nginx (反向代理 + 限流 + TLS 终结) │ ▼vLLM (OpenAI 兼容 :8000) ──► Prometheus 指标 /metrics │ ├── 模型权重(Qwen2.5-27B FP8/AWQ) └── 可选: RAG 侧车(Milvus/Qdrant + Embedding)三层:
- 网关层(Nginx):对外统一入口,隐藏内部端口;TLS 证书 + 限流(
limit_req,防止打爆);按路径路由到推理服务。 - 推理层(vLLM):核心服务,OpenAI 兼容
/v1/chat/completions、/v1/completions;暴露/metrics给监控。 - 可选应用层:RAG(外接知识库时加向量库)与业务代码(FastAPI 做业务封装、鉴权、多租户)。
一键部署(Docker Compose)
这是最省心的方案,不需要 K8s(单实例规模)。
docker-compose.yml:
services: vllm: image: vllm/vllm-openai:latest entrypoint: ["python", "-m", "vllm.entrypoints.openai.api_server"] command: - --model=/models/Qwen2.5-27B-Instruct - --max-model-len=8192 - --max-num-seqs=10 - --kv-cache-dtype=fp8_e5m2 - --gpu-memory-utilization=0.9 - --served-model-name=qwen27b - --port=8000 volumes: - /data/models:/models:ro deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu] ports: - "127.0.0.1:8000:8000" # 只暴露内网,对外走 Nginx
nginx: image: nginx:alpine volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro ports: - "443:443" depends_on: - vllm
prometheus: image: prom/prometheus volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro - prom-data:/prometheus ports: - "9090:9090"
grafana: image: grafana/grafana ports: - "3000:3000" environment: - GF_SECURITY_ADMIN_PASSWORD=<PASSWORD> volumes: - grafana-data:/var/lib/grafana
volumes: prom-data: grafana-data:nginx.conf(限流 + 反代):
worker_processes auto;events { worker_connections 1024; }
http { limit_req_zone $binary_remote_addr zone=llm:10m rate=20r/s;
upstream vllm { server vllm:8000; keepalive 32; }
server { listen 443 ssl; # ssl_certificate /etc/nginx/ssl/server.crt; # ssl_certificate_key /etc/nginx/ssl/server.key;
location /v1/ { limit_req zone=llm burst=20 nodelay; proxy_pass http://vllm; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_read_timeout 300s; # 长生成需放宽 } }}部署要点
| 环节 | 关键点 |
|---|---|
| 模型加载 | 从 ModelScope 拉 Qwen2.5-27B-Instruct(FP8 或 AWQ 版),挂载只读目录,避免每次启动重下载 |
| 连续批处理 | vLLM 默认开启,--max-num-seqs 10 即并发上限,10 并发接近满是因为单实例 |
| 鉴权 | 对外上线务必加 API Key(Nginx 层校验或 vLLM --api-key),别裸奔 |
| 限流 | Nginx limit_req 兜底,防 DDoS 和误刷 |
| 监控 | vLLM 自带 /metrics(tokens/s、TTFT、TPOT、GPU 利用率),Prometheus 抓取 + Grafana 看板 |
| 日志 | Docker 日志 + 可选接入 Loki/ELK,审计算法调用 |
| GPU 驱动 | 宿主机装 NVIDIA 驱动 + nvidia-container-toolkit,这是 Docker 用 GPU 的前提 |
验证与上线路径
- 本地冒烟:
curl http://127.0.0.1:8000/v1/chat/completions发一条测试消息。 - 压测:用小工具打 10 并发,看 TTFT / TPOT / 显存(vLLM 指标里都有)。
- 调整:显存紧张就降
--gpu-memory-utilization或--max-num-seqs。 - 上线:Nginx 挂 TLS + API Key,Prometheus/Grafana 看板挂好。
八、后端开发视角的服务方案
把推理服务当成自己团队要维护的一个后端系统来设计。不要把 vLLM 直接暴露给业务,中间要加一层「业务网关服务」。
分层架构
客户端/前端 │ HTTPS ▼┌─────────────────────────────┐│ BFF / 业务服务 (FastAPI) │ ← 后端开发的主战场│ 鉴权 · 租户 · 限流 · 计费 · ││ 编排 · 缓存 · 路由 · 重试 │└──────────────┬──────────────┘ │ internal HTTP (OpenAI 兼容) ▼┌─────────────────────────────┐│ vLLM 推理服务 (单实例) │ ← 基础设施,屏蔽细节└─────────────────────────────┘核心原则:业务服务是「唯一入口」,vLLM 是「可替换的推理后端」。这样以后换引擎(SGLang/TensorRT)、换模型、加 GPU,前端和业务代码都不用动。
统一 API 层(对外契约)
对外暴露你自己的接口,而不是直接把 vLLM 的 OpenAI 接口透传出去——这样你可以控制协议、加字段、做版本管理。
# 请求体设计class ChatRequest(BaseModel): messages: list[Message] # 对话历史 temperature: float = 0.7 # 白名单参数,别让用户传任意值 max_tokens: int = 2048 # 有上限,防烧钱 user_id: str # 租户/用户标识(计费、限流用) stream: bool = False # 是否流式
# 响应体class ChatResponse(BaseModel): id: str reply: str usage: Usage # token 数,用于计费/审计 latency_ms: int设计要点:
- 参数白名单化:只暴露 Temperature / Top_P / Max_Tokens 等几个,不暴露
--gpu-memory-utilization这类基础设施参数。 - 统一 Trace ID:每个请求带
x-request-id,贯穿业务服务 → vLLM → 日志 → 监控,排查问题全靠它。
流式与协议处理
- 流式:
stream=True时用 SSE(Server-Sent Events)转发 vLLM 的 token 流,逐 token 吐给前端,改善首字延迟体感。 - 非流式:等 vLLM 完整返回再回包。
- 超时控制:vLLM 单请求可能生成很久,业务层要设超时(如 120s)和取消传播(
asyncio取消请求时同步取消上游 fetch)。
鉴权与多租户
- API Key 鉴权:业务层先校验 Key → 映射到租户/用户 → 生成请求上下文。
- 租户隔离:不同租户的 QPS、并发、token 配额分开限制(
concurrency按租户切分,防止一个租户打满 10 并发饿死别人)。 - 配额/计费:按
usage.total_tokens记 Mock 计费,写审计日志。
限流与并发控制(关键!)
vLLM 只有 10 并发,这是全系统最硬的约束,业务层必须做并发闸门:
# 轻量信号量做并发闸门,超过即排队或返回 429semaphore = asyncio.Semaphore(10)
async def chat(req): if semaphore.locked(): # 超额定,返回 429 + Retry-After,或进队列 raise HTTPException(429, "server busy") async with semaphore: return await engine_call(req)- 队列 vs 拒绝:10 并发满时,选「排队(带超时)」还是「直接 429」?建议:短请求排队、长请求 429,避免队头阻塞。
- 区分层:业务层限流(限业务量)+ vLLM 内部
--max-num-seqs(兜底),两层叠闸。
缓存层
- 精确命中缓存:相同 prompt 的重复请求(如常见 QA)直接返回缓存,不进推理,省 GPU 又降延迟。
- 语义缓存(可选):RAG 场景做 embedding 相似度去重,能大幅降负载。
- 会话管理:多轮对话的历史存 Redis,别每次全量重传。
错误处理与重试
- vLLM 侧错误:区分「可重试」(超时、503 瞬时)和「不可重试」(400 参数错误、模型加载失败)。
- 重试策略:指数退避 + 抖动,但要小心——生成到一半的重试会浪费 token,只对「未开始生成」的请求重试。
- 降级:vLLM 挂了时,返回友好错误或降级到小模型/规则回复,别让用户看到 500 裸奔。
数据流与状态管理
业务请求 → 校验 → 限流 → 并发闸门 → 拼 prompt → 调 vLLM → 解析 → 校验输出 → 缓存 → 落库 → 返回- 无状态业务服务:业务服务本身无状态(可水平扩展多个副本),有状态的部分(会话、缓存、配额)全部放 Redis/DB。
- 单点瓶颈:vLLM 是唯一有状态单点。如果未来要扩并发,要么加 GPU 做多实例 + 负载均衡,要么上 vLLM 的分布式(PVC/DeepSeek 风格),但 10 并发阶段不需要。
后端工程化要点
| 维度 | 方案 |
|---|---|
| 语言/框架 | Python + FastAPI(生态与 vLLM/SDK 最搭);追求性能可换 Go,但开发成本高 |
| 异步 | 全链路 asyncio,一个请求不阻塞其他;vLLM 调用用 httpx.AsyncClient |
| 配置管理 | pydantic-settings + 环境变量,模型名、并发、显存参数全走配置,别写死 |
| 测试 | vLLM 契约测试(mock 掉推理,测业务逻辑);集成测试打真实 vLLM |
| CI/CD | Docker 镜像 + GitLab CI/GitHub Actions,自动构建、跑测试、部署 |
| 日志 | 结构化日志(JSON),带 trace_id、user_id、latency、token 数 |
| 可观测 | 业务指标:QPS、P95/P99 延迟、缓存命中率、429 率、token 消耗;vLLM 指标:TTFT/TPOT/GPU 利用率 |
| 告警 | 429 率上升、GPU 利用率高、P99 延迟超阈值 → 告警 |
目录结构(可直接落地的骨架)
app/├── main.py # FastAPI 入口├── config.py # 配置(pydantic-settings)├── api/│ ├── chat.py # 对话路由│ └── health.py # 健康检查├── core/│ ├── auth.py # API Key 鉴权│ ├── ratelimit.py # 限流│ ├── concurrency.py # 并发闸门│ └── tracing.py # trace_id 注入├── services/│ ├── llm_client.py # vLLM 客户端(OpenAI 兼容)│ ├── cache.py # 缓存│ └── quota.py # 配额/计费├── models/│ └── schemas.py # Pydantic 模型├── tests/│ ├── docker-compose.yml│ └── nginx.conf九、关键决策回顾
- 业务服务必须独立于 vLLM:别把认证、限流逻辑塞进 vLLM 配置里,它们职责不同,分开才能各自演进。
- 并发闸门是核心:10 并发是硬限制,业务层用信号量做闸门,配合 429/排队策略,这比任何调优都重要。
- 流式优先:首字延迟(TTFB)对用户体验影响最大,后端默认支持 SSE 流式。
- 先做契约测试再联调:vLLM 用 mock 掉,业务单测先跑通,最后再真机联调,省大量时间。
- 可观测性从第一天就上:trace_id + 结构化日志 + 延迟/token 指标,比事后补容易得多。
- 先定死参数再选硬件:上下文长度、并发上限、量化精度是决定显存成本的最大变量,先定死再反推配置。
- 10 并发是单卡分水岭:收敛到 8K 上下文 + 10 并发后,单卡 L40S 即可覆盖,成本可控。