245 lines
10 KiB
Markdown
245 lines
10 KiB
Markdown
# 5700U 局域网服务器部署执行单
|
||
|
||
本文用于让 Codex 在已经拉取本仓库的 Ryzen 7 5700U Linux 主机上,完成 Rust API 与中文 FunASR 的 Docker 化部署。它既是部署约束,也是验收清单。
|
||
|
||
## 使用方式
|
||
|
||
在 5700U 主机的仓库根目录更新代码:
|
||
|
||
```bash
|
||
git pull --ff-only
|
||
```
|
||
|
||
然后在仓库根目录启动 Codex 新会话,发送:
|
||
|
||
```text
|
||
请完整阅读 DEPLOY_5700U.md,把它作为本次任务的执行约束。请实际完成部署、测试和验收,不要只返回操作步骤。遇到可自行诊断的问题请继续排查;只有缺少数据库凭据、sudo 权限或会改变既有数据的决定时才停下来问我。
|
||
```
|
||
|
||
## 固定部署目标
|
||
|
||
目标主机资源:
|
||
|
||
- CPU:AMD Ryzen 7 5700U,8 核 16 线程。
|
||
- GPU:AMD Lucienne 集显,本方案不使用 GPU、不安装 ROCm。
|
||
- 内存:64 GB。
|
||
- 网络:Mac mini、小程序测试手机和 5700U 主机位于可信局域网。
|
||
|
||
服务拓扑:
|
||
|
||
```text
|
||
Mac mini / 测试手机
|
||
-> http://<5700U局域网IP>:39180
|
||
-> Rust API 容器(容器内 0.0.0.0:8080)
|
||
-> 现有远程 PostgreSQL
|
||
-> http://funasr:10095(仅 Docker 内部网络)
|
||
-> FunASR CPU 容器
|
||
```
|
||
|
||
必须满足:
|
||
|
||
1. Rust API 和 FunASR 都使用 Docker 部署,由同一份生产 Compose 文件管理。
|
||
2. 宿主机只向可信局域网开放 TCP `39180`,映射到 Rust API 容器的 `8080`。
|
||
3. FunASR 的 `10095` 不发布到宿主机,只允许 Compose 内部网络访问。
|
||
4. PostgreSQL 继续使用已有的远程 `DATABASE_URL`,不得新增 PostgreSQL 容器。
|
||
5. 使用 CPU 版 PyTorch、Paraformer 中文模型、FSMN-VAD 和中文标点模型。
|
||
6. 模型缓存和录音文件使用持久化卷,重建容器不得丢失。
|
||
7. 所有业务容器设置 `restart: unless-stopped`,Docker 服务设置为开机启动。
|
||
8. 不再为 Rust API 创建 systemd 单元;进程生命周期统一由 Docker 管理。
|
||
9. 不把 `.env`、数据库密码、模型、录音、Docker 卷或构建产物提交到 Git。
|
||
10. 局域网调试使用 HTTP;正式接入微信合法域名时仍必须另行配置 HTTPS。
|
||
|
||
## 并发策略
|
||
|
||
当前 Python 服务在一个进程中共享一个 FunASR `AutoModel`。FunASR 官方没有承诺同一个 Python `AutoModel` 实例可以被多个线程安全地同时调用,因此不能只把 `ASR_MAX_CONCURRENCY` 从 `1` 改为更大的值。
|
||
|
||
初次部署使用以下安全配置:
|
||
|
||
```text
|
||
Uvicorn worker 数量:1
|
||
ASR_MAX_CONCURRENCY=1
|
||
ASR_WORKER_CONCURRENCY=1
|
||
DATABASE_MAX_CONNECTIONS=5
|
||
```
|
||
|
||
一条 8 分钟录音在现有 CPU 实测中约 45 秒完成,单 worker 已明显快于录音产生速度。单教师或少量并发时通常不会形成队列积压。
|
||
|
||
需要提高吞吐时,使用独立模型进程,而不是并发调用同一个模型实例:
|
||
|
||
```text
|
||
Uvicorn worker 数量:2
|
||
每个进程 ASR_MAX_CONCURRENCY=1
|
||
ASR_WORKER_CONCURRENCY=2
|
||
DATABASE_MAX_CONNECTIONS=10
|
||
```
|
||
|
||
每个 Uvicorn worker 会加载自己的模型副本。按照现有实测估算,两个模型进程可能占用约 6 GB 常驻内存,并在推理时出现接近 10 GB 的合计峰值;64 GB 主机有足够余量,但仍需实测。
|
||
|
||
并发升级规则:
|
||
|
||
1. 首次启动必须先用一个 Uvicorn worker 完成模型下载和健康检查。
|
||
2. 使用真实的 8 分钟课堂录音验证单进程结果。
|
||
3. 只有队列持续积压时,才切换为两个 Uvicorn worker,并同步把 Rust worker 调为 `2`。
|
||
4. 对相同录音比较转写内容、耗时、失败率,并观察 `docker stats`、系统负载和容器重启次数。
|
||
5. 如果出现随机推理错误、转写质量变化、持续交换或整体耗时反而增加,立即退回 `1/1`。
|
||
6. 不得直接升到 `3` 或 `4`。只有两个独立模型进程稳定压测后,才逐级增加进程和 Rust worker。
|
||
|
||
参考:FunASR 普通 Python 示例按顺序调用 `AutoModel.generate()`;明确提供多线程和动态批处理能力的是专用 runtime,而不是当前 Python `AutoModel` 服务。
|
||
|
||
- <https://github.com/modelscope/FunASR/blob/main/docs/tutorial/README.md>
|
||
- <https://github.com/modelscope/FunASR/blob/main/docs/tutorial/Tables.md>
|
||
- <https://github.com/modelscope/FunASR/blob/main/runtime/readme.md>
|
||
|
||
## Codex 执行约束
|
||
|
||
### 1. 先检查,不覆盖
|
||
|
||
开始时必须:
|
||
|
||
1. 阅读 `README.md`、`DEBUG_GUIDE.md`、`server/.env.example`、`server/API.md`、`asr-service/README.md`、`compose.asr.yml` 和相关源码。
|
||
2. 检查 `git status`。保留并兼容用户已有改动,不得重置或覆盖。
|
||
3. 检查 Linux 发行版、`x86_64` 架构、CPU、内存、磁盘、局域网 IP、时区和时间同步。
|
||
4. 检查 Docker Engine、Docker Compose 插件、Docker 服务自启动和当前用户权限。
|
||
5. 检查 TCP `39180` 是否空闲,确认远程 PostgreSQL 地址可达。
|
||
6. 至少保留 20 GB 可用磁盘空间,供镜像、Rust 构建层、约 2.1 GB 模型缓存和录音使用。
|
||
7. 宿主机不需要安装 Rust、Python、PyTorch、FunASR 或 ffmpeg,这些依赖应全部位于镜像内。
|
||
|
||
如果缺少 `DATABASE_URL`、`WECHAT_APP_ID` 或 `WECHAT_APP_SECRET`,停止并让用户在主机本地安全填写。不得要求用户在聊天中粘贴密码或 AppSecret,也不得在日志或最终报告中输出敏感值。
|
||
|
||
### 2. 创建生产部署文件
|
||
|
||
检查现有文件后,按仓库模式创建最少且清晰的部署文件。预期至少包括:
|
||
|
||
- `server/Dockerfile`:Rust 多阶段 release 构建。
|
||
- `server/.dockerignore`:排除 `.env`、`target/`、`data/` 和无关文件。
|
||
- `compose.deploy.yml`:统一管理迁移、Rust API 和 FunASR。
|
||
|
||
不要把本地开发用的 `compose.asr.yml` 改造成生产文件,避免破坏 Mac mini 的现有调试方式。
|
||
|
||
Rust 镜像要求:
|
||
|
||
1. builder 阶段使用与 `server/Cargo.toml` 中 `rust-version` 兼容的 Rust 工具链。
|
||
2. 构建 `teaching-feedback-api`、`migrate` 和 `migrate-user-owner` 三个 release 二进制。
|
||
3. runtime 阶段只包含运行所需 CA 证书、健康检查工具和二进制,不携带编译器与源码。
|
||
4. 尽可能使用非 root 用户运行 API,并确保录音卷目录可写。
|
||
5. 不把 `server/.env` 复制进镜像。
|
||
|
||
生产 Compose 至少包含:
|
||
|
||
- `funasr`:复用 `asr-service/Dockerfile`,CPU 模式,模型缓存卷,内部健康检查,不设置宿主机 `ports`。
|
||
- `migrate`:复用 Rust API 镜像,运行迁移二进制,成功后正常退出;迁移必须可重复执行。
|
||
- `api`:等待迁移成功和 FunASR 健康后启动,发布 `39180:8080`,挂载录音持久化卷。
|
||
|
||
容器环境覆盖值:
|
||
|
||
```env
|
||
HOST=0.0.0.0
|
||
PORT=8080
|
||
AUDIO_STORAGE_DIR=/data/audio
|
||
ASR_PROVIDER=local
|
||
LOCAL_ASR_URL=http://funasr:10095
|
||
ASR_REQUEST_TIMEOUT_SECONDS=1800
|
||
ASR_WORKER_CONCURRENCY=1
|
||
ASR_JOB_LEASE_SECONDS=3600
|
||
DATABASE_MAX_CONNECTIONS=5
|
||
AUTH_SESSION_TTL_DAYS=30
|
||
ALLOW_DEVELOPMENT_USER_HEADER=false
|
||
```
|
||
|
||
FunASR 初始配置:
|
||
|
||
```env
|
||
ASR_MODEL=paraformer-zh
|
||
ASR_VAD_MODEL=fsmn-vad
|
||
ASR_PUNC_MODEL=ct-punc
|
||
ASR_DEVICE=cpu
|
||
ASR_MAX_CONCURRENCY=1
|
||
ASR_BATCH_SIZE_SECONDS=300
|
||
MODELSCOPE_CACHE=/models
|
||
```
|
||
|
||
`DATABASE_URL` 和 `WECHAT_APP_SECRET` 只从未提交的 `server/.env` 或同等安全的 Compose secret/env 文件注入。设置文件权限为仅部署用户可读,并确认 `git check-ignore` 能匹配该文件。
|
||
|
||
### 3. 构建和启动
|
||
|
||
实际执行以下工作,不要只打印命令:
|
||
|
||
1. 对新建的 Dockerfile 和 Compose 文件执行语法及配置校验。
|
||
2. 在容器或临时构建阶段运行 Rust 格式检查、测试和严格 Clippy。
|
||
3. 构建生产镜像。
|
||
4. 首次以单 FunASR worker 启动,等待模型下载完成;网络较慢时应继续观察日志,不要因等待时间长提前判定失败。
|
||
5. 执行数据库迁移,确认输出 `PostgreSQL migrations completed`。
|
||
6. 启动 Rust API,并检查容器健康状态和重启次数。
|
||
7. 确保 Docker daemon 开机启动;不要通过 systemd 重复管理单个 API 进程。
|
||
|
||
部署命令最终应收敛为类似:
|
||
|
||
```bash
|
||
docker compose -f compose.deploy.yml build
|
||
docker compose -f compose.deploy.yml up -d
|
||
docker compose -f compose.deploy.yml ps
|
||
```
|
||
|
||
具体命令应以最终创建的 Compose 文件为准。
|
||
|
||
### 4. 网络和防火墙
|
||
|
||
1. FunASR 不得出现在 `docker ps` 的宿主机端口映射中。
|
||
2. Rust API 只映射宿主机 `39180`,容器内继续使用 `8080`。
|
||
3. 如果主机启用了防火墙,只允许当前可信局域网 CIDR 访问 TCP `39180`。
|
||
4. 修改防火墙前必须识别当前 SSH 来源和规则,不能导致 SSH 连接中断。
|
||
5. 不向公网开放 PostgreSQL、`10095` 或 `39180`。
|
||
|
||
### 5. 验收
|
||
|
||
必须完成以下验证:
|
||
|
||
```bash
|
||
curl http://127.0.0.1:39180/health
|
||
curl http://<5700U局域网IP>:39180/health
|
||
```
|
||
|
||
Rust 健康检查应包含:
|
||
|
||
```json
|
||
{
|
||
"status": "ok",
|
||
"database_configured": true,
|
||
"speech_configured": true,
|
||
"speech_available": true,
|
||
"speech_provider": "local-funasr",
|
||
"wechat_auth_configured": true,
|
||
"development_auth_enabled": false
|
||
}
|
||
```
|
||
|
||
还要验证:
|
||
|
||
1. 从 FunASR 容器内部访问 `/health` 成功。
|
||
2. 从 Rust API 容器访问 `http://funasr:10095/health` 成功。
|
||
3. 宿主机和另一台局域网设备都无法直接访问 `10095`。
|
||
4. 使用一段真实中文音频调用转录链路,确认文字可写入数据库。
|
||
5. 重启容器组后健康检查恢复,模型不重复下载,录音文件仍存在。
|
||
6. `docker stats` 中内存、CPU 和磁盘无异常,容器没有重启循环。
|
||
7. 小程序“我的”页面可保存 `http://<5700U局域网IP>:39180` 并通过服务检查。
|
||
|
||
如果没有可用测试音频,应明确报告这一项未验证,不能用健康检查代替真实转录验收。
|
||
|
||
### 6. 完成报告
|
||
|
||
完成后向用户报告:
|
||
|
||
- 实际局域网 IP 和小程序 API 地址。
|
||
- 新增或修改的部署文件及 `git diff --stat`。
|
||
- 三个 Compose 服务的状态和镜像版本。
|
||
- 数据库迁移结果。
|
||
- Rust API 与 FunASR 健康检查结果。
|
||
- 微信登录是否已配置,以及生产开发身份兼容是否关闭。
|
||
- 实际并发配置,以及是否完成真实录音压测。
|
||
- 模型缓存卷和录音卷名称、占用空间及备份方式。
|
||
- 查看日志、启动、停止、重启、更新代码和重新部署的准确命令。
|
||
- 防火墙规则是否修改。
|
||
- 尚未完成的验证或残余风险。
|
||
|
||
不得在报告中输出数据库密码、完整 `DATABASE_URL`、API 密钥或其他敏感信息。部署文件验证通过后保留改动供用户审查,不要自行提交或推送,除非用户在该会话中明确要求。
|