Files
sanguo_vnpy_v2/docs/deployment/nas-deploy-plan.md
T
claude_dev c210197778
CI/CD / test (push) Successful in 8s
CI/CD / nas-deploy (push) Failing after 34s
CI/CD / nas-verify (push) Has been skipped
fix(entrypoint): +x 执行位 + SKIP_REDIS_CHECK 默认1(治 run535 NAS crash loop)
run535 部署后容器 crash loop(exit126),两个独立根因:
1. Permission denied: entrypoint.sh git mv 后 644 无执行位,rsync 推 NAS 也是 644,
   bind mount 覆盖镜像层 chmod +x 无效,tini exec 失败。→ entrypoint.sh git 100755。
2. Redis 连接超时:新版 entrypoint 检查 SKIP_REDIS_CHECK!=1 才查 Redis(旧版 if false
   硬跳过),NAS 无 Redis 卡检查 crash。→ 默认 SKIP_REDIS_CHECK=1(复现旧版跳过)。

附:文档(nas-deploy-plan 方案①已实施 + ops/README 根治标记)。
2026-08-01 21:43:29 +08:00

7.3 KiB
Raw Blame History

Sanguo VeighNa — NAS Docker 迭代部署方案

维护人:诸葛亮 · 最后更新:2026-07-04 基于实机查证(Synology NAS cfeasynas 216+II 首次安装见 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→80008080→8080
代码挂载 /volume1/homes/admin/.sanguo_projects/sanguo_vnpy_v2/app
启动 /app/entrypoint.shpython /app/run_web.pyrun_web.py 跑 uvicorn sanguo_api.main:create_app --factory,单 workerPhase 3b 起)
重启策略 unless-stopped
启动方式 docker run(非 compose

三、常规迭代(只改代码)— 90% 场景

# 在 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 列表无意外项再实跑。

# 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 的改动不会自动同步(需手动处理)。
  • 根治方案( 2026-08-01 已实施方案①)dev 根目录恢复 entrypoint.shgit mvdocker/entrypoint.sh,单一权威),promote 全量 rsync 自动推送(RSYNC_EXCLUDE 不排除 entrypoint.sh),NAS 宿主孤儿遗留被覆盖、可复现。Dockerfile COPY entrypoint.sh /app/ + ENV PYTHONPATH=/app:/app/vnpy_v4.4.0 同步固化。方案②(镜像层提供)弃用——bind-mount 必然覆盖 /app,镜像层 entrypoint 无法生效,只能靠宿主(promote 推送的)那份。

四、依赖变更(改 requirements-docker.txt)— 偶尔

# 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