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 根治标记)。
7.3 KiB
Sanguo VeighNa — NAS Docker 迭代部署方案
维护人:诸葛亮 · 最后更新:2026-07-04 基于实机查证(Synology NAS
cfeasynas216+II) 首次安装见synology-nas.md,本文只讲日常迭代与运维。
2026-07-07 Phase 3b 更新:容器 uvicorn 目标从
sanguo_web.api:app(旧实盘交易 API)切到sanguo_api.main:create_app --factory(研究/回测 API + Vue SPA),单 worker(orchestrator 任务状态在内存)。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,单 worker,Phase 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.sh(git mv自docker/entrypoint.sh,单一权威),promote 全量 rsync 自动推送(RSYNC_EXCLUDE不排除entrypoint.sh),NAS 宿主孤儿遗留被覆盖、可复现。DockerfileCOPY 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 + 大包约 30–60 分钟。
五、外网链路(部署时不要动)
外网 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。