用 OpenResty 搭一个轻量 LLM 网关
从运维视角出发,用 Nginx/OpenResty 为多个大模型后端做统一鉴权、限流与可观测,避免每个服务各搞一套。
把大模型接入生产环境时,最先暴露的往往不是模型本身,而是入口:鉴权散落在各处、没有统一限流、调用量和成本无法观测。与其在每个业务里重复造轮子,不如在入口加一层薄薄的网关。
为什么选 OpenResty
OpenResty 本质是 Nginx 加上 LuaJIT,既保留了 Nginx 的高并发反向代理能力,又能用 Lua 在请求生命周期里插入自定义逻辑。对运维来说,它的好处是:
- 复用现有的 Nginx 运维经验与配置习惯;
- 在
access_by_lua阶段做鉴权与限流,几乎零额外延迟; - 共享内存字典
lua_shared_dict天然适合做计数器。
统一鉴权
最小可用的做法是校验一个内部 API Key,并把调用方身份写入变量,供后续日志使用。
location /v1/ {
access_by_lua_block {
local key = ngx.var.http_x_api_key
if not key or key == "" then
return ngx.exit(401)
end
-- 这里可换成查 Redis / 内存表
ngx.var.caller = key
}
proxy_pass http://llm_upstream;
}
按调用方限流
用共享字典做一个简单的滑动窗口计数,超限直接 429:
local limit = ngx.shared.rate
local k = "rl:" .. ngx.var.caller
local count = limit:incr(k, 1, 0, 60) -- 60 秒窗口
if count > 600 then
return ngx.exit(429)
end
把成本变可观测
LLM 调用的成本和 token 强相关,因此日志里一定要带上 token 数。可以在 log_by_lua 阶段解析上游返回的 usage 字段,写入结构化日志,再交给 Loki / Prometheus 聚合:
| 维度 | 用途 |
|---|---|
| caller | 区分调用方、做成本分摊 |
| model | 不同模型单价不同 |
| prompt/completion tokens | 估算费用 |
| latency | 发现慢模型与超时 |
小结
一层薄网关就能把鉴权、限流、可观测三件事收口。先做最小可用版本,再按需要往里加灰度、缓存和熔断 —— 这正是运维擅长的渐进式演进。