Files
sanguo_vnpy_v2/docs/deployment/vps-production-runbook.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

18 KiB
Raw Blame History

VPS 生产运维 Runbook

sanguo QMT bridge 生产环境运维手册。 VPS 已部署并在运行,本文档是"运维 + 已验证状态记录",不是从零搭建指南。

相关文档:

安全约定:本文档进 git不硬编码 BRIDGE_TOKEN。所有命令中 <BRIDGE_TOKEN> 为占位符,真实值见 Claude memory windows-vps-access.md 或 VPS 系统环境变量 BRIDGE_TOKEN


1. 已验证生产状态(2026-07-14 巡检)

以下事实经实地验证,直接采纳。下次巡检时更新日期并重新核实。

项目 备注
VPS 49.232.102.198 腾讯云轻量,Win Server 20224C/16G/180G SSD
SSH ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198 Mac ~/.ssh/id_ed25519 免密
磁盘 C: 24G 用 / 156G 空闲 充裕
Python C:\Python310\python.exe (3.10.11) xtquant import OK
miniQMT C:\国金QMT交易端\ userdata userdata_mini 模拟账户 66639661 已登录
bridge 代码 C:\sanguo_qmt_bridge\ FastAPI + uvicorn :8765
bridge 启动脚本 C:\run_bridge.ps1 设环境变量 + BRIDGE_SESSION_ID + uvicorn
Caddy C:\caddy\ + C:\run_caddy.ps1 :443 → :8765 反代,Let's Encrypt 自动续期
schtasks sanguo-bridge + sanguo-caddy 均 Running SYSTEM 账户 / onstart 自启

进程状态

进程 映像名 角色
XtMiniQmt XtMiniQmt.exe miniQMT 客户端,已登录模拟账户
python python.exe uvicorn bridge :8765
caddy caddy.exe HTTPS 反代 :443 → :8765

bridge 端点验证(VPS 本地 127.0.0.1:8765

端点 响应
GET /health {"status":"ok","miniqmt_connected":true}
GET /account {"ok":true,"cash":9997075.51,"frozen":9001769.0,"market_value":2901.0,"total":9999978.51}
GET /positions {"ok":true,"positions":[...sh600000(200股), sz000001(100股)...]}

三条访问路径

# 路径 地址 场景
VPS 本地 http://127.0.0.1:8765 运维 RDP/SSH 内验证
公网 HTTPS https://bridge.mysanguo.top 外部客户端(Mac 浏览器等)
NAS 容器经 Mac 隧道 http://192.168.2.101:8765 NAS sanguo 容器调 bridge

2. 拓扑图

┌───────────────────────────────────────────────────┐
│          Windows VPS  ·  49.232.102.198           │
│          Win Server 2022 · 4C/16G/180G SSD        │
│                                                   │
│   ┌───────────┐  xtquant  ┌──────────────┐        │
│   │  miniQMT  │◄──────────│   bridge     │        │
│   │  66639661 │           │   :8765      │        │
│   └───────────┘           └──────┬───────┘        │
│                                  │ 反代            │
│                           ┌──────▼───────┐        │
│                           │    Caddy     │        │
│                           │    :443      │        │
│                           │  Let's Encr. │        │
│                           └──────┬───────┘        │
└──────────────────────────────────┼────────────────┘
                                   │
          ┌────────────────────────┼───────────────────┐
          │                        │                   │
     ① VPS 本地              ② 公网 HTTPS         ③ NAS→Mac→VPS
  http://127.0.0.1:8765    https://bridge.       http://192.168.2.101
  (运维 RDP/SSH)            mysanguo.top              :8765
          │                        │                   │
          ▼                        ▼                   ▼
    ┌──────────┐           ┌──────────────┐   ┌──────────────┐
    │ VPS 终端  │           │ 外部客户端    │   │  Mac Mini     │
    └──────────┘           │(Mac 浏览器等) │   │  192.168.2.101│
                           └──────────────┘   │  开发 + 隧道   │
                                              │              │
                                              │ ssh -N -L    │
                                              │ 0.0.0.0:8765:│──SSH:22──► VPS
                                              │ 127.0.0.1:8765│
                                              │              │
                                              │ caffeinate   │
                                              │ -i -s 防睡眠 │
                                              └──────┬───────┘
                                                     │
                                              ┌──────▼───────┐
                                              │  NAS 容器     │
                                              │ 192.168.2.154│
                                              │ sanguo_vnpy  │
                                              │ _v2 (测试+备) │
                                              └──────────────┘

  ⚠ 华为光猫拦截 NAS→VPS 的 80/443 → NAS 不能走路径②
    → NAS 容器走路径③:HTTP 到 Mac :8765 → Mac SSH 隧道 → VPS :8765
    → Mac 需常驻 SSH tunnel + caffeinate -i -s 防睡眠

三机分工

机器 IP 角色 能做什么 不能做什么
VPS 49.232.102.198 生产 miniQMT + bridge + Caddy 全链路 无开发环境
Mac Mini 192.168.2.101 开发 + 隧道中继 写代码、git、scp 部署、SSH 隧道 跑不了 xtquantWindows only
NAS 192.168.2.154 测试 + 备份 Docker 容器跑集成测试、回测、每日备份 无法直连 VPS 80/443(光猫拦截)

3. 日常运维操作

以下命令均从 Mac Mini 执行,通过 SSH 远程操控 VPS。

3.1 健康巡检(一条命令验三端点)

快速健康(无需 token):

ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198 \
  "curl -s http://127.0.0.1:8765/health"

期望:{"status":"ok","miniqmt_connected":true}

完整三端点验证(含 account + positions,需 token):

复杂引号场景用 stdin 喂 PowerShell(见 §8 Windows 坑):

ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198 "powershell -Command -" <<'PS1'
Write-Host "=== health ==="
curl.exe -s http://127.0.0.1:8765/health
Write-Host "`n=== account ==="
curl.exe -s -H "X-Bridge-Token: <BRIDGE_TOKEN>" http://127.0.0.1:8765/account
Write-Host "`n=== positions ==="
curl.exe -s -H "X-Bridge-Token: <BRIDGE_TOKEN>" http://127.0.0.1:8765/positions
PS1

检查 schtasks 状态 + 进程 + 磁盘:

ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198 "powershell -Command -" <<'PS1'
Write-Host "=== schtasks ==="
schtasks /query /tn "sanguo-bridge" /fo list | Select-String "Status"
schtasks /query /tn "sanguo-caddy" /fo list | Select-String "Status"
Write-Host "=== processes ==="
Get-Process python,caddy,XtMiniQmt -ErrorAction SilentlyContinue | Format-Table Name,Id,CPU -Auto
Write-Host "=== disk ==="
Get-PSDrive C | Format-Table Used,Free -Auto
PS1

3.2 bridge 重启(必须换 BRIDGE_SESSION_ID

关键xtquant 的 connect() 复用旧 session 会返回 -1。每次重启 bridge 前必须换 BRIDGE_SESSION_ID,否则 bridge 起来但 miniQMT 连不上。

步骤:

  1. 改 session_id — RDP 或 SSH 编辑 C:\run_bridge.ps1,把 $env:BRIDGE_SESSION_ID 改为新值(如日期递增 2026071420260715 或加后缀 20260714b):
# SSH 在线编辑(PowerShell 替换)
ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198 "powershell -Command -" <<'PS1'
$path = "C:\run_bridge.ps1"
$content = Get-Content $path -Raw
# 把旧 session_id 替换为今天的(按实际值调整正则)
$newId = Get-Date -Format "yyyyMMddHHmm"
$content = $content -replace 'BRIDGE_SESSION_ID\s*=\s*"\d+"', "BRIDGE_SESSION_ID = `"$newId`""
Set-Content $path -Value $content -Encoding UTF8
Write-Host "session_id updated to $newId"
# 确认
Select-String -Path $path -Pattern "BRIDGE_SESSION_ID"
PS1
  1. 重启 schtask
ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198 \
  "schtasks /end /tn \"sanguo-bridge\" & schtasks /run /tn \"sanguo-bridge\""
  1. 等 5 秒后验证健康(同 §3.1 快速健康命令)。

3.3 Caddy 重启

Caddy 无 session 状态问题,直接重启:

ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198 \
  "schtasks /end /tn \"sanguo-caddy\" & schtasks /run /tn \"sanguo-caddy\""

验证公网:

curl -s https://bridge.mysanguo.top/health

3.4 miniQMT 崩溃恢复(human-gated

miniQMT 是券商客户端 GUI 程序,崩溃后需要人工 RDP 登录。无法自动化。

症状/health 返回 {"miniqmt_connected": false},或 bridge 日志 xtquant connect 报错。

恢复步骤(人工操作):

  1. RDP 登录 VPS(远程桌面 49.232.102.198
  2. 手动启动 miniQMT 客户端 → 登录模拟账户 66639661
  3. 确认客户端进入极简模式/独立交易界面
  4. 换 BRIDGE_SESSION_ID 后重启 bridge(同 §3.2
  5. 验证 /healthminiqmt_connected: true

4. dev → test → prod 发布流水线

  Mac Mini (开发)              NAS (测试)                VPS (生产)
  ─────────────               ──────────               ──────────
  写代码                        git pull                  scp bridge 代码
  sanguo_qmt_bridge/           → 容器跑集成测试            → schtasks 重启
  本地 unit test                验证通过                    → 健康巡检
       │                           │                         │
       ▼                           ▼                         ▼
  git push ──────────►  gitea ──────►  NAS pull ────►  VPS deploy
  (git.mysanguo.top)              (Docker 容器)         (scp + restart)
                                                           │
                                                     每日 VPS 状态
                                                     备份 → NAS
阶段 机器 动作 验证
dev Mac Mini sanguo_qmt_bridge/ 代码 本地 unit testpytest tests/
push Mac Mini git push origin master gitea 仓库更新
test NAS 容器 git pull + 跑集成测试 bridge_client 测试通过、数据管道 OK
prod VPS scp 变更文件 + 重启 bridge /health + /account + /positions 三通
backup NAS 每日 VPS 状态备份到 NAS bridge 代码 + config 快照

注意NAS 测试只能验 bridge_client 端(发 HTTP 请求到 bridge),无法验 xtquant/miniQMT 端(Windows only)。bridge 服务端 + xtquant + miniQMT 的端到端验证只能在 VPS 上做。


5. 代码部署到 VPSMac → VPS scp + 重启)

5.1 scp 变更文件

bridge 代码在 Mac 的 sanguo_qmt_bridge/ 目录,改完后 scp 到 VPS

# 只传变更的 .py 文件(快速迭代)
scp -i ~/.ssh/id_ed25519 \
  sanguo_qmt_bridge/bridge.py \
  sanguo_qmt_bridge/xt_gateway.py \
  sanguo_qmt_bridge/auth.py \
  Administrator@49.232.102.198:C:/sanguo_qmt_bridge/

# 或整目录同步(含 requirements.txt 等)
scp -i ~/.ssh/id_ed25519 sanguo_qmt_bridge/*.py \
  Administrator@49.232.102.198:C:/sanguo_qmt_bridge/

Windows scp 路径用正斜杠C:/sanguo_qmt_bridge/(不是反斜杠)。

5.2 换 session_id + 重启

# 1. 换 BRIDGE_SESSION_ID(同 §3.2 步骤 1
ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198 "powershell -Command -" <<'PS1'
$path = "C:\run_bridge.ps1"
$content = Get-Content $path -Raw
$newId = Get-Date -Format "yyyyMMddHHmm"
$content = $content -replace 'BRIDGE_SESSION_ID\s*=\s*"\d+"', "BRIDGE_SESSION_ID = `"$newId`""
Set-Content $path -Value $content -Encoding UTF8
Write-Host "session_id → $newId"
PS1

# 2. 重启 schtask
ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198 \
  "schtasks /end /tn \"sanguo-bridge\" & schtasks /run /tn \"sanguo-bridge\""

# 3. 等 5 秒,验证
sleep 5
ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198 \
  "curl -s http://127.0.0.1:8765/health"

5.3 如改了 requirements.txt(依赖变更)

ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198 \
  "cd C:\sanguo_qmt_bridge && C:\Python310\python.exe -m pip install -r requirements.txt"
# 然后同 5.2 重启

6. 人类闸口清单

以下操作无法自动化,必须人工执行。自动化脚本碰到这些步骤应明确报"需人工干预"。

操作 为什么只能人做 频率
VPS 开通 / 重置密码 云厂商控制台操作 极低(首次/安全事件)
miniQMT 券商客户端登录 GUI 程序 + 可能需验证码/密码 崩溃后 / VPS 重启后
BRIDGE_TOKEN 轮换 需同时在 VPS + NAS + Mac 三处同步更新 定期(安全策略)
Let's Encrypt 证书异常处理 Caddy 自动续期,但 Rate Limit / DNS 异常需人工介入 极低(自动续期正常时无需干预)
华为光猫 / 网络配置变更 运营商设备,SSH 碰不到 极低
VPS 计划任务创建/修改 首次配置 schtasks /create 需 RDP 低(配置变更时)

BRIDGE_TOKEN 轮换流程(人工)

  1. 生成新 tokenpython -c "import secrets; print(secrets.token_urlsafe(32))"
  2. VPS:更新 C:\run_bridge.ps1 里的 BRIDGE_TOKEN(或系统环境变量)
  3. NAS:更新 sanguo 容器环境变量 + docker restart sanguo_vnpy_v2
  4. Mac:更新 sanguo_trader/bridge_client.py 引用的配置或环境变量
  5. VPS:换 session_id + 重启 bridge(§3.2
  6. 全链路验证三端点

7. 排障表

现象 根因 排查 / 修复
/healthminiqmt_connected: false miniQMT 未登录 / 崩溃 / userdata 路径错 RDP 检查 miniQMT 客户端状态(§3.4),确认 MINIQMT_USERDATA 指向正确 userdata_mini
bridge 启动 xtquant connect 返回 -1 BRIDGE_SESSION_ID 与旧 session 冲突 换 session_id 后重启(§3.2)——这是最常见坑
401 token 无效 VPS 与 NAS/Mac 的 BRIDGE_TOKEN 不一致 核对三处 token 值一致
公网 https://bridge.mysanguo.top 502 Caddy 没启 或 bridge 没启 先验 VPS 本地 curl 127.0.0.1:8765/health;本地通=bridge OK→查 Caddy(§3.3);本地不通→查 bridge
公网 DNS 解析到错误 IP(如 198.18.1.244 Mac 本地代理(Clash/SurgeDNS 劫持到 fake-IP。公网 DNS8.8.8.8)解析正确到 49.232.102.198,但代理层拦截 修复①:/etc/hosts49.232.102.198 bridge.mysanguo.top;修复②:代理规则里该域名直连(bypass proxy
NAS 容器访问 bridge.mysanguo.top 超时 华为光猫拦截 NAS→VPS 的 80/443 走路径③:Mac SSH 隧道 http://192.168.2.101:8765Mac 需常驻 tunnel + caffeinate
Mac SSH 隧道断了 → NAS 容器连不上 bridge Mac 睡眠 / SSH 进程退出 Mac 跑 caffeinate -i -s & 防睡眠;用 autossh 或 launchd 守护 SSH tunnel
下单报 [120141][证券交易未初始化] 非交易日(miniQMT 交易日才初始化交易通道) 等交易日。这是 miniQMT 设计,不是 bug
影子下单未触发 live.enabled 未开 / 当日无成交 / BRIDGE_TOKEN 未设 / bridge_url 不对 逐项检查 NAS config/data_platform.yaml + 环境变量
重复下单 不会。paper_shadow_ordersUNIQUE(account_id, trade_id) 幂等去重
cmd 中文/emoji 乱码 cmd 默认 GBK 编码 用 PowerShell + chcp 65001,或 python 加 -X utf8(§8
scp 中文路径失败 Windows 中文目录 + SSH 编码 用正斜杠路径 + ASCII 变量名,中文路径用搜索代替字面量

8. Windows 跑命令的坑

Windows SSH 远程跑命令有三个经典坑:编码、引号、中文路径。

8.1 编码(GBK → UTF8

cmd 默认 GBK,中文输出和 emoji 会乱码。

# PowerShell 设 UTF8(代码页 65001
ssh ... "powershell -Command \"[Console]::OutputEncoding = [Text.Encoding]::UTF8; chcp 65001; <你的命令>\""

# Python 加 -X utf8
ssh ... "C:\Python310\python.exe -X utf8 script.py"

8.2 引号嵌套 → 用 stdin

SSH → cmd → PowerShell 三层引号极易出错。复杂脚本用 stdin 喂:

# powershell -Command - 从 stdin 读脚本
ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198 "powershell -Command -" <<'PS1'
# 这里写 PowerShell,引号无需转义
curl.exe -s -H "X-Bridge-Token: <BRIDGE_TOKEN>" http://127.0.0.1:8765/account
PS1

<<'PS1' 的单引号防止 Mac shell 展开变量,PowerShell 在 VPS 侧原样执行。

8.3 中文路径

C:\国金QMT交易端\ 等中文路径在 SSH 传输中可能因编码错乱。

  • scp 目标用正斜杠:C:/sanguo_qmt_bridge/ASCII
  • 需引用中文路径时,在 PowerShell 内用变量拼接或 Get-ChildItem 搜索,不写字面量:
# 不写 "C:\国金QMT交易端\...",用搜索
$qmt = Get-ChildItem C:\ -Directory | Where-Object Name -like "*QMT*"

8.4 curl vs Invoke-WebRequest

PowerShell 里 curl 默认是 Invoke-WebRequest 的别名(参数语法不同)。要用的标准 curl:

curl.exe -s http://...          # 显式 .exe 绕过别名

cmd 里 curl 直接就是 curl.exe,无此问题。