部署与迁移
本文档面向运维:单机 systemd 部署、发行包安装、目录与权限、从
llmsproxy改名后的迁移与回滚。 返回 README。
1. 单机部署(推荐)
仓库根目录的 deploy.sh 负责:构建 → 预检配置 → 备份 → 原子替换二进制 →
重启服务 → 后置校验。它不修改源码,也不假设全新机器。
sudo ./deploy.sh # 默认读取仓库内 config.yaml
sudo ./deploy.sh --config other.yaml # 投放并部署另一份配置
部署目标(改名后):
两种布局并存,区别只在于二进制位置:包安装(deb/rpm)放
/usr/bin, unit 自带;源码部署(deploy.sh)放/usr/local/bin,需要让 unit 的ExecStart指向它。deploy.sh会校验这一点,写错时直接中止而不是留下 一个 203/EXEC 的服务。
| 项 | 路径(源码部署 / 包安装) |
|---|---|
| 二进制 | /usr/local/bin/modelrouter / /usr/bin/modelrouter |
| 配置 | /etc/modelrouter/config.yaml |
| 适配器 | /etc/modelrouter/adapters/ |
| 插件(可选) | /etc/modelrouter/plugins/(需在配置里设 plugin_dir) |
| 主密钥 | /etc/modelrouter/master.key(0600) |
| systemd unit | /etc/systemd/system/modelrouter.service |
deploy.sh 会在以下情况中止而不是"看起来成功":
- 部署后二进制起不来、或新配置
-check不通过; - 凭据无法解封(
master.key丢失 / 与配置不匹配); - 检测到改名前的
/etc/llmsproxy,但新配置尚未建立(否则会部署出一个 没有 key、没有源的空实例,所有请求都被拒)。
2. 发行包安装
三个产物由 packaging/core-dist.sh 生成到 cmd/build/dist/:
modelrouter_<ver>_amd64.deb
modelrouter-<ver>.x86_64.rpm
modelrouter-<ver>-linux-amd64.tar.gz
deb / rpm 安装后:
sudo systemctl enable --now modelrouter
sudo journalctl -u modelrouter -f
tar 包内只有:modelrouter、config.example.yaml、modelrouter.service、
migrate-to-modelrouter.sh、adapters/。
发行包不含业务插件(仓库只保留 billing / usage 两个与厂商无关的示例)。
它们保留在仓库中作为可选扩展,需要时手工放入 plugin_dir,网关不会自动安装、
覆盖或删除。这是有意的:插件属于运维资产,生命周期不与内核绑定。
首次启动会在 /etc/modelrouter/config.yaml 生成一份随机 admin key 的配置,
key 打印在启动日志里。发行包不预置任何可用 key(历史上曾因此泄露过一个真实的
sk-gw-local-0001)。
3. 目录与权限
/etc/modelrouter是唯一的读写根(systemd unit 用ProtectSystem=strict+ReadWritePaths=/etc/modelrouter固化这一点)。master.key必须 0600。当前 unit 以 root 运行:降权到普通用户需要先把master.key交给该用户并统一目录属主,否则服务会"带着明文落盘继续跑", 失败只留一行日志——这是独立变更,不要和加固混在一起。plugin_dir由运维维护;插件自带的状态文件也在其中。
4. 从 llmsproxy 迁移
二进制、配置目录、服务名、环境变量已统一为 modelrouter:
| 旧 | 新 |
|---|---|
/usr/local/bin/llmsproxy |
/usr/local/bin/modelrouter |
/etc/llmsproxy/ |
/etc/modelrouter/ |
llmsproxy.service |
modelrouter.service |
LLMSPROXY_PLUGIN_DIR |
MODELROUTER_PLUGIN_DIR |
LLMSPROXY_XDISPLAY_BASE |
MODELROUTER_XDISPLAY_BASE |
LLMS_PROXY_MASTER_KEY |
MODELROUTER_MASTER_KEY |
迁移脚本默认 dry-run,复制而非移动,旧目录永不删除:
sudo ./packaging/migrate-to-modelrouter.sh # 预演(逐条打印将做什么)
sudo ./packaging/migrate-to-modelrouter.sh --apply # 执行
它做五件事:复制状态目录并改写配置里的旧绝对路径、复核 master.key 0600、
安装新二进制(旧文件保留)、由旧 unit 生成新 unit(改写 ExecStart /
WorkingDirectory / ReadWritePaths)、复制 /usr/share 资源;最后跑
modelrouter -check 并 systemctl daemon-reload。
确认新服务可用后,再手工清理旧布局(脚本不会删):
sudo systemctl disable --now llmsproxy.service
sudo rm -f /etc/systemd/system/llmsproxy.service
sudo rm -rf /usr/local/bin/llmsproxy /etc/llmsproxy /usr/share/llmsproxy
回滚(不需要旧文件被删):
sudo ./packaging/migrate-to-modelrouter.sh --rollback --apply
读取侧兼容:旧配置路径与旧环境变量仍被接受,所以"先换二进制、后改 unit" 这种顺序不会立刻失效。但写入永远落在新路径,迁移完成后不要两台布局混用。
5. 部署后验证清单
# 1) 进程与监听
systemctl status modelrouter --no-pager
ss -ltnp | grep modelrouter
# 2) 鉴权生效(无 key 必须 401)
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/v1/models
# 3) 带 key 能列出模型
curl -s -H "Authorization: Bearer $KEY" http://127.0.0.1:8080/v1/models | head -c 400
# 4) 真实请求走通(证明 LuaJIT 适配器路径未被打断)
curl -s -H "Authorization: Bearer $KEY" \
-d '{"model":"AUTO","messages":[{"role":"user","content":"hi"}]}' \
http://127.0.0.1:8080/v1/chat/completions | head -c 400
# 5) 仓库文件与生产文件一致(部署脚本的收尾检查)
sha256sum /usr/local/bin/modelrouter build/modelrouter
第 4 步失败但第 3 步成功,通常是适配器或上游凭据问题,不是部署问题:
查看 journalctl -u modelrouter -n 100。