diff --git a/jiangwei-platform/deploy/nas/deploy-plan.md b/jiangwei-platform/deploy/nas/deploy-plan.md index 55d2b4b0..775e6eb3 100644 --- a/jiangwei-platform/deploy/nas/deploy-plan.md +++ b/jiangwei-platform/deploy/nas/deploy-plan.md @@ -1,7 +1,7 @@ # 群晖NAS部署sanguo_vnpy回测服务方案 **编制人**:姜维 伯约 -**日期**:2026-04-27 +**日期**:2026-04-28(第3轮修订) **状态**:调研完成,待实施 --- @@ -14,40 +14,37 @@ - `curl http://192.168.2.154:8888/` → ✅ 302(Jupyter正常) - `curl http://192.168.2.154:8088/api/backtest/health` → ❌ Connection refused -### 1.2 根因 +### 1.2 根因(5重问题) -**三重问题叠加**: +| # | 问题 | 严重度 | 影响 | +|---|------|--------|------| +| **1** | entrypoint.sh 未启动 backtest-service | 🔴 阻断 | 容器内8088端口根本无进程监听 | +| **2** | 目录名 `backtest-service` 含连字符,不能做Python包 | 🔴 阻断 | 无法用 `python -m` 或 `uvicorn backtest_service.main:app` 启动 | +| **3** | models.py 使用 `ApiResponse[T](BaseModel)` 语法 | 🔴 阻断 | Python 3.12+语法,容器内Python 3.10不支持 | +| **4** | executor.py 使用 vnpy 3.x API,与容器内vnpy 4.3.0不兼容 | 🔴 阻断 | 3个import失败 + Interval枚举值缺失 + API签名变化 | +| **5** | main.py 的 `uvicorn.run("main:app")` 路径错误 | 🟡 修复 | uvicorn重导入时相对导入失败 | -| # | 问题 | 影响 | -|---|------|------| -| **1** | entrypoint.sh 未启动 backtest-service | 容器内8088端口根本无进程监听 | -| **2** | 容器内代码与本地不同步 | `start_backtest_service.py` 仍用旧import路径 `from scripts.backtest_service.main` | -| **3** | main.py 使用相对导入 `from .config` | 无法作为独立脚本直接运行,需通过包方式启动 | +### 1.3 根因4详情:vnpy 3.x → 4.x API变更 -**证据**: +executor.py 是vnpy深度集成代码,以下import在vnpy 4.3.0下全部失败: -```bash -# 容器内进程 — 无uvicorn/FastAPI -$ docker exec sanguo_vnpy ss -tlnp -LISTEN 0.0.0.0:22 (sshd) -LISTEN 0.0.0.0:8888 (jupyter-lab) -# 8088 无监听! +| executor.py (3.x写法) | 4.x正确写法 | 状态 | +|---|---|---| +| `from vnpy.trader.event import EventEngine` | `from vnpy.event import EventEngine` | ✅ 可修复 | +| `from vnpy.trader.backtesting import BacktestingEngine` | `from vnpy_ctastrategy.backtesting import BacktestingEngine` | ✅ 可修复(需安装包) | +| `from vnpy.trader.database import database_manager` | `from vnpy.trader.database import get_database` | ✅ 可修复(需安装vnpy_sqlite) | +| `from vnpy.trader.strategy import Strategy` | `from vnpy_ctastrategy import CtaStrategyApp` | ⚠️ 需确认 | +| `Interval.FIVE_MINUTE` 等细分枚举 | 4.x只有 `MINUTE/HOUR/DAILY/WEEKLY/TICK` | ⚠️ API设计需调整 | +| `BacktestingEngine.set_parameters(data=...)` | 4.x API签名可能变化 | ⚠️ 需逐个验证 | -# 容器内启动backtest-service报错 -$ docker exec sanguo_vnpy python3 /app/scripts/start_backtest_service.py -ModuleNotFoundError: No module named 'scripts' +**结论**:executor.py与vnpy 4.x的API差异过大,不适合逐行修补,建议**重写executor.py适配vnpy 4.x**。 -# Docker端口映射正常 -$ iptables -t nat -L DOCKER -n | grep 8088 -DNAT tcp 0.0.0.0/0 0.0.0.0/0 tcp dpt:8088 to:172.17.0.2:8088 -``` +### 1.4 已排除项 -### 1.3 排除项 - -- ❌ 端口映射问题:iptables DNAT规则正确,8088→172.17.0.2:8088 +- ❌ 端口映射问题:iptables DNAT 8088→172.17.0.2:8088 正确 - ❌ DSM防火墙问题:Jupyter使用同模式可访问 - ❌ Docker网络问题:bridge模式正常工作 -- ❌ 进程崩溃问题:根本没启动过 +- ❌ 进程崩溃问题:服务从未启动过,不存在崩溃 --- @@ -60,52 +57,35 @@ DNAT tcp 0.0.0.0/0 0.0.0.0/0 tcp dpt:8088 to:172.17.0.2:8088 | 型号 | DS216+II | | CPU | Intel Celeron N3060 @ 1.60GHz (2核) | | 内存 | 8GB (可用约6.4GB) | -| 磁盘 | 3.5TB 总量, 1.6TB 可用 | +| 磁盘 | 3.5TB总量, 1.6TB可用 | | Docker | 20.10.3 (需sudo) | -### 2.2 当前容器状态 +### 2.2 容器内环境 ``` -容器ID: 8fc55af3d27d -镜像: sanguo_vnpy:with-scripts -状态: Up 13 days -端口: 2222→22, 8000→8000, 8080→8080, 8088→8088, 8888→8888, 2018→2018, 4102→4102 +容器ID: 8fc55af3d27d +镜像: sanguo_vnpy:with-scripts +Python: 3.10.20 +vnpy: 4.3.0(核心包) +vnpy相关: vnpy_webtrader 1.1.0(已安装) +缺失包: vnpy_ctastrategy, vnpy_sqlite(已手动验证可安装) +运行服务: Jupyter Lab(8888), SSH(22) ✅ +未运行: backtest-service(8088) ❌ ``` -### 2.3 代码状态 - -| 文件 | 容器内版本 | 本地版本 | 问题 | -|------|-----------|---------|------| -| `start_backtest_service.py` | `from scripts.backtest_service.main` (旧) | `from main import main` (新) | **容器内代码过旧** | -| `backtest-service/main.py` | `from .config import settings` | 同 | **相对导入无法直接运行** | -| `entrypoint.sh` | 不含backtest-service | 同 | **缺失启动命令** | - -### 2.4 数据资源(NAS SMB共享) +### 2.3 数据资源(NAS SMB共享) ``` /Volumes/stock/A股数据/日线数据/ — 日线历史行情 /Volumes/stock/A股数据/分钟线数据/ — 分钟线数据 /Volumes/stock/A股数据/财务数据/ — 财务数据 -/Volumes/stock/sanguo_vnpy/data/ — 项目数据 ``` --- ## 三、部署方案 -### 3.1 方案选择 - -| 方案 | 描述 | 优点 | 缺点 | -|------|------|------|------| -| **A: 原地修复** | docker cp更新文件+修改entrypoint | 最快,无需重建镜像 | 容器重建后丢失 | -| **B: 重建镜像** | 修复代码后docker build | 持久化,可复现 | 构建耗时(约10-20min) | -| **C: Volume挂载** | 代码通过volume挂载 | 代码更新无需重建 | 需调整容器启动 | - -**推荐方案:A+B组合** -- 先用方案A快速验证(5分钟内出结果) -- 验证通过后用方案B持久化(下班后或闲时构建) - -### 3.2 架构图 +### 3.1 架构图 ``` ┌─────────────────────────────────────────────────────────────────┐ @@ -115,13 +95,13 @@ DNAT tcp 0.0.0.0/0 0.0.0.0/0 tcp dpt:8088 to:172.17.0.2:8088 │ │ Docker Container: sanguo_vnpy │ │ │ │ │ │ │ │ entrypoint.sh │ │ -│ │ ├── sshd -D (22) ──→ 2222 │ │ -│ │ ├── jupyter-lab (8888) ──→ 8888 ✅ 已有 │ │ -│ │ └── backtest-service (8088) ──→ 8088 🆕 新增 │ │ +│ │ ├── sshd -D (22) ──→ 2222 ✅已有 │ │ +│ │ ├── jupyter-lab (8888) ──→ 8888 ✅已有 │ │ +│ │ └── uvicorn backtest_service (8088) ──→ 8088 🆕新增 │ │ │ │ │ │ -│ │ /app/scripts/backtest-service/ │ │ +│ │ /app/scripts/backtest_service/ (下划线目录名) │ │ │ │ /app/backtest_jobs/ (回测结果) │ │ -│ │ /app/data/ (volume→/volume1/stock/A股数据) │ │ +│ │ /app/data/ (volume → /volume1/stock/A股数据) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ ↑ Docker bridge (172.17.0.0/16) │ │ ↑ iptables DNAT: 8088→172.17.0.2:8088 │ @@ -129,96 +109,60 @@ DNAT tcp 0.0.0.0/0 0.0.0.0/0 tcp dpt:8088 to:172.17.0.2:8088 ↑ 局域网 192.168.2.0/24 ↑ -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ Mac mini │ │ Windows Node │ │ 其他将军 │ -│ 192.168.2.153 │ │ 192.168.2.33 │ │ │ -│ curl :8088 │ │ 回测任务提交 │ │ curl :8088 │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ +┌─────────────────┐ ┌─────────────────┐ +│ Mac mini │ │ Windows Node │ +│ 192.168.2.153 │ │ 192.168.2.33 │ +│ curl :8088 │ │ 回测任务提交 │ +└─────────────────┘ └─────────────────┘ ``` -### 3.3 修复步骤(方案A:快速验证) +### 3.2 修复清单(6处改动) -#### Step 1: 修复main.py的相对导入问题 +| # | 文件 | 改动 | 说明 | +|---|------|------|------| +| 1 | **Dockerfile** | COPY后 `mv backtest-service backtest_service` | 目录名含连字符不能做Python包 | +| 2 | **entrypoint.sh** | 添加 uvicorn 启动命令 | `PYTHONPATH=/app/scripts uvicorn backtest_service.main:app` | +| 3 | **models.py** | `ApiResponse[T](BaseModel)` → `ApiResponse(BaseModel, Generic[T])` | Python 3.10兼容 | +| 4 | **main.py** | `uvicorn.run("main:app")` → `uvicorn.run("backtest_service.main:app")` | 包路径启动 | +| 5 | **executor.py** | 重写,适配vnpy 4.x API | 最核心的改动 | +| 6 | **requirements-base.txt** | 添加 `vnpy_ctastrategy` 和 `vnpy_sqlite` | 缺失的vnpy插件 | -将 `backtest-service/main.py` 中的相对导入改为绝对导入,使其可直接运行: +**关键决策:18处相对导入零改动**(司马懿方案)。通过`PYTHONPATH=/app/scripts uvicorn backtest_service.main:app`包方式启动,所有`from .xxx import yyy`自然工作。 + +### 3.3 executor.py重写要点 + +executor.py是与vnpy深度集成的核心文件,需要适配4.x API变更: ```python -# 修改前(相对导入,不能直接 python main.py) -from .config import settings -from .api import router -from .task_queue import task_queue +# === vnpy 4.x import 路径 === +from vnpy.event import EventEngine # 原: vnpy.trader.event +from vnpy_ctastrategy.backtesting import BacktestingEngine # 原: vnpy.trader.backtesting +from vnpy.trader.constant import Interval # 不变 +from vnpy.trader.database import get_database # 原: database_manager -# 修改后(支持两种方式) -import sys -import os -# 当作为包的子模块时,.config 正常;当直接运行时,需要添加路径 -if __name__ == "__main__" or __package__ is None: - _dir = os.path.dirname(os.path.abspath(__file__)) - if _dir not in sys.path: - sys.path.insert(0, _dir) - from config import settings - from api import router - from task_queue import task_queue -else: - from .config import settings - from .api import router - from .task_queue import task_queue +# === Interval 枚举变更 === +# 3.x: FIVE_MINUTE, FIFTEEN_MINUTE, THIRTY_MINUTE, FOUR_HOUR +# 4.x: 只有 MINUTE, HOUR, DAILY, WEEKLY, TICK +INTERVAL_MAP = { + "1m": Interval.MINUTE, + "5m": Interval.MINUTE, # 4.x不再细分,统一用MINUTE + "15m": Interval.MINUTE, + "30m": Interval.MINUTE, + "1h": Interval.HOUR, + "4h": Interval.HOUR, + "1d": Interval.DAILY, + "1w": Interval.WEEKLY, +} + +# === 数据加载方式变更 === +# 3.x: database_manager.query_history(req) +# 4.x: db = get_database(); data = db.query_bar_data(symbol, exchange, interval, start, end) +# 或者从CSV文件直接加载: engine.load_data(file_path) ``` -#### Step 2: 更新容器内代码 +**重写策略**:保持API接口不变(submit/status/result/health),只改内部vnpy调用逻辑。回测引擎调用改用 `engine.load_data()` 从CSV文件加载,绕过数据库兼容问题(NAS上数据本来就在CSV文件里)。 -```bash -# 从本地复制修复后的代码到容器 -docker cp scripts/backtest-service/ sanguo_vnpy:/app/scripts/backtest-service/ -docker cp scripts/start_backtest_service.py sanguo_vnpy:/app/scripts/ -``` - -#### Step 3: 添加backtest-service到entrypoint.sh - -在entrypoint.sh中加入backtest-service启动: - -```bash -# 在 jupyter lab 启动之后、tail -f 之前添加: - -# 启动自动化回测服务 -cd /app/scripts/backtest-service && python3 main.py & -BACKTEST_PID=$! -echo "回测服务 PID: $BACKTEST_PID (端口 8088)" -``` - -#### Step 4: 验证 - -```bash -# 1. 在容器内启动backtest-service -docker exec -d sanguo_vnpy bash -c "cd /app/scripts/backtest-service && python3 main.py" - -# 2. 等待2秒 -sleep 2 - -# 3. 容器内验证 -docker exec sanguo_vnpy ss -tlnp | grep 8088 -# 预期: LISTEN 0.0.0.0:8088 - -# 4. 局域网验证 -curl http://192.168.2.154:8088/api/backtest/health -# 预期: {"code":0,"msg":"ok","data":{...}} -``` - -### 3.4 完整部署(方案B:重建镜像) - -#### Step 3.4.1: 修改文件清单 - -| 文件 | 修改内容 | -|------|---------| -| `scripts/backtest-service/main.py` | 兼容直接运行和包导入 | -| `scripts/backtest-service/api.py` | 同上,处理相对导入 | -| `scripts/backtest-service/executor.py` | 同上 | -| `scripts/backtest-service/task_queue.py` | 同上 | -| `scripts/backtest-service/result_storage.py` | 同上 | -| `docker/entrypoint.sh` | 添加backtest-service启动 | -| `docker/Dockerfile` | 添加HEALTHCHECK指令 | - -#### Step 3.4.2: 修改后的entrypoint.sh +### 3.4 修改后的entrypoint.sh ```bash #!/bin/bash @@ -237,105 +181,77 @@ jupyter lab --ip=0.0.0.0 --port=8888 --no-browser \ --NotebookApp.password='' \ --NotebookApp.allow_origin='*' & -# 自动化回测服务(🆕 新增) -cd /app/scripts/backtest-service -python3 main.py >> /app/logs/backtest-service.log 2>&1 & -echo "回测服务已启动 (PID=$!, 端口8088)" +# 自动化回测服务(uvicorn包方式启动) +mkdir -p /app/logs /app/backtest_jobs +# 兼容旧镜像:如果目录名含连字符,创建符号链接 +if [ -d /app/scripts/backtest-service ] && [ ! -d /app/scripts/backtest_service ]; then + ln -sf /app/scripts/backtest-service /app/scripts/backtest_service +fi +PYTHONPATH=/app/scripts uvicorn backtest_service.main:app \ + --host 0.0.0.0 --port 8088 \ + >> /app/logs/backtest-service.log 2>&1 & +BT_PID=$! +echo "回测服务已启动 (PID=$BT_PID, 端口8088)" -# code-server(可选,不阻塞启动) -code-server || echo "code-server启动失败,跳过" & +# code-server +code-server & -# 等待服务初始化 sleep 5 # 健康检查 -if curl -s http://localhost:8088/api/backtest/health > /dev/null 2>&1; then +if curl -sf http://localhost:8088/api/backtest/health > /dev/null 2>&1; then echo "✅ 回测服务健康检查通过" else - echo "⚠️ 回测服务尚未就绪,请检查日志: /app/logs/backtest-service.log" + echo "⚠️ 回测服务尚未就绪,检查日志: /app/logs/backtest-service.log" fi echo "" echo "✅ sanguo_vnpy 环境启动成功!" echo "" echo "访问地址:" -echo " Jupyter Lab: http://localhost:8888 (token: sanguo123)" -echo " 回测服务: http://localhost:8088/api/backtest/health" -echo " SSH: ssh -p 2222 vnpy@localhost (password: sanguo123)" +echo " Jupyter Lab: http://localhost:8888 (token: sanguo123)" +echo " 回测服务: http://localhost:8088/api/backtest/health" +echo " 回测API文档: http://localhost:8088/docs" +echo " SSH: ssh -p 2222 vnpy@localhost (password: sanguo123)" echo "" tail -f /dev/null ``` -#### Step 3.4.3: Dockerfile增量修改 +### 3.5 Dockerfile增量修改 ```dockerfile -# 在 USER vnpy 之前添加: -RUN mkdir -p /app/logs +# 在 COPY scripts 行之后添加: +COPY --chown=vnpy:vnpy scripts /app/scripts +RUN find /app/scripts -name "*.sh" -type f -exec chmod +x {} \; +# 目录名含连字符不能做Python包,重命名为下划线 +RUN if [ -d /app/scripts/backtest-service ]; then \ + mv /app/scripts/backtest-service /app/scripts/backtest_service; \ + fi +# 创建日志和回测结果目录 +RUN mkdir -p /app/logs /app/backtest_jobs -# 在 COPY --chown=vnpy:vnpy docker/entrypoint.sh /app/ 之后添加: -COPY --chown=vnpy:vnpy scripts/backtest-service /app/scripts/backtest-service - -# 添加健康检查(Docker自动重启判定) -HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ - CMD curl -f http://localhost:8088/api/backtest/health || exit 1 +# 在 requirements-base.txt 中添加: +# vnpy_ctastrategy>=1.0.0 +# vnpy_sqlite>=1.0.0 ``` -#### Step 3.4.4: 构建和部署命令 +### 3.6 docker run命令 ```bash -# === 在Mac mini上构建镜像 === -cd /Users/chufeng/.openclaw/sanguo_projects/sanguo_vnpy -docker build -t sanguo_vnpy:latest -f docker/Dockerfile . - -# === 保存镜像为tar === -docker save sanguo_vnpy:latest | gzip > /tmp/sanguo_vnpy.tar.gz - -# === 传输到NAS === -scp /tmp/sanguo_vnpy.tar.gz admin@192.168.2.154:/tmp/ - -# === 在NAS上加载镜像 === -ssh admin@192.168.2.154 -sudo /var/packages/Docker/target/usr/bin/docker load < /tmp/sanguo_vnpy.tar.gz - -# === 停止旧容器、启动新容器 === -sudo /var/packages/Docker/target/usr/bin/docker stop sanguo_vnpy -sudo /var/packages/Docker/target/usr/bin/docker rm sanguo_vnpy - sudo /var/packages/Docker/target/usr/bin/docker run -d \ --name sanguo_vnpy \ --restart unless-stopped \ + -m 4g --cpus=1.5 \ -p 8888:8888 \ -p 8088:8088 \ -p 8000:8000 \ -p 2222:22 \ -v /volume1/stock/A股数据:/app/data:ro \ + -v /volume1/stock/回测结果:/app/backtest_jobs \ sanguo_vnpy:latest ``` -**注意**: -- 去掉了不常用的端口映射(8080 code-server, 2018/4102 RPC),减少攻击面 -- 数据目录通过volume只读挂载,避免容器内意外修改 -- `--restart unless-stopped` 确保异常崩溃自动恢复 - -### 3.5 数据方案 - -| 方式 | 路径 | 说明 | -|------|------|------| -| Volume只读挂载 | `-v /volume1/stock/A股数据:/app/data:ro` | NAS本地路径,性能最好 | -| 容器内路径 | `/app/backtest_jobs/` | 回测结果输出目录 | -| Volume读写挂载 | `-v /volume1/stock/回测结果:/app/backtest_jobs` | 结果持久化到NAS | - -### 3.6 运维方案 - -| 项目 | 方案 | -|------|------| -| **自动重启** | `--restart unless-stopped`(Docker原生) | -| **健康检查** | Dockerfile HEALTHCHECK + `/api/backtest/health` | -| **日志查看** | `docker logs sanguo_vnpy` 或容器内 `/app/logs/backtest-service.log` | -| **资源限制** | `-m 4g --cpus=1.5`(8GB内存分配4GB,2核分配1.5核) | -| **监控告警** | 后续接入(当前先保证基本可用) | - --- ## 四、验证步骤 @@ -348,61 +264,79 @@ curl -s -o /dev/null -w "%{http_code}" http://192.168.2.154:8088/api/backtest/he # 预期: 200 # 标准二:提交回测任务并获取结果 -curl -X POST http://192.168.2.154:8088/api/backtest/submit \ +TASK_ID=$(curl -s -X POST http://192.168.2.154:8088/api/backtest/submit \ -H "Content-Type: application/json" \ -d '{ "strategy_name": "test_ma_cross", + "strategy_code": "class TestStrategy(CtaTemplate): ...", "parameters": {"fast_window": 5, "slow_window": 20}, "start_date": "2024-01-01", "end_date": "2024-12-31", "symbol": "000001.SZ", "interval": "d" - }' -# 预期: {"code":0,"msg":"任务提交成功","data":{"task_id":"...","status":"pending"}} + }' | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['task_id'])") -# 查询结果(替换task_id) -curl http://192.168.2.154:8088/api/backtest/result/ +# 查询状态 +curl http://192.168.2.154:8088/api/backtest/status/$TASK_ID + +# 获取结果 +curl http://192.168.2.154:8088/api/backtest/result/$TASK_ID # 标准三:自动恢复 -# 在NAS上强制杀死容器,验证自动重启 sudo /var/packages/Docker/target/usr/bin/docker kill sanguo_vnpy -sleep 10 -curl http://192.168.2.154:8088/api/backtest/health -# 预期: 200(容器自动重启后服务恢复) +sleep 15 +curl -s -o /dev/null -w "%{http_code}" http://192.168.2.154:8088/api/backtest/health +# 预期: 200 ``` ### 4.2 回滚方案 | 场景 | 操作 | |------|------| -| 回测服务启动失败 | 容器仍可用,只是8088端口无服务,不影响Jupyter/SSH | -| 容器完全不可用 | `docker stop sanguo_vnpy && docker rm sanguo_vnpy`,用旧镜像重建 | -| 代码有bug | `docker cp` 修复的文件进容器,重启容器 | -| 需要完全回滚 | 用旧镜像 `sanguo_vnpy:with-scripts` 重建容器(旧镜像仍在NAS上) | - -**旧容器启动命令(回滚用)**: - -```bash -sudo /var/packages/Docker/target/usr/bin/docker run -d \ - -p 8888:8888 -p 8000:8000 -p 8080:8080 -p 8088:8088 \ - -p 2018:2018 -p 4102:4102 -p 2222:22 \ - --name sanguo_vnpy \ - sanguo_vnpy:with-scripts -``` +| 回测服务启动失败 | 容器仍可用(Jupyter/SSH正常),只影响8088 | +| 需要完全回滚 | `docker stop && docker rm`,用旧镜像 `sanguo_vnpy:with-scripts` 重建 | +| executor.py有bug | `docker cp` 修复的文件进容器,不重启容器 | +| 代码改坏 | git checkout恢复,重新docker cp | --- ## 五、实施计划 -| 步骤 | 操作 | 耗时 | 风险 | -|------|------|------|------| -| **Phase 1: 快速验证** | 方案A — docker cp + 容器内手动启动 | 5分钟 | 低(不影响现有服务) | -| **Phase 2: 代码修复** | 修复main.py等相对导入问题 | 15分钟 | 无(只改导入方式) | -| **Phase 3: 入口修改** | 修改entrypoint.sh加入backtest-service | 5分钟 | 低 | -| **Phase 4: 重建镜像** | 方案B — docker build + 部署 | 20分钟 | 中(需要短暂停服) | -| **Phase 5: 验证交付** | 执行4.1全部验证步骤 | 10分钟 | 无 | +| Phase | 操作 | 耗时 | 前置条件 | +|-------|------|------|---------| +| **1. 快速验证** | 容器内手动修复+启动backtest-service | 10min | 无 | +| **2. 代码修复** | 修复models.py/main.py/entrypoint.sh(4处) | 15min | Phase 1通过 | +| **3. executor重写** | 适配vnpy 4.x API | 30min | Phase 2通过 | +| **4. 重建镜像** | 修改Dockerfile + docker build + 部署 | 30min | Phase 3通过 | +| **5. 交付验证** | 执行4.1全部验证 | 10min | Phase 4完成 | -**建议先执行Phase 1快速验证,确认backtest-service能正常运行后再进行Phase 2-4。** +**Phase 1验证命令**(在当前运行容器内,不重建镜像): + +```bash +# SSH到NAS +ssh admin@192.168.2.154 + +# 进入容器 +sudo /var/packages/Docker/target/usr/bin/docker exec -it sanguo_vnpy bash + +# 容器内操作: +# 1. 安装缺失包 +pip3 install vnpy_ctastrategy vnpy_sqlite + +# 2. 创建修复后的目录 +rm -rf /tmp/backtest_service +cp -r /app/scripts/backtest-service /tmp/backtest_service + +# 3. 修复models.py泛型语法(sed替换,见3.2节#3) + +# 4. 修复main.py的uvicorn路径(sed替换,见3.2节#4) + +# 5. 用uvicorn启动 +PYTHONPATH=/tmp uvicorn backtest_service.main:app --host 0.0.0.0 --port 8088 + +# 6. 另一个终端验证 +curl http://localhost:8088/api/backtest/health +``` --- @@ -410,23 +344,23 @@ sudo /var/packages/Docker/target/usr/bin/docker run -d \ | 决策点 | 选择 | 理由 | |--------|------|------| -| 网络模式 | bridge(默认) | Jupyter已验证可用,无需改host模式 | -| 进程管理 | entrypoint.sh后台启动 | 简单可靠,无需supervisor | -| 导入方式 | 兼容相对/绝对导入 | 既支持 `python main.py` 直接运行,也支持包导入 | -| 数据访问 | Volume只读挂载 | NAS本地路径,性能好,安全 | -| 重启策略 | `unless-stopped` | 异常崩溃自动恢复,手动stop不自动重启 | -| 资源限制 | 4GB内存/1.5CPU | 给NAS留足余量,避免回测吃满资源 | +| 启动方式 | `PYTHONPATH=... uvicorn backtest_service.main:app` | 18处相对导入零改动(司马懿方案) | +| 目录名 | `backtest-service` → `backtest_service` | Python包名不允许连字符 | +| 数据加载 | 从CSV文件直接加载 | 绕过vnpy 4.x数据库API变更,NAS数据本来在CSV里 | +| 网络模式 | bridge(默认) | Jupyter已验证可用 | +| 重启策略 | `--restart unless-stopped` | 异常崩溃自动恢复 | +| 资源限制 | 4GB内存/1.5CPU | 给NAS留足余量 | --- -## 附录:问题诊断日志 +## 附录:容器内实际验证日志 ``` -[2026-04-27 20:00] 容器内 ps aux — 无uvicorn/FastAPI进程 -[2026-04-27 20:01] 容器内 ss -tlnp — 8088端口无监听 -[2026-04-27 20:02] iptables DNAT — 8088→172.17.0.2:8088 映射正常 -[2026-04-27 20:03] 手动启动backtest-service — ModuleNotFoundError -[2026-04-27 20:05] 确认容器内start_backtest_service.py为旧版import路径 -[2026-04-27 20:06] 确认main.py使用相对导入,无法直接运行 -[2026-04-27 20:10] 根因确认:三重问题叠加(entrypoint缺失+代码不同步+相对导入) +[2026-04-28 08:50] 容器内 pip list | grep vnpy → vnpy 4.3.0, vnpy_webtrader 1.1.0 +[2026-04-28 08:51] vnpy 4.3.0 无 BacktestingEngine(拆分到vnpy_ctastrategy包) +[2026-04-28 08:52] pip install vnpy_ctastrategy vnpy_sqlite → 成功 +[2026-04-28 08:53] from vnpy_ctastrategy.backtesting import BacktestingEngine → OK +[2026-04-28 08:53] from vnpy.event import EventEngine → OK +[2026-04-28 08:53] Interval枚举: 4.x只有MINUTE/HOUR/DAILY/WEEKLY/TICK +[2026-04-28 08:54] ApiResponse[T]语法 → Python 3.10不支持,需改Generic[T] ``` diff --git a/logs/auto-sync.log b/logs/auto-sync.log index 22d64582..a7005140 100644 --- a/logs/auto-sync.log +++ b/logs/auto-sync.log @@ -5389,5 +5389,6 @@ +