Files
sanguo_vnpy_v2/scripts/ops/README.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

150 lines
6.3 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.
# scripts/ops — NAS 运维脚本
NAS(测试 + 备份角色)的运维脚本。仅用标准库 / `set -euo pipefail` bash
不引入第三方依赖,不硬编码密钥。
| 脚本 | 用途 | 运行位置 |
|------|------|----------|
| `nas_bridge_probe.py` | QMT bridge 连通性 / 健康探针 | NAS 容器内(容器无 curl,只能 python |
| `nas_backup_vps.sh` | 生产 VPS → NAS 每日快照备份(模板) | NAS(ssh 免密拉取生产) |
---
## 1. nas_bridge_probe.py — bridge 健康探针
**为何存在**:容器镜像没装 curl/wgetbridge 连通性只能用 python urllib 探。
走 Mac tunnel `http://192.168.2.101:8765`(华为光猫拦 NAS→VPS 公网 80/443)。
**接口**
- `GET /health`(免鉴权)→ `{"status":"ok","miniqmt_connected":true}`
- `GET /account`(需 `X-Bridge-Token`)→ `{"ok":true,"cash":..,"total":..}`
**退出码**`0`=健康(可达 + miniQMT 连接),`1`=不健康。
### 用法(容器内)
```bash
# 进容器(Docker 不在 NAS PATH,须全路径)
/var/packages/Docker/target/usr/bin/docker exec -it sanguo_vnpy_v2 \
python /app/scripts/ops/nas_bridge_probe.py
# 额外探 /account(从环境读 token,绝不硬编码)
/var/packages/Docker/target/usr/bin/docker exec -it -e BRIDGE_TOKEN=xxx \
sanguo_vnpy_v2 python /app/scripts/ops/nas_bridge_probe.py
# 覆盖 bridge URL(换 tunnel 端点时)
BRIDGE_URL=http://192.168.2.101:8765 BRIDGE_TOKEN=xxx \
python /app/scripts/ops/nas_bridge_probe.py
```
**环境变量**
| 变量 | 默认 | 说明 |
|------|------|------|
| `BRIDGE_URL` | `http://192.168.2.101:8765` | bridge 基址(Mac tunnel 端点) |
| `BRIDGE_TOKEN` | — | 设了才探 `/account`;从环境读,绝不硬编码 |
| `BRIDGE_TIMEOUT` | `8` | 单请求超时秒数 |
---
## 2. nas_backup_vps.sh — 生产→NAS 每日快照备份
**方向(铁律)**:生产 VPS → NAS 单向,**绝不反向写**(NAS 是测试/备份角色,
回灌会污染生产库)。
**策略**:每日 `YYYYMMDD/` 快照 + `--link-dest` 硬链接去重(7 份近乎 1 份体积)
+ 保留最近 N 份自动清旧。**模板**:源路径随生产拓扑填。
### 用法
```bash
# 生产拓扑就绪后(占位示例)
./nas_backup_vps.sh \
--host sanguo-vps \
--paths /var/lib/sanguo/data,/opt/sanguo/config \
--dest /volume1/stock/backup/sanguo_vps \
--keep 7
# 先 dry-run 看会做什么(不传输/不删除)
./nas_backup_vps.sh --host sanguo-vps --paths /a,/b --dest /volume1/.../x --dry-run
```
**参数**
| 参数 | 必填 | 默认 | 说明 |
|------|------|------|------|
| `--host` | 是 | — | 生产源 ssh 别名(`~/.ssh/config` |
| `--paths` | 是 | — | 生产侧目录列表(逗号分隔,绝对路径) |
| `--dest` | 是 | — | NAS 备份根目录(快照建在其下 `YYYYMMDD/` |
| `--keep` | 否 | `7` | 保留最近 N 份快照 |
| `--dry-run` | 否 | — | 只打印,不传输/不删除 |
**前置**:生产 host 别名加进 NAS 的 `~/.ssh/config` + key 免密。
别名不存在时 preflight 会清晰报错并给 `~/.ssh/config` 占位片段。
---
## 3. dev→test→prod 流水线命令清单
### devMac Mini 开发目录)
```bash
SRC=~/.openclaw/sanguo_projects/sanguo_vnpy_v2/
DEST=sanguo-nas:/volume1/homes/admin/.sanguo_projects/sanguo_vnpy_v2/
DOCKER="/var/packages/Docker/target/usr/bin/docker"
# (1) 同步代码到 NAStest/staging)—— 注意每个 pattern 单独 --exclude
# ⚠️ 务必排除 data_cache/(见下方"已知坑"),否则 --delete 会删 NAS 上
# 万级 staging parquet
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='.superpowers/' \
--exclude='entrypoint.sh' \
-e ssh "$SRC" "$DEST"
# (2) 重启容器(bind-mount,改代码即生效,重启重载进程)
ssh sanguo-nas "$DOCKER restart sanguo_vnpy_v2"
# (3) 冒烟:看日志 + 探健康
sleep 8
ssh sanguo-nas "$DOCKER logs --tail 30 sanguo_vnpy_v2"
```
### testNAS 容器内)
```bash
# bridge 连通性(容器无 curl,用 python 探针)
/var/packages/Docker/target/usr/bin/docker exec -it sanguo_vnpy_v2 \
python /app/scripts/ops/nas_bridge_probe.py
```
### prod(生产 VPS,部署时按 `docs/deployment/nas-deploy-plan.md` 第五节,
不要动 frpc/socat/Caddy 外网链路)
> 生产方向数据备份:`nas_backup_vps.sh` 从 VPS 拉 → NAS,单向。
---
## 已知坑
1. **rsync `--exclude` 语法**`--exclude='a' 'b' 'c'`(一个 `--exclude` 后跟多个
bare pattern)是**错的**——rsync 会把 `b`/`c` 当源路径(报 `lstat` 错,排除不生效)。
正确写法:每个 pattern 单独一个 `--exclude=PATTERN``nas-deploy-plan.md` 第三节的
命令需修。
2. **`data_cache/` 必须排除**:dev↔NAS 同步是**代码**迭代,`data_cache/` 是本机下载缓存
dev 59k 文件 / NAS 69k 文件,差异万级 staging parquet)。带 `--delete` 不排除会删 NAS
上大量 staging 文件。deploy-plan 排除列表漏了它,已在上文命令补 `--exclude='data_cache/'`
3. **`venv/`(无点)必须排除**dev 机有 `venv/`Python 3.14)和 `venv311/`Python 3.11),
deploy-plan 只排了 `.venv/`(带点)。不排会把 509 个 venv 文件(含 182MB 的
`_polars_runtime.abi3.so`)推到 NAS 容器(容器是 Python 3.10,这堆二进制毫无用处)。
已在上文命令补 `--exclude='venv/' --exclude='venv311/'`
4. **`entrypoint.sh` 位置不一致(会炸容器,已确认)**:容器 `Entrypoint=[/app/entrypoint.sh]`
`docker inspect` 实证),但 dev 侧根目录已无 `entrypoint.sh`(移到 `docker/entrypoint.sh`)。
直接跑带 `--delete` 的 rsync 会删 NAS 的 `/app/entrypoint.sh` → 下次 `docker restart`
启动失败(entrypoint not found)。**已在上文命令加 `--exclude='entrypoint.sh'` 保护**。
✅ 根治(2026-08-01 已实施:entrypoint git mv 回根单一权威,promote 全量 rsync 自动推可复现,Dockerfile COPY+ENV PYTHONPATH 同步;方案②弃用,bind-mount 必覆盖 /app