Files
Nexus/docs/project/script-execution.md
T
2026-07-08 22:31:31 +08:00

8.9 KiB
Raw Blame History

脚本批量执行平台 — 技术说明(SSOT)

最后更新: 2026-05-22
状态: 已实现,待本地 / 外网 E2E 验证

1. 目标与约束

目标 实现方式
2000+ 台批量执行 内置分批(默认 50 台/批),批内并发 10
长任务不阻塞 HTTP nohup + 子机结束 curl 主站回调
短任务执行中可停止 Agent wait=falsepid/exec/kill
减轻 MySQL 压力 执行态只写 RedisMySQL 60s 分批刷盘
全链路可审计 audit_logs 即时写入;results._meta.events 时间线
不用 WS 换票 / 异步 exec API 同步 POST /api/scripts/exec + 页顶状态条轮询

不可改 / 环境项.env):

  • NEXUS_API_BASE_URL — 长任务子机回调主站地址(必填,否则长任务 400)
  • NEXUS_SCRIPT_EXEC_BATCH_SIZE(默认 50)、NEXUS_SCRIPT_EXEC_CONCURRENCY(默认 10

代码内常量server/infrastructure/redis/script_execution_store.py,不写 .env):

常量 含义
REDIS_TTL_SECONDS 7200 热数据最长 2h
FLUSH_INTERVAL_SECONDS 60 Redis→MySQL 刷盘周期
FLUSH_BATCH_LIMIT 50 每轮最多刷 50 条执行记录
STUCK_NO_PROGRESS_SECONDS 3600 卡住检测阈值(1h

2. 架构与数据流

flowchart LR
  UI[scripts.html / script-executions.html]
  API[POST /api/scripts/exec]
  SVC[ScriptService]
  Redis[(Redis script_exec:id)]
  Flush[script_execution_flush 60s]
  MySQL[(script_executions)]
  Agent[子机 Agent :8800]
  CB[POST /api/agent/script-callback]

  UI --> API --> SVC
  SVC --> Redis
  SVC --> Agent
  Agent -->|长任务结束 curl| CB --> Redis
  Flush --> MySQL
  UI -->|GET executions/id| Redis
  UI -.->|历史列表可能滞后| MySQL

2.1 Redis 热数据

  • Key: script_exec:{execution_id} — JSON 全量快照(status、results、events、operator…)
  • 队列: script_exec:flush_queue — 待刷 MySQL 的 id 集合
  • 长任务回调: script_job:{job_id} — 一次性 secretTTL 7 天,见 script_jobs.py

2.2 MySQL 冷数据

  • 表: script_executions
  • results JSON{ "<server_id>": { ... }, "_meta": { "events": [...] } }
  • 刷盘后从 flush 队列移除;读接口优先 Redis,无 Redis 再读 MySQL

2.3 进度字段

  • API 字段 progress: "done/total"pending 计为未完成)
  • 实现: execution_progress_summary() in script_execution_store.py

3. 执行模式

3.1 短任务(默认)

  1. 主站 POST 子机 Agent /exec + wait: false → 得 pid立即 patch_live 写入 Redisphase: pending
  2. 主站 POST /exec/wait 等待结束
  3. 用户点「停止」→ 主站 POST /exec/kill(需新版 Agent
  4. 未升级 Agent:回退同步 /exec wait=true无法中途停止

3.2 长任务(long_task: true

  1. 命令存库前缀 [long_task]
  2. script_jobs.build_long_task_command()nohup + 日志 /var/log/nexus-job/{job_id}.log + 结束 curl 主站
  3. 点火成功 → phase: pending + job_id + started_at
  4. 子机 POST /api/agent/script-callback → 更新 results + WebSocket script_job_done
  5. 停止:kill ${LOG}.pid + 撤销 Redis job secret

4. HTTP APIJWT,除回调)

方法 路径 说明
GET /api/scripts/exec-config batch_size, concurrency
POST /api/scripts/exec server_ids, command, timeout, long_task, credential_id?
GET /api/scripts/executions 分页历史 limit, offset, status
GET /api/scripts/executions/{id} ?fetch_logs=true 拉子机 tail 日志
POST /api/scripts/executions/{id}/stop 停止 pending
POST /api/scripts/executions/{id}/retry 失败/未完成重试;新建 execution_id,响应含 parent_execution_id
POST /api/scripts/executions/{id}/mark-stuck 人工标记卡住
POST /api/agent/script-callback job_id, secret, server_id, exit_code(无 JWTRedis 限速)

4.1 安全与执行面

风险 现状 缓解
子机 subprocess_shell / 长任务 bash -c 运维必需能力 仅 API Key 可调 /exec;主站 check_dangerous_command 拒绝危险模式;Agent 每次 exec 打 WARNING 审计日志
回调 HTTP 明文 生产配置错误 master_callback_url() 非 localhost 强制 https://
回调刷接口 无 JWT script_callback_rate:每 job 10/min、每 IP 60/min
重试语义 每次 retry 新建记录 API 返回 parent_execution_id 关联原单;历史页按 id 追踪

子机应网络隔离:仅中心机 IP 可访问 Agent :8800

5. 子机 Agent 接口

文件: web/agent/agent.py

方法 路径 说明
POST /exec wait 默认 truefalse 返回 pid
POST /exec/wait 等待 pid
POST /exec/kill 终止 pid

部署:各子机更新 Agent 后重启;否则短任务 stop 不可用。

6. 后台任务

  • 模块: server/background/script_execution_flush.py
  • 注册: server/main.pyscript_execution_flush_loop()primary worker
  • 每 60s
    1. detect_stuck_executions() — 单台 pending 超 1h → server_auto_stuck;整单无更新 1h → auto_stuck
    2. flush_pending_batch() — 最多 50 条写入 MySQL

7. 审计(audit_logs.action

action 触发场景
execute_started 创建执行记录
execute_command 记录实际下发命令摘要
stop_execution 用户停止
retry_execution 用户重试
mark_stuck 用户标记卡住
script_job_callback 长任务子机回调
auto_stuck 整单 1h 无 Redis 更新
server_auto_stuck 单台 pending 超 1h

前端筛选: web/app/audit.html 已增加上述选项。

8. 前端页面

页面 路径 功能
脚本库 web/app/scripts.html 执行弹窗、分批、长任务勾选、页顶「批量执行状态」
执行历史 web/app/script-executions.html 列表、进度、停止/重试/卡住、详情+日志、10s 轮询
侧栏 web/app/layout.js 菜单项「执行历史」

页顶状态条按钮:刷新状态、状态+日志、停止、重试、标记卡住。

9. 涉及文件清单

后端

文件 职责
server/application/services/script_service.py 分批执行、Agent 调用、stop/retry/list
server/application/services/script_jobs.py 长任务 nohup + job Redis
server/application/services/script_job_callback.py 回调合并结果
server/infrastructure/redis/script_execution_store.py Redis 热数据、刷盘队列、卡住检测、progress
server/background/script_execution_flush.py 60s 循环
server/infrastructure/database/script_repo.py list_recent, update_status
server/api/scripts.py REST 路由
server/api/agent.py script-callback
server/config.py SCRIPT_EXEC_BATCH_SIZE, SCRIPT_EXEC_CONCURRENCY
server/main.py 注册 flush 后台任务

前端 / Agent

文件 职责
web/app/scripts.html 执行 UI + 状态条
web/app/script-executions.html 历史页
web/app/layout.js 侧栏
web/app/audit.html 审计筛选
web/agent/agent.py exec / wait / kill

10. 已知限制

  1. MySQL 视图比 Redis 最多晚 60s(直接查库时);页面轮询读 Redis 无影响。
  2. 短任务 stop 依赖子机 Agent 新版本。
  3. 自动卡住:单台按 started_at;整单按 updated_at(非单台日志活跃度)。
  4. 危险命令仍由 check_dangerous_command 拦截。
  5. 长任务依赖 NEXUS_API_BASE_URL 对子机可达,且生产须为 HTTPS(本地可用 http://localhost)。
  6. 重试会创建新的 script_executions.id;原单结果保留,通过 parent_execution_id 关联。

11. 测试清单(待执行)

  • T-SE1: 选 3 台短任务,页顶进度 3/3,审计有 execute_started
  • T-SE2: 选 60 台,确认分 2 批、合并一条状态卡
  • T-SE3: 长任务 + 配置 NEXUS_API_BASE_URL,回调后状态变完成、WS 通知
  • T-SE4: 长任务执行中「停止」,子机进程结束
  • T-SE5: 新版 Agent 短任务执行中「停止」
  • T-SE6: 执行历史页停止/重试/状态+日志
  • T-SE7: 等待或调低 STUCK_NO_PROGRESS_SECONDS 验证 server_auto_stuck / auto_stuck
  • T-SE8: Redis 有 key 时 MySQL 在 60s 内出现对应行

12. 相关 Changelog

文档 内容
2026-05-22-script-execution-platform.md 本次交付总览与文件变更索引
2026-05-22-script-exec-batching.md 内置分批与 exec-config
2026-05-22-script-job-callback.md 长任务回调与 Redis 刷盘摘要