docs: add 5700U Docker deployment runbook

This commit is contained in:
2026-07-20 11:39:03 +08:00
parent c4fd47e774
commit 75abc7bfe1

239
DEPLOY_5700U.md Normal file
View File

@@ -0,0 +1,239 @@
# 5700U 局域网服务器部署执行单
本文用于让 Codex 在已经拉取本仓库的 Ryzen 7 5700U Linux 主机上,完成 Rust API 与中文 FunASR 的 Docker 化部署。它既是部署约束,也是验收清单。
## 使用方式
在 5700U 主机的仓库根目录更新代码:
```bash
git pull --ff-only
```
然后在仓库根目录启动 Codex 新会话,发送:
```text
请完整阅读 DEPLOY_5700U.md把它作为本次任务的执行约束。请实际完成部署、测试和验收不要只返回操作步骤。遇到可自行诊断的问题请继续排查只有缺少数据库凭据、sudo 权限或会改变既有数据的决定时才停下来问我。
```
## 固定部署目标
目标主机资源:
- CPUAMD Ryzen 7 5700U8 核 16 线程。
- GPUAMD 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`,停止并让用户在主机本地安全填写。不得要求用户在聊天中粘贴密码,也不得在日志或最终报告中输出完整连接串。
### 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` 两个 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
```
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` 只从未提交的 `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"
}
```
还要验证:
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 密钥或其他敏感信息。部署文件验证通过后保留改动供用户审查,不要自行提交或推送,除非用户在该会话中明确要求。