docs: 添加项目说明与生产部署文档

This commit is contained in:
dekun
2026-05-19 02:08:06 +08:00
parent a98b24fa32
commit c21707b111
2 changed files with 493 additions and 118 deletions
+280 -118
View File
@@ -1,181 +1,343 @@
# LLM 中转网关 · PM2 部署说明 # openai_node · 生产部署文档
本文说明如何在服务器上使用 **PM2** 守护 **FastAPIuvicorn** 进程,实现崩溃自动拉起、开机自启与日志管理。 本文说明如何在 **VPS** 上部署本网关,并通过 **宝塔面板** 反向代理、**frp** 汇聚多台本地 Ollama 节点。适用于当前推荐拓扑:
- 网关监听:**8150**
- 宝塔反代:域名 → `127.0.0.1:8150`
- 各 PC 模型端口:**33133318**frp 映射到 VPS 本机 `127.0.0.1`
代码仓库:[https://git.bz121.com/dekun/openai_node.git](https://git.bz121.com/dekun/openai_node.git)
--- ---
## 1. 前置条件 ## 1. 部署拓扑
| 组件 | 说明 | ```
|------|------| Internet
| **Python** | 建议 3.10+,已加入 `PATH` |
| **Node.js** | 用于安装 PM2LTS 即可) |
| **PM2** | `npm i -g pm2` | ┌─────────────────┐
| **本项目** | 含 `main.py``requirements.txt`,可选 `ecosystem.config.cjs` | │ VPS │
│ 宝塔 Nginx:443 │
│ │ │
│ ▼ │
│ openai_node │
│ :8150 │
│ │ │
│ 127.0.0.1:3313 ├─── frp ─── PC1 Ollama :3313
│ 127.0.0.1:3314 ├─── frp ─── PC2 Ollama :3314
│ 127.0.0.1:3315 ├─── frp ─── PC3 Ollama :3315
└─────────────────┘
```
上游本地大模型(默认 `http://127.0.0.1:10434`)需在同一台机器或内网可达;若在大模型同一主机部署网关,确保端口未被占用(下文示例为 **8000**)。 要点:
1. **不要**把 3313 等端口直接暴露到公网,仅 VPS 本机访问。
2. **不要**让宝塔直接把域名反代到各 PC;应只反代到 **8150**,由网关按 `model` 调度。
3. `nodes.json``host`**`127.0.0.1`**`port` 填 frp 映射后的端口。
--- ---
## 2. 准备运行环境 ## 2. 前置条件
**项目根目录**(与 `main.py` 同级)执行: | 组件 | 版本建议 | 用途 |
|------|----------|------|
| Python | 3.10+ | 运行网关 |
| Node.js | LTS | 安装 PM2 |
| PM2 | 最新 | 进程守护 |
| 宝塔 | 7.x+ | Nginx 反代、SSL |
| frp | 客户端 + 服务端 | 内网穿透(可用其它方案,原理相同) |
### Linux / macOS 各 PC 已安装 **Ollama** 并监听本地端口(如 3313),且与 VPS 网络可达(通过 frp)。
---
## 3. 在 VPS 上部署网关
### 3.1 克隆代码
```bash
cd /www/wwwroot # 按宝塔网站目录调整
git clone https://git.bz121.com/dekun/openai_node.git
cd openai_node
```
### 3.2 虚拟环境与依赖
```bash ```bash
cd /path/to/中转网关
python3 -m venv venv python3 -m venv venv
source venv/bin/activate source venv/bin/activate
pip install -r requirements.txt -U pip install -r requirements.txt -U
``` ```
### WindowsPowerShell ### 3.3 配置文件
```powershell
cd C:\path\to\中转网关
python -m venv venv
.\venv\Scripts\Activate.ps1
pip install -r requirements.txt -U
```
在项目目录创建 **`gateway.json`**(可复制 `gateway.json.example`),填写 **`username`**、**`password`****`api_key`** 可留空,首次启动会自动写入 `sk-...`。也可用环境变量 **`GATEWAY_CONFIG`** 指定配置文件绝对路径。
确认可手动启动:
```bash ```bash
python -m uvicorn main:app --host 0.0.0.0 --port 8000 cp gateway.json.example gateway.json
cp nodes.json.example nodes.json
chmod 600 gateway.json nodes.json
``` ```
浏览器访问 `http://服务器IP:8000` 正常后再交给 PM2 编辑 **`gateway.json`**:设置 `username``password``api_key` 可留空自动生成
编辑 **`nodes.json`**(示例三台):
```json
{
"nodes": [
{
"id": "node-1",
"name": "书房电脑",
"host": "127.0.0.1",
"port": 3313,
"enabled": true,
"max_concurrent": 1,
"models": [
{ "id": "qwen2.5:14b", "label": "千问 14B" }
]
}
]
}
```
`models[].id` 必须与 Ollama 模型名、API 请求中的 `model` **完全一致**
也可部署后浏览器打开 **系统设置** 页面维护(需先登录)。
### 3.4 环境变量
| 变量 | 生产建议 |
|------|----------|
| `JWT_SECRET` | **必改**,长随机字符串 |
| `GATEWAY_PORT` | `8150`(与 PM2、宝塔一致) |
可选:
| 变量 | 说明 |
|------|------|
| `STATS_DB` | 统计库路径,默认 `gateway_stats.db` |
| `NODES_CONFIG` | 节点配置路径,默认 `nodes.json` |
| `UPSTREAM_URL` | 仅在没有配置 `nodes.json` 节点时作为单机上流 |
### 3.5 手动试跑
```bash
export JWT_SECRET="你的随机密钥"
python -m uvicorn main:app --host 0.0.0.0 --port 8150
```
确认:
```bash
curl -s http://127.0.0.1:8150/api/models/cards | head
curl -s http://127.0.0.1:3313/v1/models # frp 通了才有返回
```
浏览器访问 `http://VPS_IP:8150`,首页应出现模型卡片。
--- ---
## 3. 环境变量(生产必改) ## 4. PM2 守护
| 变量 | 含义 | 建议 | 项目已包含 **`ecosystem.config.cjs`**(端口 **8150**)。
|------|------|------|
| `JWT_SECRET` | 签发网页登录 JWT 的密钥 | 长随机字符串,勿泄露 |
| `UPSTREAM_URL` | 上游 OpenAI 兼容服务根地址(无末尾 `/` | 默认 `http://127.0.0.1:10434`,按实际修改 |
可在 shell 中导出,或在 PM2 的 `env` / `ecosystem.config.cjs` 中配置(见下文)。 ### 4.1 修改 PM2 配置
--- 编辑 `ecosystem.config.cjs``env.JWT_SECRET` 为生产密钥。
## 4. 使用 PM2 启动(推荐 ecosystem ### 4.2 启动
项目根目录已提供 **`ecosystem.config.cjs`**:自动根据 **Windows / 非 Windows** 选择虚拟环境中的 Python 解释器,并启动:
`python -m uvicorn main:app --host 0.0.0.0 --port 8000`
### 4.1 修改配置
1. 用编辑器打开 `ecosystem.config.cjs`
2.`env` 里填写 **`JWT_SECRET`**(必填),按需修改 **`UPSTREAM_URL`**。
3. 若端口冲突,可在 `args` 中把 `8000` 改成其它端口。
### 4.2 启动与常用命令
在项目根目录执行:
```bash ```bash
cd /www/wwwroot/openai_node
source venv/bin/activate
pm2 start ecosystem.config.cjs pm2 start ecosystem.config.cjs
pm2 save pm2 save
pm2 status
``` ```
常用运维 常用命令
| 命令 | 作用 | | 命令 | 作用 |
|------|------| |------|------|
| `pm2 status` | 查看进程状态 | | `pm2 logs llm-gateway` | 查看日志 |
| `pm2 logs llm-gateway` | 实时日志 | | `pm2 restart llm-gateway` | 重启(改代码/配置后) |
| `pm2 restart llm-gateway` | 重启 |
| `pm2 stop llm-gateway` | 停止 | | `pm2 stop llm-gateway` | 停止 |
| `pm2 delete llm-gateway` | 从 PM2 列表移除 |
修改代码或依赖后:`pip install …` 完成再执行 `pm2 restart llm-gateway` 更新代码后:
---
## 5. 不用配置文件时的等价命令
若暂时不用 `ecosystem.config.cjs`,需自行写出虚拟环境里 **Python 可执行文件绝对路径**
**Linux 示例:**
```bash ```bash
pm2 start /path/to/中转网关/venv/bin/python \ git pull
--name llm-gateway \ source venv/bin/activate
--cwd /path/to/中转网关 \ pip install -r requirements.txt -U
--interpreter none \ pm2 restart llm-gateway
-- -m uvicorn main:app --host 0.0.0.0 --port 8000
``` ```
**Windows 示例(PowerShell,路径按实际修改):** ### 4.3 开机自启
```powershell
pm2 start "C:\path\to\中转网关\venv\Scripts\python.exe" `
--name llm-gateway `
--cwd "C:\path\to\中转网关" `
--interpreter none `
-- -m uvicorn main:app --host 0.0.0.0 --port 8000
```
然后通过 PM2 设置环境变量(任选其一):
```bash
pm2 restart llm-gateway --update-env
```
或在启动前于终端导出 `JWT_SECRET``UPSTREAM_URL` 后再 `pm2 start …`(注意:`--update-env` 与持久化策略以当前 PM2 版本文档为准)。
---
## 6. 开机自启
PM2 保存当前进程列表后,生成并启用开机脚本:
```bash ```bash
pm2 save pm2 save
pm2 startup pm2 startup
# 按提示执行输出的 sudo 命令
``` ```
终端会打印一行以 `sudo` 开头的命令,**复制执行**即可完成 systemd(Linux)或对应平台的自启配置。Windows 可使用 **pm2-windows-service** 或任务计划程序配合 `pm2 resurrect`,按环境选用。 ---
## 5. 宝塔反向代理
### 5.1 添加站点
宝塔 → **网站** → 添加站点 → 绑定域名 → 申请 **SSL**Lets Encrypt)。
### 5.2 反向代理到 8150
网站 → **设置****反向代理** → 添加:
| 项 | 值 |
|----|-----|
| 目标 URL | `http://127.0.0.1:8150` |
| 发送域名 | `$host` |
**配置文件** 或「高级」中建议补充(流式 LLM 必备):
```nginx
location / {
proxy_pass http://127.0.0.1:8150;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
```
说明:
- `X-Forwarded-For` / `X-Real-IP` 用于网关 **流量统计** 记录真实客户端 IP。
- `proxy_buffering off` 与长超时避免流式输出卡顿或中断。
### 5.3 验证
```bash
curl -I https://你的域名/
```
应返回 200。登录 Web → 首页查看模型卡片状态。
--- ---
## 7. 日志与配置文件 ## 6. frp 多机穿透(示例)
| 项 | 说明 | 以下为常见做法,按你实际 frp 版本调整。
### 6.1 VPS 运行 frps
`/etc/frp/frps.toml` 示例:
```toml
bindPort = 7000
```
### 6.2 各 PC 运行 frpc
`/etc/frp/frpc.toml` 示例(书房电脑,本地 Ollama 3313):
```toml
serverAddr = "VPS公网IP"
serverPort = 7000
[[proxies]]
name = "ollama-pc1"
type = "tcp"
localIP = "127.0.0.1"
localPort = 3313
remotePort = 3313
```
其它电脑分别映射 **3314、3315** … 到 VPS 的 **3314、3315**
### 6.3 在 VPS 上验证
```bash
curl http://127.0.0.1:3313/v1/models
curl http://127.0.0.1:3314/v1/models
```
均正常后,在 Web **系统设置** 中为对应节点点击 **测试**,或等待首页健康检查(约 30 秒)变为 **空闲**
---
## 7. Ollama 侧注意
1. Ollama 需监听 `0.0.0.0` 或 frp 能访问的地址(仅内网/frp,勿公网裸露)。
2. API 请求中的 `model` 使用 `ollama list` 中的名称,与 `nodes.json` 一致。
3. 流式统计 Token 依赖响应中的 `usage`;可在请求中加 OpenAI 兼容参数(若上游支持):
```json
"stream_options": { "include_usage": true }
```
---
## 8. 客户端接入
| 项 | 值 |
|----|-----|
| Base URL | `https://你的域名/v1` |
| API Key | `gateway.json` 的 `api_key` 或用户中心复制 |
示例:
```bash
curl https://你的域名/v1/chat/completions \
-H "Authorization: Bearer sk-你的密钥" \
-H "Content-Type: application/json" \
-d '{"model":"qwen2.5:14b","messages":[{"role":"user","content":"hi"}]}'
```
---
## 9. 运维与数据文件
| 路径 | 说明 | 备份 |
|------|------|------|
| `gateway.json` | 账号、API Key | 建议 |
| `nodes.json` | 节点与模型 | 建议 |
| `gateway_stats.db` | 访问与 Token 统计 | 可选 |
| `~/.pm2/logs/` | PM2 日志 | 按需 |
修改 `nodes.json` 或 Web 设置后,**PM2 会自动读入**(`save_nodes_config` 热加载);若仅改环境变量需 `pm2 restart`。
---
## 10. 故障排查
| 现象 | 排查 |
|------|------| |------|------|
| PM2 日志 | `pm2 logs llm-gateway`,默认在 `~/.pm2/logs/` | | 首页卡片 **离线** | VPS 上 `curl 127.0.0.1:端口/v1/models`;检查 frp、Ollama 是否启动 |
| 账号与 API Key | **`gateway.json`**(或 `GATEWAY_CONFIG` 指向的文件),含明文密码,请限制权限(如 `chmod 600`),勿提交到版本库 | | **404 未找到模型** | 请求 `model` 与 `nodes.json` 中 `models[].id` 不一致 |
| **502 上游连接失败** | 端口错、frp 断、防火墙;`pm2 logs llm-gateway` |
| 统计 IP 全是 VPS | 宝塔未传 `X-Forwarded-For`,见 §5.2 |
| Token 始终为 0 | 上游未返回 `usage`;流式需 `include_usage` |
| 改 `JWT_SECRET` 后无法进设置 | 重新登录 Web |
--- ---
## 8. 反向代理(可选) ## 11. 部署检查清单
对外只暴露 443/80 时,可用 Nginx/Caddy 把域名反向代理到 `http://127.0.0.1:8000`,并配置 TLS。此时上游 **`UPSTREAM_URL`** 仍指向本机大模型地址,与网关对外端口无关。 - [ ] `gateway.json`、`nodes.json` 已配置且权限 `600`
- [ ] `JWT_SECRET` 已修改
- [ ] PM2 进程 `llm-gateway` 在线,端口 **8150**
- [ ] 宝塔 HTTPS 反代到 `127.0.0.1:8150`,流式相关 Nginx 参数已加
- [ ] frp 映射 3313+ 在 VPS 本机 `curl` 通过
- [ ] 首页模型卡片状态为 **空闲** 或 **忙碌**(非长期离线)
- [ ] API 试调用成功,`/stats` 有记录
--- ---
## 9. 故障排查 ## 12. 相关文档
1. **`pm2 logs` 中报 ModuleNotFoundError** - 功能与配置总览:[README.md](./README.md)
确认 PM2 使用的解释器是 **`venv` 内的 python**,且已在同一 venv 中执行 `pip install -r requirements.txt` - 仓库地址:[https://git.bz121.com/dekun/openai_node.git](https://git.bz121.com/dekun/openai_node.git)
2. **502 / 上游连接失败**
检查大模型是否监听 `UPSTREAM_URL`,防火墙与内网穿透是否正常。
3. **修改 `JWT_SECRET` 后旧登录失效**
属预期行为,用户需重新登录网页端。
---
## 10. 小结
1. 创建 venv 并安装 `requirements.txt`
2. 配置 **`JWT_SECRET`**(及可选 **`UPSTREAM_URL`**)。
3. `pm2 start ecosystem.config.cjs``pm2 save``pm2 startup`(按需)。
进程名 **`llm-gateway`** 与 `ecosystem.config.cjs``name` 字段一致,便于 `pm2 restart llm-gateway` 等操作。
+213
View File
@@ -0,0 +1,213 @@
# openai_node
本地 **Ollama / OpenAI 兼容** 大模型中转网关:统一鉴权、多节点按模型调度、Web 管理面板与流量统计。
代码仓库:[https://git.bz121.com/dekun/openai_node.git](https://git.bz121.com/dekun/openai_node.git)
---
## 功能概览
| 模块 | 说明 |
|------|------|
| **OpenAI 兼容 API** | `POST /v1/chat/completions`Bearer `sk-...` 鉴权,支持流式响应 |
| **多节点调度** | 按请求体中的 `model` 转发到不同机器/端口(`nodes.json` |
| **Web 控制台** | 首页模型分布卡片、流量统计、系统设置、用户中心 |
| **访问统计** | 记录客户端 IP、Token 用量、按节点汇总(SQLite |
| **健康检查** | 定时探测各节点 `/v1/models`,首页显示中文状态 |
---
## 架构示意
```
客户端 / Cursor / 脚本
宝塔 Nginx443/80)──反代──► VPS :8150(本网关)
│ │
│ ├─ 鉴权 / 统计 / Web
│ │
│ ┌────────────┼────────────┐
│ ▼ ▼ ▼
│ 127.0.0.1:3313 :3314 :3315 …(frp 映射)
│ │ │ │
│ PC-A Ollama PC-B Ollama PC-C Ollama
```
- **对外只暴露一个域名**,反代到网关 **8150**
- 各家用/办公室电脑通过 **frp** 等内网穿透,把模型端口映射到 VPS 本机 `127.0.0.1:3313` 等。
- 网关在 **系统设置** 中维护「端口 ↔ 模型」关系,无需为每台机器单独配公网域名。
---
## 环境要求
- Python **3.10+**
- 上游服务:**Ollama**(或其它 OpenAI 兼容 HTTP API
- 生产建议:**PM2** 守护进程;**宝塔** 或 Nginx 做 HTTPS 反代
---
## 快速开始
### 1. 克隆仓库
```bash
git clone https://git.bz121.com/dekun/openai_node.git
cd openai_node
```
### 2. 安装依赖
```bash
python3 -m venv venv
source venv/bin/activate # Windows: .\venv\Scripts\Activate.ps1
pip install -r requirements.txt -U
```
### 3. 配置文件
```bash
cp gateway.json.example gateway.json
cp nodes.json.example nodes.json
```
编辑 **`gateway.json`**
```json
{
"username": "admin",
"password": "你的强密码",
"api_key": ""
}
```
`api_key` 留空时,首次启动会自动生成 `sk-...` 并写回文件。
编辑 **`nodes.json`**(示例为 3 台节点,主机默认 `127.0.0.1`,端口 `33133315`):
- `models[].id` 必须与调用 API 时 JSON 里的 **`model` 字段完全一致**(Ollama 模型名)。
- `models[].label` 为 Web 卡片上的显示名。
### 4. 启动
```bash
export JWT_SECRET="请改为长随机字符串"
python -m uvicorn main:app --host 0.0.0.0 --port 8150
```
浏览器打开 `http://127.0.0.1:8150`
| 路径 | 说明 |
|------|------|
| `/` | 首页:模型分布卡片 + 使用说明 |
| `/login` | 管理员登录 |
| `/settings` | 系统设置(节点/端口/模型,需登录) |
| `/stats` | 流量统计、IP 列表、Token 账单 |
| `/user` | 查看 / 复制 API Key |
---
## 调用示例
`https://你的域名` 换为实际地址(或 `http://IP:8150` 调试):
```bash
curl https://你的域名/v1/chat/completions \
-H "Authorization: Bearer sk-xxxx" \
-H "Content-Type: application/json" \
-d '{
"model": "your-model-id-1",
"messages": [{"role": "user", "content": "你好"}],
"stream": true
}'
```
在 Cursor、OpenAI SDK 等客户端中:
- **Base URL**`https://你的域名/v1`
- **API Key**`gateway.json` 中的 `api_key` 或用户中心页面复制
---
## 配置说明
### gateway.json
| 字段 | 说明 |
|------|------|
| `username` | Web 登录用户名 |
| `password` | Web 登录密码(明文存储,请限制文件权限) |
| `api_key` | OpenAI 兼容 API Key,可自动生成 |
### nodes.json
| 字段 | 说明 |
|------|------|
| `id` | 节点唯一 ID(系统自动生成亦可) |
| `name` | 显示名称 |
| `host` | 上游主机,frp 场景填 **`127.0.0.1`** |
| `port` | 上游端口,如 **33133318** |
| `enabled` | 是否启用 |
| `max_concurrent` | 单节点最大并发请求数(建议 1) |
| `models` | `{ "id": "模型ID", "label": "显示名" }` 列表 |
也可在 Web **系统设置** 中增删改,保存后写入 `nodes.json`
### 环境变量
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `JWT_SECRET` | (弱默认值) | **生产必改**Web 登录 JWT 签名 |
| `GATEWAY_PORT` | `8150` | 监听端口 |
| `GATEWAY_CONFIG` | `gateway.json` | 账号配置路径 |
| `NODES_CONFIG` | `nodes.json` | 节点配置路径 |
| `STATS_DB` | `gateway_stats.db` | 统计数据库路径 |
| `UPSTREAM_URL` | `http://127.0.0.1:10434` | **未配置 nodes 时** 的单机上流回退地址 |
| `APP_ROOT` | 空 | 反代子路径前缀,如 `/wg` |
---
## 节点状态(中文)
| 状态 | 含义 |
|------|------|
| 空闲 | 在线且可接受新请求 |
| 忙碌 | 已达 `max_concurrent` |
| 离线 | 健康检查未通过 |
| 未启用 | 节点在设置中关闭 |
| 异常 | 连接失败并带有错误信息 |
---
## 项目文件
| 文件 | 说明 |
|------|------|
| `main.py` | 网关主程序(API + Web 单文件) |
| `gateway.json.example` | 账号配置示例 |
| `nodes.json.example` | 多节点配置示例 |
| `requirements.txt` | Python 依赖 |
| `ecosystem.config.cjs` | PM2 配置 |
| `DEPLOY.md` | **生产部署文档**(宝塔 + frp + PM2 |
---
## 安全提示
- 勿将 `gateway.json``nodes.json``gateway_stats.db` 提交到公开仓库。
- 生产环境务必修改 **`JWT_SECRET`**,并为 `gateway.json` 设置严格文件权限(如 `chmod 600`)。
- 统计与设置接口需 Web 登录;API 调用使用 `sk-` Key,请勿混用。
---
## 生产部署
详见 **[DEPLOY.md](./DEPLOY.md)**VPS、宝塔反代 8150、frp 多机、PM2)。
---
## 许可证
私有仓库,按团队内部约定使用。