
简介
我手里有多个渠道的大模型接口。平时用 Hermes 跑各种任务,换模型的时候不想每次去改 Hermes 的配置。所以搭了个 LiteLLM,把不同渠道的模型收进同一个 OpenAI 兼容接口。Hermes 只需要配置一次,之后想切换底层模型,在 LiteLLM 的管理页面把模型映射改一下就行。
先在本机跑通,确认模型真能调用,再搬到 Kubernetes。最终接了 PostgreSQL、Redis、Traefik 和 ArgoCD。这篇记录一下完整配置,还有中间碰到的几个坑。
为什么用 LiteLLM
LiteLLM 可以把不同厂商的模型包装成同一套 OpenAI 兼容 API。客户端只需要改 base_url、API Key 和模型名,不用管后面接的到底是什么渠道。
Hermes 平时通过模型名调模型。我用 LiteLLM 之后,Hermes 的模型名就固定指向 LiteLLM 里的一个入口,比如 main-fast。哪天想换更合适的模型,在 LiteLLM 后台把 main-fast 指到别的模型就行,Hermes 那边什么都不用动。
我现在配了 3 个模型入口,主要是给 Hermes 不同用途准备的:
main-fast:日常高频,快和省main-pro:复杂推理,能力最强main-vision:带视觉理解
入口名是我自己在 LiteLLM 里起的,指向不同渠道的真实模型。换模型只改映射,不动 Hermes。
数据库用 PostgreSQL,保存虚拟 Key、用量、模型配置和调用日志。Redis 用来做响应缓存,也给路由器共享状态。入口还是我常用的 Traefik,部署交给 ArgoCD。
目录结构
配置放在 aws-k8s/litellm:
litellm/
├── argocd.yaml
├── config.yaml
├── deploy.yaml
├── ingressroute.yaml
├── kustomization.yaml
└── svc.yaml
argocd.yaml 是 ArgoCD Application,不放进当前目录的 resources。剩下的资源由 Kustomize 渲染。
LiteLLM 配置
下面是精简后的 config.yaml。为了避免把真实凭据发到文章里,示例中的 Key 和密码都做了脱敏。当前仓库里的配置是直接写值,部署起来省事,但仓库必须保持私有。
model_list:
- model_name: main-fast
litellm_params:
model: openai/main-fast
api_base: https://llm.example.com/v1
api_key: os.environ/FAST_API_KEY
- model_name: main-pro
litellm_params:
model: openai/main-pro
api_base: https://llm.example.com/v1
api_key: os.environ/PRO_API_KEY
- model_name: main-vision
litellm_params:
model: openai/main-vision
api_base: https://llm.example.com/v1
api_key: os.environ/VISION_API_KEY
litellm_settings:
drop_params: true
cache: true
json_logs: true
default_fallbacks:
- main-fast
cache_params:
type: redis
host: redis
port: 6379
password: os.environ/REDIS_PASSWORD
ttl: 3600
router_settings:
redis_host: redis
redis_port: 6379
redis_password: os.environ/REDIS_PASSWORD
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY
database_url: os.environ/DATABASE_URL
store_model_in_db: true
store_prompts_in_spend_logs: true
background_health_checks: true
health_check_interval: 3600
这里有几个配置我觉得挺实用。
drop_params: true 会丢掉上游模型不支持的参数。不同厂商兼容 OpenAI API 的程度不一样,开着能少碰到一些参数报错。
default_fallbacks 配了兜底模型。默认调用失败时,LiteLLM 可以继续尝试备用模型。不过模型能力和返回风格可能不一样,别把 fallback 当成完全无感的切换。
store_model_in_db: true 允许从管理页面或 API 添加模型,配置会保存到 PostgreSQL。文件里写的模型不会消失,会和数据库里的模型一起加载。
store_prompts_in_spend_logs: true 会把请求和响应写进 Spend Logs。排查调用挺方便,但这东西会保存真实对话。多人使用或者有敏感数据时,最好关掉,或者至少设置日志保留时间。
用 Redis 做缓存
一开始我用的是本地缓存。放到 Kubernetes 后就换成 Redis:
litellm_settings:
cache: true
cache_params:
type: redis
host: redis
port: 6379
password: os.environ/REDIS_PASSWORD
ttl: 3600
router_settings:
redis_host: redis
redis_port: 6379
redis_password: os.environ/REDIS_PASSWORD
这两块不是一回事。
cache_params 缓存模型响应。相同请求再次进来时,可以直接返回 Redis 里的结果,省时间也省 token。
router_settings 里的 Redis 用来共享路由状态,比如限流计数和模型冷却。以后把 LiteLLM 扩成多个副本,这块状态不会散落在每个 Pod 里。
坑1. 缓存开了不等于一定能命中。
精确缓存很看请求内容。system prompt 里带时间、随机 ID,或者多轮聊天每次都多一段历史,缓存键都会变。FAQ、翻译、固定文本摘要这类请求更容易命中。
坑2. TTL 不要一上来设太长。
我先设成 3600 秒。实时性强的内容不应该长时间缓存。稳定的知识问答可以按业务改到 6 小时,甚至一天。
用 configMapGenerator 管配置
我没有手写 ConfigMap,而是让 Kustomize 根据文件生成:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deploy.yaml
- svc.yaml
- ingressroute.yaml
configMapGenerator:
- name: litellm-config
namespace: app
files:
- config.yaml
渲染后会得到类似这样的名字:
litellm-config-bkgbcc6hgc
Deployment 里的引用也会自动改成带 Hash 的名字。config.yaml 一变,Hash 跟着变,Deployment 的 Pod 模板也会变化,配置更新时就能触发滚动发布。不用自己维护 ConfigMap 名字。
Deployment 中这样挂载:
volumeMounts:
- name: config
mountPath: /app/config.yaml
subPath: config.yaml
readOnly: true
volumes:
- name: config
configMap:
name: litellm-config
源码里只写 litellm-config。最后的 Hash 由 Kustomize 处理。
Deployment
镜像用了固定版本,没有用 latest:
apiVersion: apps/v1
kind: Deployment
metadata:
name: litellm
namespace: app
spec:
replicas: 1
selector:
matchLabels:
app: litellm
template:
metadata:
labels:
app: litellm
spec:
imagePullSecrets:
- name: docker-registry-secret
containers:
- name: litellm
image: litellm/litellm:v1.99.1
args:
- --config
- /app/config.yaml
- --port
- "4000"
ports:
- name: http
containerPort: 4000
env:
- name: TZ
value: Asia/Shanghai
volumeMounts:
- name: config
mountPath: /app/config.yaml
subPath: config.yaml
readOnly: true
volumes:
- name: config
configMap:
name: litellm-config
当前的 Deployment 只设置了 TZ,敏感配置仍然跟着 config.yaml 一起挂载。更推荐的做法是用 Secret 注入这些环境变量:
FAST_API_KEY
PRO_API_KEY
VISION_API_KEY
LITELLM_MASTER_KEY
LITELLM_SALT_KEY
DATABASE_URL
REDIS_PASSWORD
我当前实际用的是 ConfigMap 整体挂载 + TZ 环境变量(见上面的真实片段),Secret 注入是更推荐的方向,后续会把敏感项都挪过去。
LITELLM_SALT_KEY 要固定下来。数据库中的模型凭据靠它加解密。模型写入数据库后再换 Salt Key,旧凭据就解不开了。
我现在只有一个副本。以后扩容时,PostgreSQL 和 Redis 已经在了,状态共享这块不用推倒重来。
Service 和 Traefik
Service 很简单,把 80 转到容器的 4000:
apiVersion: v1
kind: Service
metadata:
name: litellm
namespace: app
spec:
ports:
- name: http
port: 80
targetPort: http
selector:
app: litellm
type: ClusterIP
入口用 Traefik IngressRoute。HTTPS 由 letsencrypt 签证书,HTTP 统一跳 HTTPS:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: litellm
namespace: app
spec:
entryPoints:
- websecure
routes:
- kind: Rule
match: Host(`litellm.example.com`)
services:
- name: litellm
namespace: app
port: 80
tls:
certResolver: letsencrypt
交给 ArgoCD
argocd.yaml 指向这个目录:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: litellm
spec:
destination:
server: https://kubernetes.default.svc
source:
path: aws-k8s/litellm
repoURL: https://git.example.com/example/kubernetes-yaml
targetRevision: HEAD
project: default
syncPolicy:
automated: null
我没有打开自动同步,先手动确认 diff,再点同步。AI 网关里放着模型密钥和调用日志,稳一点没坏处。
部署前检查
先渲染一遍:
kubectl kustomize aws-k8s/litellm > /tmp/litellm.yaml
确认生成了这些资源:
ConfigMap/litellm-config-xxxxx
Service/litellm
Deployment/litellm
IngressRoute/litellm
IngressRoute/litellm-http
同步完成后检查:
kubectl -n app get pod,svc
kubectl -n app logs deploy/litellm -f
curl https://litellm.example.com/health/readiness
最后用 OpenAI 兼容接口跑一遍:
curl https://litellm.example.com/v1/chat/completions \
-H "Authorization: Bearer $LITELLM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "main-fast",
"messages": [
{"role": "user", "content": "只回复 ok"}
]
}'
能正常返回,再去看 PostgreSQL 的表、Redis 里的缓存,以及 LiteLLM 管理页面里的 Spend Logs。
最后
这套配置不复杂。LiteLLM 负责统一模型接口,PostgreSQL 管持久数据,Redis 管缓存和共享状态,Kustomize 负责配置变更,ArgoCD 负责部署。
后面准备再补健康检查、资源限制、日志自动清理和虚拟 Key 的预算限制。先让它稳定跑起来,再慢慢加。
欢迎关注我的博客 www.bboy.app
Have Fun
