dpl/docs/maintenance/NODE_BATCH.md
hellcat 1de59971f6
Some checks are pending
Docker Build and Publish / build (push) Waiting to run
docs: record three-node Japan batch acceptance and recovery
2026-10-17 09:11:53 +08:00

16 KiB
Raw Blame History

节点批量接入

本专题说明批量调度;单节点操作、append 的保护范围与验收仍以 执行手册 为准,展示编号以 命名规则 为准。

2026-10-16:新增标准库 Python 调度器、命令适配器和离线进程测试。没有部署、连接服务器或增加生产节点。调度器可以运行经过审阅的任务适配器;**没有内置 Cloudflare/中转后台 API,也没有替使用者实现各站点的 SSH 登录与现场检查。**生产任务必须先按下述协议配置命令,不能将离线测试通过视为生产全链路可用。

文件与运行方式

文件 用途
scripts/node-onboarding/batch.py 参数校验、资源预留、并发、进程间锁、逐阶段账本与恢复
scripts/node-onboarding/command-adapter.py 调用经过摘要固定的任务命令;不经过 shell,缺命令返回等待
tests/node-onboarding/test_batch.py 临时目录模拟远端,验证并发、重复运行和回包丢失;不加载 Laravel/.env

在仓库根目录运行,计划文件与状态目录用绝对路径,放在受保护任务目录。计划只含非秘密参数和引用;不要加入密码、令牌、私钥内容、含订阅令牌的完整 URL 或命令行凭据。

python3 scripts/node-onboarding/batch.py plan --plan /private/task/batch.json --state-root /private/task/ledger
python3 scripts/node-onboarding/batch.py run --plan /private/task/batch.json --state-root /private/task/ledger --jobs 4 --timeout 900 --execute
python3 scripts/node-onboarding/batch.py status --plan /private/task/batch.json --state-root /private/task/ledger

plan 仅做本地参数校验,不预留资源、不访问网络、不生成状态目录。status 只读账本,不触发命令或创建账本。run --execute 才可能执行写入。退出码 0 表示本次要求完成;2 表示校验失败、等待、冲突、未知或未完成,不应自动当成临时网络错误无限重试。默认并发 4,上限 16;timeout 是每次适配器调用的超时秒数,不是整个批次的时限。

参数格式

以下使用保留示例地址,不能直接运行生产任务。provision_commit 填已审阅、已推送的完整 Git 提交,bundle 与客户端二进制路径必须由前检核验。VPS ID 与实际地址从 CoDocs 对应资源读取,不能从展示编号推断。所有节点放在同一个 nodes 数组,SOCKS 端口各不相同。

{
  "version": 1,
  "mode": "append",
  "batch_id": "jp-batch-20261016",
  "context": {
    "panel_vps": "VPS-001",
    "gateway_vps": "VPS-002",
    "jump_vps": "VPS-003",
    "app_root": "/absolute/panel/app",
    "panel_url": "https://panel.example.invalid",
    "subscription_url": "https://subscribe.example.invalid",
    "provision_commit": "0000000000000000000000000000000000000000",
    "bundle": "/private/task/reviewed.bundle",
    "client_binary": "/absolute/gateway/sing-box"
  },
  "adapter": ["/absolute/python3", "/absolute/repo/scripts/node-onboarding/command-adapter.py"],
  "command_profile": {
    "files": {"/private/task/handler.py": "填写该文件的SHA256"},
    "phases": {
      "preflight": {
        "probe": ["/absolute/python3", "/private/task/handler.py", "--plan", "{plan}", "--node", "{node}", "--phase", "{phase}", "--action", "{action}", "--state-dir", "{state_dir}"]
      }
    }
  },
  "nodes": [{
    "key": "jp-new-a",
    "country": "JP", "sequence": 2, "flag": "🇯🇵", "country_name": "日本",
    "name": "🇯🇵 日本 JP - 02",
    "vps_id": "VPS-011", "node_address": "192.0.2.11", "listen_port": 24443,
    "relay_host": "relay.example.invalid", "relay_port": 47011,
    "tls_domain": "jp-new-a.example.invalid", "formal_group_id": 1,
    "expected_exit": "192.0.2.11", "socks_port": 19011
  }]
}

示例只声明 preflight 命令,其他阶段必然等待。实际任务可为八个阶段逐一登记 probe,并在 stage/dns/provision/relay/publish 登记有权限执行的 apply。缺少 API 的阶段可以只提供读取实际状态的 probe,由操作者完成外部操作后续跑。不要使用“手工勾选成功”或静态回执替代现场 probe。

files 必须覆盖本地适配器、handler 及其加载的操作配置/脚本;不要只固定 Python 解释器。任一摘要变化停止,不接受在原批次中改命令后直接续跑。远端代码、安装包、二进制也由前检按明确 Git 提交/摘要核验。通用调度器不会自动检查命令是否遵守这些语义,也不会替命令构造 SSH 配置。adapter 亦可指向符合协议的专用程序,不必使用命令适配器。

调度与操作协议

所有候选节点先通过 preflight,才能开始任何 stage。每个节点依次执行以下流程;独立节点同时推进,共享写入由锁串行执行。

阶段 并发边界 任务命令必须核验的事实
preflight 按 VPS 并发,全批屏障 当前代码/工具/系统依赖;VPS 与地址;空闲端口;现有机器/节点/组/名称/域名/中转冲突;目标正式组;保留资源基线
stage 面板串行 参数与 manifest 完整匹配;隔离 QA 和新节点确属本任务;创建后回读对象及私有传输文件
dns 全局串行 本次域名实际 DNS 指向;有权限的 API 才能 apply;无 API 则等待
provision 按 VPS 并发 已推送提交、安装包摘要、独立服务/配置归属、证书/SNI、旧服务保持;不能仅按文件存在判定完成
relay 全局串行 独立规则的入口和目标完全匹配;网关侧严格 TLS/SNI 连通;不得占用旧规则
verify 按 VPS 并发 从隔离网关获取真实完整 QA 订阅,用 verify-node.py --run 验证预期出口/国家并核对实际流量上报;报告绑定本请求
publish 面板串行 promote.php --mode=append 先预览后应用;只开放本次新节点到指定已有组并禁用 QA;不能重写套餐、用户组或订阅地址
audit 按 VPS 并发 正式多节点订阅完整、旧节点/网关/套餐/用户关联/订阅地址符合基线、QA 禁用且订阅拒绝/不下发;新节点仍可用

preflight/verify/audit 仅调用 probe,没有 apply。verify 的 probe 会产生隔离测试流量,可以启动/停止本次临时客户端;不能修改正式配置。已经完成 audit 的节点,重跑只做 preflight 与完整 audit,避免重新激活 QA。尚未完成的节点会从第一阶段重新逐项 probe,确认后才越过。

适配器接受 --plan FILE --node FILE --phase PHASE --action probe|apply --state-dir DIR。node 文件包含 request_sha256,它绑定 context 与完整节点参数。标准输出必须只含一条 JSON,字段如下:

{"key":"jp-new-a","phase":"stage","request_sha256":"从node文件原样读取","status":"absent","retry_safe":true}
status 含义与调度行为
ready 当前现场满足本阶段所有要求,可以继续;不能只依据上次成功日志
absent 已权威确认本阶段尚未执行;只有同时明确 retry_safe: true 才能调用 apply
waiting 外部依赖未就绪或需人工处理,本节点停下;不占工作线程反复轮询
unknown 现场无法确认,不写入,不自动重试 apply
conflict 资源占用、参数漂移或归属冲突;阻止该节点,通知其余任务停止继续写入

apply 返回成功后仍必须 probe 确认。退出码非零、输出损坏、身份不符、超时或 Ctrl-C 后,远端结果一律按未知处理;重跑仍从现场读取。apply 命令必须支持安全处理部分完成的现场;不能把“有进程/有目录但未完成”随意返回 absent。stage 的现有对象没有匹配 manifest、或事务结果与 manifest 不一致时应 conflict/unknown,人工核实,不能自动创建第二套对象。

命令适配器的占位符仅支持 plan/node/state_dir/key/phase/action;每个元素作为独立 argv 传递,不解释 shell。操作脚本需要参数时自行读取受保护的 JSON。禁止在 argv/标准输出加入凭据;密码与令牌由 handler 按 CoDocs 私有引用按需读取,SSH 保持经白色机跳转;本地 HTTP 继续使用明确登记的反代。不要为了批量化改变既有访问规则。

查重、锁与断点恢复

计划内拒绝重复的部署 key、展示名、国家编号、VPS ID、实际落地 IP、TLS 域名、中转入口域名+端口与网关 SOCKS 端口。展示名格式与旗帜/国家/两位编号必须匹配。**这些是本地候选查重,已在线资源的查重必须由 preflight/stage 实际查询。**同一物理地址使用不同 VPS 别名也会被拦下。

状态目录是唯一协调入口:SQLite 事务原子预留身份,同目录 fcntl 锁协调进程。VPS/IP/TLS 与中转入口预留跨面板;名称/编号/key 按面板应用划分;SOCKS 按网关划分。相同节点参数可重跑;不同参数不能借用同一批次或占用已有预留。预留不自动释放,不提供强制接管命令。

所有参与聊天/进程必须使用同一台协调机、同一个状态目录。另建账本、复制状态目录、在另一台主机启动独立协调器都不能获得跨机锁保证。对共享面板的非批量手工操作也必须协调暂停,不能指望本地锁约束后台人工修改。每次写入前的远端归属/参数检查仍必要。

中断后保留原计划、账本、远端 manifest 和操作日志。先用 status 查看,再以同一计划、同一状态目录运行相同 run 命令;不要删除账本、改 batch_id 或清除 manifest 来“解除报错”。冲突需先核对原始请求与现场,不能通过修改参数静默接管。未知结果宁可停住;恢复不保证所有断点都能无人干预推进。

SIGINT/SIGTERM 会停止派发后续写入,终止当前本地适配器进程组;这不保证已发出的远端命令停止,也不自动回滚。其他已开始的节点可能已完成步骤。已完成节点保留成果;未完成节点留在其最后安全阶段。需要回退时按执行手册逐字段预览与回退,不做全批删除。

原服务自行重启时的恢复

PID/启动时间漂移应先停止写入,不直接刷新基线。读取服务生命周期、退出原因、重启策略、内核OOM与本任务命令记录,区分本次变更影响和原服务自身故障。确认已恢复时,保留原基线只读副本及SHA256、事故日志、两次稳定采样、443监听归属与当前配置/二进制/unit哈希;没有历史哈希就明确不能证明前后相同。独立助手复核后,只更新本任务进程比对所需字段,保存diff、原因与证据路径;不修改原服务、不删除账本、不改变冻结代码/计划。再次漂移必须重新调查,不能滚动更新绕过保护。2026-10-17 jp03 QUIC sniffer自身panic的实际处理见 STATUS。

多智能体协作流程

用户于 2026-10-16 明确授权:以后执行批量节点接入,默认启动一个子助手,按本节分工执行,不再重复询问是否需要助手。触发入口见项目根 AGENTS.md。这里的“启动”由主协调者使用子助手工具完成;batch.py 本身不调用模型或创建助手。明确要求批量,或同批接入两个及以上节点时触发;仅讨论、修改脚本/文档、单节点接入不自动触发。用户当次明确调整角色或数量时,以最新要求为准。

角色 固定职责 写入与协作边界
主协调者 明确本批范围,统一参数、展示编号与端口,固定计划,持有唯一账本,安排生产写入并汇总结果 只有主协调者启动本批正式调度和共享资源写入;不以助手已派发代替工作完成
核查验收助手 接入前核查候选服务器身份、既有节点/机器、端口、中转、DNS 和归属冲突;独立审查脚本与参数、测试并发/查重/恢复;开放前后核对 QA、正式订阅和旧资源基线 分阶段完成两类职责,返回每节点结论、证据及缺口;不自行分配最终编号、创建节点、改后台或另起调度。真实联网验收的对象、端口和时机由主协调者统一安排

执行顺序:

  1. 主协调者在生产写入前启动/复用一个核查验收助手,给出当前任务范围、必要资料入口、受保护资源、分阶段输出和完成标准。当前批次已存在对应子助手时继续使用,不在每个节点或每次续跑时重新创建。
  2. 助手分阶段核查资源与审查方案;主协调者并行准备参数与调度。助手完成冲突核查和相关隔离测试/审查后,主协调者解决影响写入的缺口,固定同一参数单,再运行全批 preflight。检查发现变更时重新核对受影响项,不把旧回执沿用到不同参数。
  3. 独立节点由脚本按 --jobs 并发,面板/DNS/中转按共享锁执行。正式开放前,核查验收助手核对本节点隔离 QA、严格 TLS、实际出口与上报证据;主协调者确认后执行 publish。助手不得另起同批调度、重复 stage、共用浏览器会话切换账户或绕开账本。
  4. 正式开放后,核查验收助手检查正式订阅、QA 禁用/拒绝、旧资源基线并报告通过或具体缺口。主协调者核实后逐节点报告完成、等待或需用户操作;批次全部完成必须有对应实际验收,不能以子助手结束作为完成证明。
  5. 子助手不可用、并发名额不足或失败时,主协调者明确报告,复用/恢复角色或等待名额;不自动追加更多助手、跳过必需角色或把未知写成通过。暂停和停止指令必须传达给子助手,并停止派发后续写入;已发出的远端操作仍按断点恢复规则回读。

主协调者与一个子助手分别消耗处理其输入和输出所需的 token,总量不是固定倍数。主协调者默认仅传必要背景、资料路径、节点参数与任务边界,避免复制整段历史;每项检查明确归属,汇报用结论、证据位置与缺口,不反复粘贴完整日志。独立验收保留必要的交叉核对,不为节省 token 省略验收。没有固定 token 配额或价格承诺。

该常驻授权仅允许启动当前任务的子助手;不自动新建/分叉侧边栏普通聊天,向已有聊天派消息仍需用户明确授权。节点并发数由脚本控制,助手数保持一个;不会为十个节点启动十个助手。项目规则不扩大实际生产变更、凭据、访问路线或后台自动化授权。

本地验证

本地测试:

python3 -m unittest discover -s tests/node-onboarding -p 'test_batch.py' -v

本次 11 项测试通过,覆盖:plan/status 无副作用,计划内重复与命名约束,全批前检屏障,独立节点重叠执行/面板串行,完整重跑不重复写,模拟写成后回包丢失,SIGTERM 中断后续跑,跨批预留/参数漂移,两进程竞争、错误身份回执、命令摘要变更及配置完整的适配器流程。测试没有验证生产命令的正确性、DNS/中转 API、真实 SSH/网络、现场负载或十节点耗时。生产批次的候选参数、命令 profile 与现场验收须在具体获授权的接入任务中另行核实。

2026-10-17追加面板隔离测试:php vendor/bin/phpunit --no-configuration tests/node-onboarding/PanelChangeIsolatedTest.php,9 tests / 65 assertions通过。真实Eloquent与SQLite内存库,无.env/Redis/队列/网络;覆盖append保护范围、QA禁用、幂等、事务失败/通知失败恢复和冲突拒绝。NodeSync为测试替身,生产通知与用户下发仍需现场验收。

2026-10-17:已在用户授权的三日本节点批次使用同一冻结计划/账本及一个核查验收助手完成生产接入与分阶段恢复;DNS/中转仍由注册后台界面保存后现场probe,无通用服务商API写入。精确版本、五节点订阅/QA/原资源验收及进程基线事故见NODE_ONBOARDING。本轮三节点结果不推导十节点耗时或无人值守能力。