部署与迁移

本文档面向运维:单机 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。