给 vLLM 加一层 LiteLLM:统一入口、按调用方计费与自动故障切换
vLLM 只负责把请求算成 token,谁在调用、花了多少钱、后端挂了怎么切换,它一概不管。本文在 vLLM 前面加一层 LiteLLM 代理,对外只暴露一个 OpenAI 兼容接口,把计量、路由、鉴权这些运维工作收拢到一处。数据存进 PostgreSQL,TLS 由 nginx 终止,进程交给 systemd 托管。
上一篇文章讲的是推理引擎本身,这一篇讲引擎前面那扇“门”怎么搭。
引擎前面为什么还要再站一层
推理引擎只做一件事:把请求变成 token,再把 token 送回去。可一旦对外提供服务,就会冒出一堆没人接的活:
- 调用方拿什么凭证进来?
- 每个凭证已经花了多少钱?
- 某个后端挂了,流量怎么自动绕开?
- 几个副本之间又该怎么分配请求?
这些都不该由推理引擎操心,但少了任何一样,服务都跑不起来。
LiteLLM 就是补在引擎前面的代理层,对外充当唯一入口。它把“谁能进来、花了多少、后端挂了怎么办”集中到一处处理。
放到 AI 编程助手、氛围编程(vibe coding)工具的落地场景里看,这层“门”往往比算力本身更刚需——一套对外服务能不能收钱、能不能稳定跑下去,恰恰取决于这些看起来琐碎的环节。
各组件分别负责什么
| 角色 | 由谁承担 |
|---|---|
| 统一出口 | LiteLLM 对外提供 OpenAI 兼容接口,调用方只需记住一个地址 |
| 计量 | 按调用方发放虚拟密钥,并按密钥维度统计花费 |
| 路由 | 在多个重复部署的后端之间做健康检查与流量分配 |
| 存储 | PostgreSQL 存放 LiteLLM 管理界面的数据 |
| 入口安全 | nginx 负责 TLS 终止 |
| 进程管理 | systemd 托管服务进程 |
这里说的 TLS 终止,意思是在 nginx 这一层把加密连接的加解密做完,后面的服务只处理明文流量,不必各自维护证书。
安装:先填一个坑
uv tool install prisma
uv tool install 'litellm[proxy]'
有个坑得提前知道。LiteLLM 启动时会调用外部命令 prisma 执行 migrate deploy,也就是把数据库表结构同步到最新版本;但它自带的 schema 生成是坏的,开箱即用必然失败。解决办法是手动为已经装好的包生成一次:
LITELLM=~/.local/share/uv/tools/litellm
PATH="$LITELLM/bin:$PATH" "$LITELLM/bin/python" -m prisma generate \
--schema="$LITELLM/lib/python3.12/site-packages/litellm_proxy_extras/schema.prisma"
这一步做完,后面启动才不会被卡在数据库初始化上。
数据库与环境变量
先建库、建用户:
sudo -u postgres psql
postgres=# CREATE USER litellm WITH PASSWORD '<secret>';
postgres=# CREATE DATABASE litellm OWNER litellm;
然后是环境变量:
# ~/.config/litellm/litellm.env
export LITELLM_MASTER_KEY=sk-<key>
export LITELLM_PORT=443
export DATABASE_URL="postgresql://litellm:<user>@<host>:5432/litellm"
这里有个安全上的取舍值得说明。主密钥(master key,权限最高的那把钥匙)不写进配置文件,免得跟着配置一起泄漏。真正发给调用方使用的是虚拟密钥——它由主密钥派生而来,只能访问被授权的模型,额度也能单独限制。虚拟密钥统一在 LiteLLM 管理界面里生成,方便随时吊销、随时查看用量。
config.yml:同名后端自动故障切换
核心思路很简单:让两个后端条目共用同一个 model_name。同一个名字下面挂两个后端,路由器就会在它们之间轮转,并定期做健康检查;某个后端出问题后还会被“冷却”一段时间——也就是暂时不再往它上面发请求,等它恢复再放回流量池。
关键要点
- vLLM 只负责推理,对外入口、计量、路由、证书这些杂活交给 LiteLLM 代理。
- LiteLLM 对外提供 OpenAI 兼容接口,调用方只需记住一个地址。
- 主密钥不落配置文件;发给调用方的是按模型授权、按额度限制的虚拟密钥,可在管理界面统一吊销和查用量。
- 安装后需手动执行一次
prisma generate,否则启动会卡在数据库初始化阶段。 - 多个后端共用同一个
model_name,即可获得轮转、健康检查与故障冷却。
路由配置:一份可以直接照抄的 YAML
LiteLLM 最省事的一点,是把「一个模型名背后挂多台后端」这件事从代码里挪进了配置文件。下面这份 YAML 可以直接当骨架用:同一个 model_name 下面挂多个后端,路由器会自动在它们之间分摊请求。
model_list:
- model_name: glm-5.2
litellm_params:
model: openai/glm-5.2
api_base: http://<backend-1>:8000/v1
api_key: <secret>
stream_timeout: 900
model_info:
health_check_timeout: 5
input_cost_per_token: 0.00000035
output_cost_per_token: 0.00000175
- model_name: glm-5.2 # same name, second backend
...
general_settings:
background_health_checks: true
health_check_interval: 30
enable_health_check_routing: true
health_check_ignore_transient_errors: true
router_settings:
routing_strategy: simple-shuffle
enable_weighted_failover: true
num_retries: 2
retry_after: 1
timeout: 5
cooldown_time: 60
allowed_fails_policy:
AuthenticationErrorAllowedFails: 0
TimeoutErrorAllowedFails: 2
RateLimitErrorAllowedFails: 5
对开发者来说,好处很直接:调用方只认一个地址、一个模型名。后端从一台扩到三台,或者某台机器临时下线换新,上层代码一行都不用改。多个 AI 编程工具、Agent 同时打过来时,也不必给每个工具单独配一套后端地址。
这几类参数,各自管什么
模型与后端的绑定(model_list)
model_name 是客户端唯一需要知道的名字——背后有几台机器、跑的是什么模型,调用方不用关心,只写这一个名字就行。
api_base指向真正的 vLLM 后端地址;api_key用于鉴权;stream_timeout单独控制流式响应的等待上限。长文本的流式输出经常拖得很久,这里给到 900 秒,就是为了别让正常的长回答中途被掐断。
计费字段(input_cost_per_token / output_cost_per_token)
这两个字段分别记录输入、输出每个 token 的单价。token 是模型处理文本的最小单位,大致可以理解为「一小段字」。单看数字很小,乘上真实 token 用量之后就能直接换算成账单金额,是后面做「按调用方计费」的基础。
健康检查(general_settings)
健康检查说白了,就是隔一段时间给每台后端发个探针请求,看它还在不在、能不能正常回话。
background_health_checks:打开后台定时探活;health_check_interval:每 30 秒探一次;enable_health_check_routing:只把请求发给当前健康的实例;health_check_ignore_transient_errors:偶发抖动不算数,避免一次网络波动就把正常后端踢出集群。
路由与故障切换(router_settings)
这部分决定请求分给谁、出错之后怎么处理:
routing_strategy: simple-shuffle:随机打散,实现简单,也够用;num_retries/retry_after:失败后重试 2 次,每次间隔 1 秒;timeout: 5:单次连接超时 5 秒,快速失败,别让请求一直挂着;cooldown_time: 60:被判为不健康的后端冷却 60 秒,这段时间不再接流量;allowed_fails_policy:按错误类型区分容忍度。鉴权错误(AuthenticationErrorAllowedFails: 0)一次就下线——key 错了不会自己好;超时允许 2 次;限流允许 5 次,因为限流往往只是暂时的,没必要立刻摘掉一台其实还能用的机器。