本地跑大模型的坑,多半不在模型本身,而在接入层。模型下载好了、推理服务也起来了,结果客户端要连的时候发现一团乱麻:Ollama 一个端口、vLLM 一个端口、Embedding 模型又一个服务,每个的 API 格式还不完全一样,代码里写死了一堆 base_url 和适配逻辑,换个环境又得改一遍。
我自己折腾过几轮之后,最直接的解法就是上一个本地大模型网关,然后用 CLI 来管理它。这篇文章就把我实操下来的完整过程写清楚:本地大模型网关解决什么问题、CLI 怎么装怎么配、日常怎么用、出了坑怎么排查。适合已经在本地部署了 LLM、想统一管理多个模型入口的开发者,也适合团队里准备搭一套共享推理服务的同学参考。
1. 为什么本地部署大模型需要网关
1.1 单模型直连的痛点
先说说我踩过的实际场景。最开始我只是在笔记本上用 Ollama 跑 Llama 3,那时候不需要网关,客户端直接指向http://localhost:11434就行。但随着项目深入,问题开始冒出来:
- 团队里其他人也要用这个模型,我不可能把自己终端开着当服务器,更不可能把本机 IP 直接甩给他们。
- 同时跑了好几个模型,有的在 Ollama 上,有的用 vLLM 部署,端口和 API 路径全都不一样。
- 有些内部工具只认 OpenAI 格式的接口,Ollama 原生接口虽然兼容,但字段总有细微差别,联调时反复出问题。
- 想给不同的人分配不同密钥、限制调用频率,完全没有现成机制。
这些问题单独看都不算大,凑在一起就是灾难。每次接入一个新客户端,都要去查这个模型跑在哪个端口、用什么格式,效率极低。
1.2 网关在本地部署里的定位
网关本质上就是一个流量入口,把所有后端推理服务的差异屏蔽掉,对外暴露一套统一的 API。你可以把它理解成公司前台:你不需要知道某个部门在第几层哪个工位,只要把需求告诉前台,前台帮你转达。
在本地大模型的场景里,网关承担这几个核心职责:
- 统一入口:所有模型都通过一个地址访问,客户端只配一个 base_url。
- 协议转换:后端可以是 Ollama、vLLM、TGI 等任意推理框架,网关统一转成 OpenAI 兼容格式。
- 路由转发:根据请求里的 model 字段,把流量转发给对应的后端服务。
- 认证鉴权:给不同用户或应用分配不同 API Key,控制谁能用哪个模型。
- 负载均衡与限流:同一个模型部署了多个副本时做分发,防止单个实例被打爆。
简单说,网关解决的是“模型多了以后怎么管”的问题。单模型单用户阶段完全不需要,一旦跨过这个阶段,它就是刚需。
1.3 为什么用 CLI 而不是 Web UI
现在很多网关工具都带 Web 控制台,点鼠标就能配。那为什么我推荐 CLI?
首先,CLI 可以脚本化。我的网关配置是要跟着项目走的,用配置文件加命令行的方式,整个配置可以放进 Git 里做版本管理,改了什么一目了然,出问题也能回滚。Web 界面点点点,别人问你“上次改了啥”,你根本说不清楚。
其次,CLI 适合自动化。比如 CI/CD 流程里要临时起一个网关实例跑测试,一条命令搞定,Web 界面做不到。包括批量生成密钥、批量刷新配置,CLI 的效率远高于鼠标操作。
最后,CLI 的调试体验更好。配完之后直接用 curl 验证,输出干净直接,不会有浏览器那堆干扰信息。我自己日常 90% 的操作都在终端里完成,CLI 是最贴合这种习惯的方式。
2. 环境准备与网关安装
2.1 硬件与系统要求
先说结论:网关本身不挑配置,它就是一层代理,主要消耗是内存和少量 CPU。真正吃资源的是后端推理服务。
我的参考配置:
- 网关节点:2 核 CPU、4GB 内存即可,跑 Docker 或直接裸装都行。
- 后端推理节点:取决于模型大小。7B 模型量化版大概需要 8GB 显存,13B 需要 16GB 左右,70B 就别想单卡了。
- 系统:Ubuntu 22.04 / Debian 12 都试过,macOS 也能跑,Windows 建议用 WSL2。
如果你只是本地单机测试,网关和推理服务放同一台机器完全没问题,端口错开就行。
2.2 网关软件选型
市面上的选择其实不少,我把自己实际用过的几类列出来对比:
| 方案 | 定位 | 优点 | 缺点 |
|---|---|---|---|
| LiteLLM | AI 原生网关 | 轻量、纯 Python、OpenAI 格式原生支持、CLI 友好 | 高并发场景性能不如 C 系网关 |
| APISIX / Kong | 通用 API 网关 | 性能强、插件生态丰富、适合大规模生产 | 配置复杂,AI 场景要自己写插件 |
| Higress | 云原生网关 | 有 AI 插件,和 K8s 集成好 | 偏云环境,本地部署略重 |
| 自研 Nginx 反代 | 最简单方案 | 零依赖,纯转发 | 没有鉴权、限流、路由能力,只是个壳 |
我自己主推 LiteLLM,原因很直接:它就是为“把各种模型统一成 OpenAI 兼容接口”这个场景设计的,模型路由、密钥管理、限流都是开箱即用,而且提供了完整的 CLI。这篇文章的实操部分也以它为主线。
如果你后续流量很大、需要极致性能,可以再加一层 APISIX 做流量入口,LiteLLM 做 AI 网关层,各司其职。但这是后话,别一上来就整太复杂。
2.3 安装 LiteLLM 与初始化配置
LiteLLM 是 Python 包,安装很简单,建议用虚拟环境隔离:
python3 -m venv litellm-env source litellm-env/bin/activate pip install 'litellm[proxy]'安装完确认版本:
litellm --version然后创建一个工作目录和配置文件。我的习惯是建一个/opt/litellm放配置和日志,目录结构如下:
mkdir -p /opt/litellm/config mkdir -p /opt/litellm/logs cd /opt/litellm初始配置文件config.yaml长这样:
model_list: - model_name: llama3 litellm_params: model: ollama/llama3 api_base: http://localhost:11434 - model_name: deepseek-coder litellm_params: model: vllm/deepseek-coder-6.7b api_base: http://localhost:8000/v1 general_settings: master_key: sk-your-master-key database_url: sqlite:///litellm.db这里model_name是对外暴露的模型名,客户端请求时用的就是这个名字;litellm_params里面写后端的真实地址和框架类型。master_key是网关的管理员密钥,所有管理操作都要用它认证。
3. 核心配置实操:从零接入本地模型
3.1 配置 Ollama 模型作为上游服务
我先把最简单的场景讲清楚:Ollama 跑在localhost:11434,网关对外暴露成 OpenAI 兼容接口。
配置文件里已经写好了llama3这一段,现在启动网关:
litellm --config config.yaml --port 4000启动成功后,终端会输出当前监听的地址和可用模型列表。用 curl 直接测试:
curl http://localhost:4000/v1/models \ -H "Authorization: Bearer sk-your-master-key"能看到返回的模型列表里有llama3,说明网关已经把 Ollama 的模型接管了。
接下来测试对话接口:
curl http://localhost:4000/v1/chat/completions \ -H "Authorization: Bearer sk-your-master-key" \ -H "Content-Type: application/json" \ -d '{ "model": "llama3", "messages": [{"role": "user", "content": "你好,介绍一下自己"}] }'如果一切正常,返回的就是 OpenAI 格式的响应。到这里,你的客户端已经可以完全忽略 Ollama 的存在,只用 OpenAI SDK 就能调用本地模型。
3.2 接入 vLLM 高并发服务
如果模型并发量上来,Ollama 顶不住,通常会用 vLLM 部署。vLLM 本身已经提供 OpenAI 兼容接口,为什么还要过一层网关?
因为直接暴露 vLLM 的端口有几个问题:一是没有鉴权,谁拿到端口谁就能用;二是多模型场景下每个 vLLM 实例一个端口,客户端要维护多个地址;三是没法做精细的流量控制。
配置方式一样,在model_list里加一项:
- model_name: qwen2.5-14b litellm_params: model: vllm/qwen2.5-14b-instruct api_base: http://localhost:8000/v1 api_key: dummy注意api_key这里填dummy是因为 vLLM 默认不校验密钥,但 LiteLLM 内部要求这个字段存在。如果 vLLM 那边配了真实的 API Key,这里就填真的。
改完配置重启网关:
kill $(pgrep -f "litellm --config") litellm --config config.yaml --port 4000这里我偷了个懒,直接用kill杀进程。正规做法是用 systemd 管理,后面第 4 章会讲。
3.3 API Key 管理与访问控制
现在网关已经能转发多个模型了,但有个问题:master_key权限太大了,发给谁都危险。实际使用中需要给不同的人或应用生成独立的 Key。
LiteLLM 提供了专门的接口生成子 Key。启动网关后,在另一个终端执行:
curl -X POST http://localhost:4000/key/generate \ -H "Authorization: Bearer sk-your-master-key" \ -H "Content-Type: application/json" \ -d '{ "duration": "30d", "models": ["llama3"], "max_budget": 10.0 }'这条命令的意思是:生成一个有效期 30 天、只能访问llama3这个模型、总预算 10 美元的 Key。返回结果里key字段就是你要发给使用者的 Key。
提示:预算单位是美元,但本地部署没有实际计价,你可以把它当成一个配额数字来理解。比如
max_budget: 1000就表示允许消耗 1000 个单位的配额。
用生成的 Key 调用:
curl http://localhost:4000/v1/chat/completions \ -H "Authorization: Bearer sk-generated-key-xxxx" \ -H "Content-Type: application/json" \ -d '{"model": "llama3", "messages": [{"role": "user", "content": "hi"}]}'如果尝试访问没被授权的模型,比如:
curl http://localhost:4000/v1/chat/completions \ -H "Authorization: Bearer sk-generated-key-xxxx" \ -H "Content-Type: application/json" \ -d '{"model": "deepseek-coder", "messages": [{"role": "user", "content": "hi"}]}'网关会直接返回401或403,提示没有权限。这个机制在多人共享一台推理服务器时特别有用。
3.4 客户端代码接入示例
服务端配好了,客户端接入非常简单。用 OpenAI 的 Python SDK,只改两个地方:base_url和api_key。
from openai import OpenAI client = OpenAI( base_url="http://localhost:4000/v1", api_key="sk-generated-key-xxxx", ) response = client.chat.completions.create( model="llama3", messages=[{"role": "user", "content": "讲个冷笑话"}], ) print(response.choices[0].message.content)代码量没有变化,只是把原来指向https://api.openai.com的地址换成局域网内的网关地址。这就是网关的价值:接入方完全无感,该干嘛干嘛。
其他语言也一样,只要是 OpenAI 兼容的 SDK,改base_url就能用。包括 TypeScript、Java、Go,原理完全相同。
4. 日常运维与进阶玩法
4.1 用 systemd 管理网关进程
刚才启动网关用的litellm --config config.yaml这种方式,终端一关进程就没了。真实场景下必须把网关注册成系统服务,让它开机自启、崩溃自动拉起。
在/etc/systemd/system/litellm.service写入:
[Unit] Description=LiteLLM Proxy After=network.target [Service] User=ubuntu WorkingDirectory=/opt/litellm Environment="PATH=/opt/litellm-env/bin" ExecStart=/opt/litellm-env/bin/litellm --config /opt/litellm/config.yaml --port 4000 Restart=always RestartSec=3 [Install] WantedBy=multi-user.target然后执行:
sudo systemctl daemon-reload sudo systemctl enable litellm sudo systemctl start litellm查看状态和日志:
sudo systemctl status litellm sudo journalctl -u litellm -f用 systemd 管理之后,改配置只需要执行:
sudo systemctl restart litellm比手动杀进程干净得多。
4.2 负载均衡与多副本配置
当模型流量大到单实例撑不住时,vLLM 可能会在同一台机器或多个机器上起多个副本。LiteLLM 可以对同一个模型配置多个上游地址,自动做负载均衡。
配置方式:
model_list: - model_name: qwen2.5-14b litellm_params: model: vllm/qwen2.5-14b-instruct api_base: http://192.168.1.10:8000/v1 api_key: dummy model_info: weight: 1 - model_name: qwen2.5-14b litellm_params: model: vllm/qwen2.5-14b-instruct api_base: http://192.168.1.11:8000/v1 api_key: dummy model_info: weight: 2这里model_name相同,但api_base不同。LiteLLM 会按照weight的比例分发请求,第二个实例配置了权重 2,理论上会承担两倍流量。
提示:负载均衡的前提是多个副本加载的是同一个模型,并且状态是独立无依赖的。如果你的应用依赖对话历史或者会话状态,负载均衡可能会导致上下文丢失。
4.3 限流与配额控制
多人共享网关时,最怕的就是某个人跑了个死循环脚本,把资源全占了。LiteLLM 的限流配置在general_settings里:
general_settings: master_key: sk-your-master-key database_url: sqlite:///litellm.db rate_limit: rpm: 60 tpm: 100000rpm是每分钟最多请求数,tpm是每分钟最多 token 数。超过限制的请求会返回429 Too Many Requests。
你也可以针对单个 Key 设置更严格的限制,创建 Key 时加参数:
curl -X POST http://localhost:4000/key/generate \ -H "Authorization: Bearer sk-your-master-key" \ -H "Content-Type: application/json" \ -d '{ "models": ["llama3"], "rpm": 10, "tpm": 20000 }'这样这个 Key 每分钟最多 10 个请求、2 万 token,适合给测试环境或者临时接入的同事用。
4.4 日志与监控
网关的日志是排查问题的第一手资料。LiteLLM 默认在控制台输出请求日志,但如果用 systemd 管理,日志会进 journald,用journalctl查看。
更实用的是把请求日志写到单独的文件里。在配置里加:
general_settings: master_key: sk-your-master-key database_url: sqlite:///litellm.db log_file: /opt/litellm/logs/access.log log_level: INFO重启后,每个请求的模型名、响应时长、状态码都会记录到文件里。我通常用这条命令统计每个模型的调用量:
awk '{print $model}' /opt/litellm/logs/access.log | sort | uniq -c具体字段格式可能随版本变化,但思路一样:先看日志确认字段,再做统计。
4.5 配置热加载与版本管理
CLI 最大的好处是配置即代码。我把config.yaml、systemd 服务文件、初始化脚本都放进 Git 仓库,改了配置提交 MR、review 通过再部署。
如果不想每次改配置都重启网关,LiteLLM 还支持配置热加载。启动时加--reload参数:
litellm --config config.yaml --port 4000 --reload这样修改config.yaml后,网关会自动重新加载配置,不需要手动重启。我在本地开发环境一直开这个参数,生产环境不开——生产环境追求稳定可控,改动必须显式走 restart。
5. 常见问题与排查技巧实录
5.1 网络类问题:连不上网关
这是最先遇到的坑。网关启动正常,但客户端从另一台机器访问不到,先按这个顺序排查:
- 端口监听:确认网关监听的是
0.0.0.0而不是127.0.0.1。LiteLLM 默认应该是全网卡,但如果用了 Docker 就要注意端口映射参数。 - 防火墙:Ubuntu 上我经常遇到 ufw 拦着,放行端口:
sudo ufw allow 4000/tcp- 同一网段:如果网关和客户端跨网段,检查路由和网关配置,确保网络连通。
用curl -v看详细连接过程,能区分是 TCP 层不通还是应用层报错。
5.2 认证问题:401 Unauthorized
密钥相关的报错,排查思路相对固定:
- 确认请求头是
Authorization: Bearer <key>,不是api_key之类的自定义头。 - 检查 Key 是否过期。生成的 Key 带有效期,过期后需要重新生成。
- 如果是刚改过
master_key,旧 Key 会全部失效,需要重新生成。 - 在日志里看具体报错,正常会明确提示是 Key 不存在还是权限不足。
我自己踩过的坑是:把master_key直接发给别人用了,后来要回收权限很麻烦。正确做法是master_key只留给自己管理,对外一律生成子 Key。
5.3 后端连接问题:上游服务无响应
网关返回502 Bad Gateway或503,说明网关本身是通的,但后端推理服务连不上。排查步骤:
- 先跳过网关,直接 curl 后端地址,确认服务正常:
curl http://localhost:8000/v1/models- 确认配置文件里的
api_base是否正确,端口有没有写错。 - 如果后端是 vLLM,可能还在加载模型阶段,此时请求会排队或拒绝,等模型加载完成再试。
- 如果后端机器和网关不在同一台机器,检查后端机器的防火墙是否放行了对应端口。
这类问题我遇到最多的是地址写错:配了localhost,但网关和后端是两台机器。localhost永远指向本机,跨机器必须写实际 IP。
5.4 模型相关报错:Model Not Found
请求时返回404,提示模型不存在,原因基本是这两种:
- 客户端传的
model字段和配置里的model_name不一致。检查配置文件里model_list中的名字,一字不差地复制过去。 - 配置改了没重启,或者没用
--reload。确认网关加载的是最新配置,可以请求/v1/models接口查看实际生效的模型列表。
有一个小技巧:配置model_name时不要用太绕的名字,否则客户端容易拼错。保持简单直观,比如llama3、qwen-14b这种。
5.5 性能问题:响应慢或超时
同样的请求,直连后端很快,过网关就慢。可能的原因:
- 网关内存不足:LiteLLM 默认会有一些缓存和统计功能,内存太小会导致 GC 频繁。看日志是否有内存相关告警。
- SQLite 锁竞争:如果用 SQLite 做数据库,高频写入时会有锁竞争。请求量大时换成 PostgreSQL 或 MySQL。
- 超时设置不当:大模型的推理时间本来就长,默认超时时间可能不够。在配置里调大:
general_settings: request_timeout: 600单位是秒,600 秒足够覆盖绝大多数场景。别调太小,否则长上下文生成任务会被误杀。
5.6 日志排查实战
最后分享一个实战案例。有一次同事反馈说某个 Key 调用偶尔失败,我直接看日志:
sudo journalctl -u litellm -f找到一条429 rate limit exceeded的记录,原来是这个 Key 的rpm设为 10,但同事写了个并发脚本,瞬间打满限额。把限额改成 50 并通知他控制并发,问题解决。
日志的价值就在这:问题发生时有据可查,不用瞎猜。所以从第一天起就要把日志管好,别等出了事再补。
6. 我的一点实操感悟
网关这东西,单机单模型阶段确实用不上,但一旦开始搞多模型、多人协作,它就是性价比最高的投资。CLI 管理配合 Git 版本控制,整个网关配置可以做到完全可追溯,这在团队协作里太重要了。
我自己实操下来的心得很简单:先拿 Ollama 跑通一条链路,再加 vLLM,再上密钥和限流,一步步来,别一口吃成胖子。配置文件的每个字段都搞清楚含义再改,别从网上复制一大段就跑,出了问题你根本不知道从哪排查。
最后再分享一个小技巧:把常用的网关操作封装成几个 shell 脚本,比如gw-start.sh、gw-restart.sh、gw-logs.sh,放仓库里共享。团队其他人不需要记住 systemd 命令,直接执行脚本就行。省心,也少踩坑。