Files
sanguo_vnpy_v2/docs/deployment/nas-deploy-plan.md
T
claude_dev 54f9ab4c4f docs(deploy): VPS生产runbook+三机环境矩阵+NAS ops脚本+修rsync危险命令
vps-production-runbook(VPS运维一站式,已验证状态+拓扑+发布流水线+人类闸口+排障); env-version-matrix(三机Python/deps矩阵+Lock建议); scripts/ops(NAS bridge探针-容器无curl-+VPS→NAS备份脚本); nas-deploy-plan§3(修rsync --exclude语法,原会删万级staging parquet+破坏entrypoint启动).
2026-07-15 07:12:46 +08:00

159 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Sanguo VeighNa — NAS Docker 迭代部署方案
> 维护人:诸葛亮 · 最后更新:2026-07-04
> 基于实机查证(Synology NAS `cfeasynas` 216+II
> 首次安装见 [`synology-nas.md`](./synology-nas.md),本文只讲**日常迭代与运维**。
> **2026-07-07 Phase 3b 更新**:容器 uvicorn 目标从 `sanguo_web.api:app`(旧实盘交易 API)切到 `sanguo_api.main:create_app --factory`(研究/回测 API + Vue SPA),单 workerorchestrator 任务状态在内存)。Vue 前端构建产物 `frontend/dist/` 由 FastAPI StaticFiles 挂在 `/`。公网 `vnpy.mysanguo.top` 现为**研究控制台**(登录 admin/admin,默认密码部署后改)。旧实盘交易路由(trading/gateway/market)本期下线,D 期接国金 QMT 时合并回来。端口 8000 / frpc / socat / Caddy 全程未动。
---
## 一、核心思路:应用层与镜像层分离
代码不打进镜像运行时,而是 bind-mount 挂进容器。**绝大多数迭代不需要重新 build 镜像。**
| 层次 | 内容 | 变更频率 | 变更方式 |
|------|------|----------|----------|
| **镜像层** `sanguo_vnpy_v2:latest` (6.9GB) | Python 3.10 + pip 依赖 + TA-Lib + entrypoint.sh | 低 | `docker build` |
| **应用层** bind-mount → `/app` | 项目源码(sanguo_web、vnpy_v4.4.0…) | 高 | rsync + `docker restart` |
| **数据层** `/app/data` | SQLite、日志(持久化) | — | 不要覆盖 |
> 容器启动时 `/app` 被宿主机目录整体覆盖,镜像内 `COPY` 的代码运行时不生效。**改代码 = 改宿主机目录 + 重启容器。**
---
## 二、当前部署实况(已查证)
| 项目 | 值 |
|------|-----|
| NAS | `cfeasynas` 192.168.2.154 |
| SSH | `admin@192.168.2.154` |
| Docker 路径 | `/var/packages/Docker/target/usr/bin/docker`**不在 PATH** |
| 权限 | admin 在 `docker` 组,**无需 sudo** |
| 容器/镜像 | `sanguo_vnpy_v2` / `sanguo_vnpy_v2:latest` |
| 端口 | `8000→8000``8080→8080` |
| 代码挂载 | `/volume1/homes/admin/.sanguo_projects/sanguo_vnpy_v2``/app` |
| 启动 | `/app/entrypoint.sh``python /app/run_web.py`run_web.py 跑 `uvicorn sanguo_api.main:create_app --factory`,单 workerPhase 3b 起) |
| 重启策略 | `unless-stopped` |
| 启动方式 | `docker run`(非 compose |
---
## 三、常规迭代(只改代码)— 90% 场景
```bash
# 在 Mac Mini 执行(ssh sanguo-nas 已 key 免密,见 ~/.ssh/config;不用 sshpass
DOCKER="/var/packages/Docker/target/usr/bin/docker"
SRC=~/.openclaw/sanguo_projects/sanguo_vnpy_v2/
DEST=sanguo-nas:/volume1/homes/admin/.sanguo_projects/sanguo_vnpy_v2/
# 1) 同步代码(已排除数据/缓存/venv/entrypoint/配置——配置含部署态密码,不覆盖)
# ⚠️ 每个 pattern 必须单独一个 --exclude=PATTERN(见下方警告框)
rsync -avz --delete \
--exclude='.git' --exclude='__pycache__' --exclude='*.pyc' --exclude='.pytest_cache' \
--exclude='data/' --exclude='data_cache/' --exclude='logs/' --exclude='temp/' \
--exclude='*.log' --exclude='.DS_Store' \
--exclude='.venv/' --exclude='venv/' --exclude='venv311/' \
--exclude='node_modules/' --exclude='config/' \
--exclude='entrypoint.sh' \
-e ssh \
"$SRC" "$DEST"
```
> ⚠️ **rsync --exclude 写法警告(2026-07-14 dry-run 实测固化)**
>
> 旧写法 `--exclude='.git' '__pycache__' '*.pyc' ...`(一个 `--exclude` 后紧挨多个 bare pattern**实测排除失效**——rsync 把 bare pattern 当**源路径**处理(报 `lstat: No such file or directory`),`--delete` 因此会误删 NAS 上:
> - `data_cache/` 全量 staging parquet**万级文件,dev/NAS 差约 1 万个**
> - `/app/entrypoint.sh`(容器 Entrypoint,删除后 `docker restart` 启动失败)
> - 以及 `venv/` 等 dev-only 目录被误推上去
>
> **必须**用每 pattern 单独 `--exclude=PATTERN` 写法(如上命令)。每次改排除列表后建议先 `rsync -avzn ...`dry-run)确认 `deleting` 列表无意外项再实跑。
```bash
# 2) 重启容器
ssh sanguo-nas "$DOCKER restart sanguo_vnpy_v2"
# 3) 看日志 + 冒烟
sleep 8
ssh sanguo-nas "$DOCKER logs --tail 20 sanguo_vnpy_v2"
curl -s http://192.168.2.154:8000/api/v1/auth/login -X POST \
-H 'Content-Type: application/json' -d '{"username":"admin","password":"<部署态密码>"}'
```
| 验证项 | 期望 |
|--------|------|
| `ssh sanguo-nas "$DOCKER ps"` | `Up` |
| `POST /api/v1/auth/login` | 返回 `{"token":...}` |
### 附:`entrypoint.sh` 根因说明(2026-07-14 dry-run + docker inspect 实证)
`docker inspect sanguo_vnpy_v2` 显示容器 `Entrypoint=[/app/entrypoint.sh]``Cmd=[]`
即容器启动**必须**找到 `/app/entrypoint.sh`。但 dev 仓库根目录已无此文件(已移到
`docker/entrypoint.sh`)。若 rsync 带 `--delete` 且不排除,会删掉 NAS 上的
`/app/entrypoint.sh` → 下次 `docker restart` 直接启动失败(entrypoint not found)。
- **短期保护(已落实)**:上方步骤 1 命令加了 `--exclude='entrypoint.sh'`NAS 现有文件
不会被删,容器可继续启动。代价:dev 侧对 entrypoint 的改动不会自动同步(需手动处理)。
- **根治方案(待用户确认采用哪种)**:
| 方案 | 做法 | 适用 |
|------|------|------|
| ① 纳入 git 根目录版本化 | dev 根目录恢复 `entrypoint.sh`(与 `docker/entrypoint.sh` 统一为同一份),去掉 `--exclude='entrypoint.sh'`rsync 正常同步 | entrypoint 需随代码迭代频繁改 |
| ② 由镜像层提供 | 确认 `entrypoint.sh` 由 Dockerfile `COPY` 进镜像;则 bind-mount 不应覆盖它(调整挂载/文件位置) | entrypoint 极少改、希望与代码解耦 |
> ⚠️ 两种方案互斥。当前默认走"短期保护",**待用户确认**后再切到 ① 或 ②。
---
## 四、依赖变更(改 requirements-docker.txt)— 偶尔
```bash
# 1) 先同步代码(含新 requirements)到 NAS,同第三节步骤 1
# 2) 在 NAS 重新 build
ssh sanguo-nas \
"cd /volume1/homes/admin/.sanguo_projects/sanguo_vnpy_v2 && \
/var/packages/Docker/target/usr/bin/docker build -f docker/Dockerfile -t sanguo_vnpy_v2:latest ."
# 3) 用相同参数重启容器
ssh sanguo-nas << 'EOF'
D=/var/packages/Docker/target/usr/bin/docker
$D stop sanguo_vnpy_v2 && $D rm sanguo_vnpy_v2
$D run -d --name sanguo_vnpy_v2 --restart unless-stopped \
-p 8000:8000 -p 8080:8080 \
-v /volume1/homes/admin/.sanguo_projects/sanguo_vnpy_v2:/app \
sanguo_vnpy_v2:latest
EOF
```
> ⚠️ NAS CPU 弱,build 编 TA-Lib + 大包约 3060 分钟。
---
## 五、外网链路(部署时不要动)
```
外网 https://vnpy.mysanguo.top
→ VPS Caddy :18000 → frps → Mac Mini frpc :8001
→ socat → 192.168.2.154:8000 → NAS 容器
```
**部署应用只动 NAS 容器,绝不碰 frpc / socat / Caddy。** 详见 `~/.openclaw/workspace/docs/vps-deployment-guide.md`
---
## 六、已知问题
| 问题 | 影响 | 处理 |
|------|------|------|
| `threads can only be started once` | VeighNa 引擎在 2 workers 下重复初始化 | Web/登录/查询正常;真实交易需改 1 worker |
| `/health` 返回 `degraded` | 引擎未初始化的降级 | 非致命 |
| Docker 不在 PATH | — | 一律用全路径 |
| admin 密码硬编码 | 安全 | 后续改 SSH key |
---
## 七、回滚
代码层(镜像未动):`git checkout <旧commit>` 后重跑第三节步骤 1-2。
镜像层:`docker images sanguo_vnpy_v2` 找旧 tag,重 `docker run`