docs(data): spec§18终版——统一到sanguo-5m一次性容器模式+断点续传互借三件 [nas]——用户终拍板「统一到5min吧」:①运行时改docker run --rm同5m全款(bash wrapper=/volume1/stock/corpus/run_corpus.sh宿主文件CI不覆盖同5m部署纪律;镜像lock-aligned版本自动跟随CI;弱依赖=daemon+tag不改名与5m同款已跑一月实证),corpus两改进=DSM任务admin跑(实测admin可docker ps)+--user admin-uid输出文件admin属主+--name sanguo-corpus运维锚点(docker stop一条命令杀);反向统一硬阻塞入档=5m写的data_backup库是03:00链root属主产物admin venv写不进;统一后全图=VPS schtask家族(数值)/NAS docker run --rm(5m+corpus对外爬取)+sync链(内部搬运)机器内一种物理下限;②断点续传对比互借(bs_5m_eod.py盘点):corpus借三件=单实例锁pid活性检测(原设计真缺口,防DSM定时与手动补跑竞写state)+退出码细分(0完成/1致命/2限流让路/3墙钟自停对齐5m语义)+state损坏幂等降级;5m反向不改造(整片推进粒度粗但回灌已完结+OR REPLACE幂等+LOOKBACK自愈,改造无未来收益),仅两个顺手项下次动run_5m.sh一并(加--name/头注释时点19:05→00:05修正);验收5/5b改docker stop+主容器restart不影响实证;T1改wrapper+金丝雀 [nas]
CI/CD / test (push) Successful in 25s
CI/CD / nas-deploy (push) Successful in 2s
CI/CD / nas-verify (push) Successful in 6s

This commit is contained in:
2026-09-06 21:42:54 +08:00
parent 8aa3a3d2fc
commit fec3e8e221
@@ -413,22 +413,23 @@ FETCHERS 注册表新增两键即自动可被未来 provider MCP server 暴露
### 18.1 硬约束(用户原话级)
1. **存储全部落 NAS、一律不上 VPS**——「vps承载有压力,之前基本面都放在了nas上,没有放vps,本次
也一样」。VPS 零参与:零 schtask、零存储、零采集进程;repo 里进脚本仅为随 NAS 部署管道分发
(promote --target nas → /volume1/homes/admin/.sanguo_projects/sanguo_vnpy_v2/**宿主机
直读该路径,不经容器**)VPS 侧永远不会被调度。
2. **采集进程=NAS 宿主机直跑,不绑定 docker**(用户钦点:「不要和docker绑定,避免docker重启就
失败」)。运行时=/volume1/stock/corpus/venv(`python3 -m venv --copies` 自举,DSM 自带
Python 3.8.12/pip 已实测可用,x86_64;依赖锁 cp38 兼容版 pandas/pyarrow/requests)——容器
重启/升级/停用零影响,venv `--copies` 连 DSM 换 Python 也不怕。家宽出口 IP ≠ VPS 公网 IP
(49.232.102.198),与 VPS 侧 ak-daily/ak-events 的东财限流**零冲突**。09-06 实测佐证:
np-listapi/巨潮/东财文章页 ~35 发连续探测自家宽 IP 全成功、零限流。
**与 sanguo-5m 独立容器方案的差异**(用户问):两者同治「不被主容器重启波及」,但 5m 停在
docker 家族内(`docker run --rm` 一次性容器,仍依赖 daemon+镜像+tag 不改名,优点=镜像环境
完整+版本自动跟随主容器),corpus 按用户指令走到底(零 docker)。5m 当年未走到底是合理的——
bs_5m_eod.py 依赖面=整个镜像环境(baostock 等),宿主 3.8 venv 装不齐;corpus 脚本依赖面
收敛为 3 通用包才有条件零 docker(代价=venv 冻结,版本漂移需手动 pip 对齐,已由 T1 锁版本
缓解)。**5m 不迁**:在跑且稳定、迁移需重验 baostock@3.8、没坏不动;但其为存量 docker
依赖点,NAS docker 套件大升级/镜像重建时需人工盯,届时再评估迁 venv。
也一样」。VPS 零参与:零 schtask、零存储、零采集进程;repo 里进脚本随 nas-deploy 到
`/volume1/homes/admin/.sanguo_projects/sanguo_vnpy_v2/`(= 采集容器的 `-v` 只读挂载 `/app`,
与 5m 同款分发通道)VPS 侧永远不会被调度。
2. **采集进程=一次性容器,与 sanguo-5m 统一**(用户终拍板 2026-09-06「统一到5min吧」)。方案
演进:初版按用户「不要和docker绑定,避免docker重启就失败」设计为宿主机 venv 零 docker;
验收中用户权衡「不想搞出多种下载方法」,比较两方向后拍板 **corpus 向 5m 的 `docker run --rm`
模式统一**(反向统一有硬阻塞:5m 写入的 data_backup 库为 03:00 链 root 属主产物,admin venv
写不进;且需重验 baostock@py3.8、动在跑系统)。**被治的痛点不变**——主容器重启不再杀任务
(08-22 5m 被 CI 杀三案后的同款根治);**保留的弱依赖**=docker daemon 在跑+镜像 tag 不改名
(与 5m 同款,已跑一个月实证;daemon 停一天,数据次日自愈)。**两项 corpus 专属改进**:①DSM
任务以 **admin** 跑(09-06 实测 admin 可执行 docker 命令);②容器加 `--user <admin-uid>`
输出文件 admin 属主(root 文件坑不复发),**杀进程=`docker stop sanguo-corpus`**(admin 一条
命令)。镜像环境完整+版本自动跟随 CI 部署,零 venv 自举/锁版本/漂移问题。家宽出口 IP ≠ VPS
公网 IP(49.232.102.198),与 VPS 侧 ak-daily/ak-events 的东财限流**零冲突**。09-06 实测
佐证:np-listapi/巨潮/东财文章页 ~35 发连续探测自家宽 IP 全成功、零限流。**统一后全图**
(机器内一种,物理下限):VPS=Windows schtask 家族(数值类)NAS=DSM→bash wrapper→
`docker run --rm`(5m+corpus 同款,对外爬取)+sync 链(内部搬运)。
3. **时间带**:全部采集限于 **00:3017:30**,避开 18:0523:45 傍晚带(VPS 夜任务密集+家宽使用)。
唯一日窗=每日 06:00 起(见 18.3)。
4. 慢爬纪律:单进程单线程、per-domain 限速+抖动、直连不走代理(Session.trust_env=False,防
@@ -467,9 +468,9 @@ Schema(列名冻结,消费方=factor session 因子加工层,后续 provider fet
- **脚本**: `scripts/data_platform/corpus_download.py`(repo 内唯一新文件;单脚本红线同款——
corpus 栈一个入口,类型靠内部注册表,禁再开新脚本)。参数 `--lane daily|backfill`
`--until HH:MM`(墙钟自停)、`--limit N`(手动冒烟)。
- **调度**: DSM 任务计划 **sanguo-corpus-daily,每日 06:00 一条,以 admin 身份跑**(先例=现有
03:00 同步链 DSM 任务(id=2),宿主机直跑多年零 docker;本设计沿同模式但改 admin 跑,避该链
root 属主文件坑)。
- **调度**: DSM 任务计划 **sanguo-corpus-daily,每日 06:00 一条,以 admin 身份跑**,机制=与
sanguo-5m 完全同款的「bash wrapper → 一次性容器」(wrapper=`/volume1/stock/corpus/
run_corpus.sh`,宿主文件 CI 不覆盖,改后手动同步——同 5m 的 run_5m.sh 部署纪律)。
**为何 admin 而非统一 root**(用户问):①03:00 链 root 属主文件坑已在案——admin scp 覆盖
root 文件 Permission denied 只能先 rm 再拉;语料库生命周期内 admin 侧操作密集(venv 装包/
回补盯跑/删错分区/debug),admin 属主=全程零摩擦 ②最小权限:出网慢爬+写文件的进程没必要
@@ -477,10 +478,10 @@ Schema(列名冻结,消费方=factor session 因子加工层,后续 provider fet
的是坏模式。**存量脚本一个都不迁移**(用户问「有必要全改admin吗」:没有——03:00 链等既有
root 任务照旧跑,没坏不动;admin 仅是新建 corpus 任务从第一天的选型;两套并行零冲突:目录
隔离/时间窗错开/部署管道不同,互不碰对方文件)。**admin 权限逐项实测**(2026-09-06):
/volume1/stock 建目录/写/删全通(rwxrwxrwx+ACL,admin 属主);venv `--copies` 在 /volume1
建成+stdlib OK+pip user-site 既有;读 admin 家 CI 部署路径无障碍;⚠️坑:venv 建在 **/tmp
会 Permission denied**(DSM /tmp 特殊限制,且 /tmp 会被清空)——设计本就落 /volume1,勿改
/tmp。设计内无任何一步需要 root;万一未来例外,人工 sudo/改任务属主是逃生门非设计依赖。
/volume1/stock 建目录/写/删全通(rwxrwxrwx+ACL,admin 属主);**admin 可执行 docker 命令**
(docker ps 实测通过)→ wrapper/容器/`docker stop` 全程 admin 可运维;读 admin 家 CI 部署
路径无障碍(初版 venv 方案及其 /tmp 坑实测,已随「统一到 5m」拍板作废,记录在 git 历史)。
设计内无任何一步需要 root;万一未来例外,人工 sudo/改任务属主是逃生门非设计依赖。
**root vs admin 终局论证**(用户问「root 数据不可修改的设计更好吗」):root 属主的「写保护」
是半吊子幻觉——admin 不能覆写 root 文件**但能删**(删看目录权限,/volume1/stock rwxrwxrwx,
03:00 链「先 rm 再拉」workaround 实证),防住原地改防不住删了重建;而 root 跑采集的代价是
@@ -502,10 +503,16 @@ Schema(列名冻结,消费方=factor session 因子加工层,后续 provider fet
root 地盘 CI 不覆盖须手动 scp,corpus 脚本走 CI deploy 的 repo 路径,搅进一条任务两头别扭
③06:00 起跑避开 03:00 链跑道(26G 库同步 IO+家宽带宽),且 00 时档公告(54%)凌晨已入库,
06:00 全吃到;DSM 加一条任务代价≈零,改 root 生产链脚本才是真风险:
`/volume1/stock/corpus/venv/bin/python -X utf8
/volume1/homes/admin/.sanguo_projects/sanguo_vnpy_v2/scripts/data_platform/corpus_download.py
--lane daily && 同解释器 --lane backfill --until 17:30`
(两 lane 串行=天然互斥;rc 聚合,任一 lane 失败 log 留痕)。**全程零 docker 依赖**。
wrapper 内部(同 5m 结构+corpus 两改进):
`/var/packages/Docker/target/usr/bin/docker run --rm --name sanguo-corpus
--user <admin-uid> --entrypoint python
-v /volume1/stock:/volume1/stock
-v /volume1/homes/admin/.sanguo_projects/sanguo_vnpy_v2:/app:ro
sanguo_vnpy_v2:lock-aligned /app/scripts/data_platform/corpus_download.py --lane daily`
成功后同款第二发 `--lane backfill --until 17:30`(两 lane 串行=天然互斥;rc 聚合,任一 lane
失败 log 留痕)。**`--name sanguo-corpus`=运维锚点:杀进程=`docker stop sanguo-corpus`(admin
一条命令);`--user <admin-uid>`=输出文件 admin 属主**。镜像 tag 与主容器同源,CI 部署自动
跟随;tag 若将来基建重建改名,wrapper 跟着改(与 5m 同款注意事项)。
**断点续传(用户钦点加固,所有 lane 的第一设计原则)**
- 粒度=unit(公告=(stock,year)/新闻=(stock,page)/全文/PDF=单 id),每 unit 一枚 marker(state/
@@ -514,6 +521,15 @@ Schema(列名冻结,消费方=factor session 因子加工层,后续 provider fet
- daily lane 幂等可重跑(短窗 id 去重,重复执行零副作用);漏日自愈=公告 T-1~T 两天窗,新闻
recent-100 页面天然覆盖数日缺口(冷门股 100 条跨数月)——某天整个任务没跑,次日一次执行即可
补齐,不依赖专门回补。
- **从 sanguo-5m 借鉴三件**(用户问「断点续传是否一样/可否互借」后盘点 bs_5m_eod.py):①
**单实例锁**(bs_5m.lock pid 活性检测同款——防 DSM 定时与手动补跑撞车双进程竞写 state,
corpus 原设计缺口,采纳为 state/corpus.lock)②**退出码细分**(对齐 5m 语义并适配:
0=完成,1=致命,2=限流让路,3=墙钟自停让路——rc 即运维信号)③**state 损坏降级路径**
(5m「state 坏→幂等重头无伤」思想;corpus=marker 坏→该 unit 重拉,append-only+id 去重
保证幂等无伤)。**5m 反向无需借鉴改造**:其整片推进粒度粗(半年片 3-4h,中断=整片重拉),
但回灌已完结、OR REPLACE 幂等无害、每日增量 LOOKBACK=7 独立自愈,改造无未来收益——
**5m 不做结构性改造**,仅两个顺手项下次动 run_5m.sh 时一并(docker run 加 --name 便于
定向 stop;头注释 DSM 时点 19:05→00:05 过时修正),不专门跑。
**daily lane(每日,总 ~4.5h,06:0010:30 前后)**(06:00 晨窗依据 09-06 实测:深市 600 条公告
发布时刻分布=盘中(09-15时)**0%**(3条)、盘后 15-20 时 46%、**00 时档 54%**(巨潮深夜批处理
@@ -564,10 +580,12 @@ PDF 按可得性实下(404 标 missing 不重试);每源每股记录 first_date
3. **news 回补**: 600519 最早条目落 2023-09±1 月(实测硬顶对齐);depth_summary.json 全量产出。
4. **PDF**: 抽 20 份跨年份可打开(%PDF 魔数+size>0+meta 可反查路径);索引行数=文件数。
5. **边界**: /volume1/stock/corpus 之外零写入;VPS 零改动(git diff 触及面仅 scripts+tests+docs);
容量闸门演练一次(临时阈值)证明会停;**docker 全停状态下手动触发 corpus-daily 照跑通**
(不绑容器的验收实证)。
5b. **断点续传**: 回补中途 `kill -9` 采集进程→重启后从未完成 unit 续跑,已完成 unit 零重拉
(marker 核对);某日整个任务停跑一天→次日单次执行把漏日补齐(T-1~T 窗+recent-100 覆盖)。
容量闸门演练一次(临时阈值)证明会停;**主容器 restart(nas-deploy)不影响在跑的 corpus 容器**
(一次性容器与主容器互不相干,5m 已实证同款);手动 `docker stop sanguo-corpus` 即停,
再触发从断点续跑。
5b. **断点续传**: 回补中途 `docker stop sanguo-corpus`→重启后从未完成 unit 续跑,已完成 unit
零重拉(marker 核对);某日整个任务停跑一天→次日单次执行把漏日补齐(T-1~T 窗+recent-100
覆盖);**单实例锁实证**:DSM 定时在跑时手动触发→让路退出(退出码=2/3 类),双进程零竞写。
6. **测试**: tests/data_platform/ 新增 CI 测试(fetch 全 mock 零网络)——失败语义/短窗去重/
两天窗自愈/墙钟自停/unit marker,全套本地绿+CI 绿。
@@ -579,10 +597,11 @@ parquet);月度 compaction(数据量小,YAGNI)。
### 18.7 实施步骤概要(验收通过后,TDD)
T1 宿主机 venv 自举(`--copies`,锁 cp38 兼容版本 pandas/pyarrow/requests,清华镜像)+连通金丝雀
(每域 1 发)+目录骨架 → T2 corpus_download.py daily lane 四段+断点续传 state 机制+测试 →
T3 DSM 任务注册(admin 身份,零 docker)+首周人工盯跑 → T4 backfill lane ①② → T5 ③④+容量闸门
→ T6 验收清单执行+活文档本节补「实施终态」小节。每步一个 commit,标签 **[nas]**(VPS 零触及)。
T1 wrapper+连通金丝雀(run_corpus.sh 按 5m 同款结构编写;`docker run --rm --user <uid>` 跑通
镜像 python+每域 1 发出网验证+目录骨架)→ T2 corpus_download.py daily lane 四段+断点续传
state 机制+单实例锁+测试 → T3 DSM 任务注册(admin 身份)+首周人工盯跑 → T4 backfill lane ①②
→ T5 ③④+容量闸门 → T6 验收清单执行+活文档本节补「实施终态」小节。每步一个 commit,标签
**[nas]**(VPS 零触及)。
### 18.8 全局下载方案与存放位置总表(回应「不想搞好几套」——本节=现势唯一完整说明)
@@ -606,7 +625,7 @@ daily/weekly/monthly/events 三频率+ak-vintage 自检):
| ak-vintage 自检 | VPS | 09:00 | 缺日/错标防线 | vintage_status.json |
| NAS sync 链(DSM id=2,root) | NAS | 03:00 | VPS→NAS 副本拉取(7 域) | /volume1/stock/… |
| sanguo-5m(DSM id=7,root) | NAS | 00:05 | baostock 5min 增量+全量回补(独立一次性容器 `docker run --rm` 跑 lock-aligned 镜像,08-22 治「CI 重启主容器杀在跑任务」;窗口 2020 起,含退市 5547 只,runner=/volume1/stock/sanguo_5m) | **NAS 副本库 dbbardata('5m')=5m 唯一权威** |
| **sanguo-corpus-daily(DSM,admin)** | **NAS** | **06:00** | **巨潮公告+东财新闻(本设计)** | **/volume1/stock/corpus/** |
| **sanguo-corpus-daily(DSM,admin)** | **NAS** | **06:00** | **巨潮公告+东财新闻(本设计,一次性容器同 5m 模式)** | **/volume1/stock/corpus/** |
**存放位置全景**:**数值权威=VPS `data/`**(dbbardata 26G+static/+valuation_baostock/),
**两个例外在 NAS**:①**5min——权威=NAS 副本库**`/volume1/stock/sanguo_vnpy_v2/data_backup/