From 75abc7bfe1372d6f6843808237d379457e1bdb7a Mon Sep 17 00:00:00 2001 From: shay7sev Date: Mon, 20 Jul 2026 11:39:03 +0800 Subject: [PATCH] docs: add 5700U Docker deployment runbook --- DEPLOY_5700U.md | 239 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 DEPLOY_5700U.md diff --git a/DEPLOY_5700U.md b/DEPLOY_5700U.md new file mode 100644 index 0000000..688fb53 --- /dev/null +++ b/DEPLOY_5700U.md @@ -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 权限或会改变既有数据的决定时才停下来问我。 +``` + +## 固定部署目标 + +目标主机资源: + +- 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` 服务。 + +- +- +- + +## 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 密钥或其他敏感信息。部署文件验证通过后保留改动供用户审查,不要自行提交或推送,除非用户在该会话中明确要求。