切换主题
任务、队列与人工卡点
本文档介绍 CICD 自动打包部署平台的任务触发方式、执行范围、状态机、任务队列与派发、实时日志、任务详情页操作,以及人工卡点,并说明任务与日志的保留策略。
任务的执行内容(构建 / 部署)见 构建与部署;触发所需的项目配置见 项目管理;自动触发的 Webhook 见 Webhook 与通知。
触发方式
任务的触发方式(triggerType)共五种:
| 取值 | 界面展示 | 说明 |
|---|---|---|
manual | 手动 | 在「运行任务」页手动提交 |
api | 接口 | 通过 API 触发 |
retry | 重试 | 由已有任务重跑产生 |
schedule | 定时 | 定时触发 |
webhook | Webhook | Git 平台推送等 Webhook 触发 |
所有触发方式都走统一入队(POST /api/projects/:id/run 或内部同源调用):校验项目与候选执行机 → 合成 RunPlan 快照(入队即固化)→ 候选机中有在线且未满载者则直接派发,否则置 queued 并返回 {taskId, queued, queuePosition}。
- 优先级默认来源:手动触发默认
priority=10(交互更急),其余触发默认0;触发时可用priority覆盖。 - 重试:从任务列表或详情页对终态任务「重跑」,会按项目当前配置重新合成 RunPlan 并新建一个任务(非原地重试),
triggerType=retry;支持指定fromStage从某阶段开始重跑。 - 置顶:仅对
queued任务有效,把优先级置为全局队列最大值 + 1。
执行范围与 fromStage
触发时可指定执行范围,范围与「四阶段模型」(打包 build / 上传 upload / 部署 deploy / 启动 start)对应:
- 「运行任务」页顶部可按分组筛选项目(
GET /api/projects?groupId=),只列出当前用户有operate权限的项目;筛选后原选中项目不在结果中时自动切换到第一个。 - 「运行任务」页提供「打包」「部署」「启动」三个勾选项,阶段按顺序执行且不可跳跃:勾选后面的会自动勾上前面,取消前面的会同时取消后面。
- 「分支 / Tag」为下拉选择:选中项目时自动拉取远端分支与 Tag(
GET /api/projects/:id/git/refs,实时git ls-remote),可点「刷新分支 / Tag」重取;留空则使用项目默认分支 / Tag,也支持手工输入列表外的值。 upload不由用户单独勾选:项目启用部署时上传与部署同步;项目关闭部署时后续阶段禁用(Docker 交付方式为「推送镜像仓库」时仍会推送镜像)。- 「从阶段开始」(
fromStage):指定后,之前的阶段标记为skipped并沿用上次结果,适用于失败重试。任务列表与详情页对终态任务提供「重跑」与「从『当前阶段』重跑」。
任务类型即以上阶段范围,PlanOptions 记录 build / upload / deploy / start / fromStage。
任务状态机
任务状态(task.status)共七个,取值与含义如下:
| 状态 | 含义 | 是否已下发指令 |
|---|---|---|
queued | 排队中:等待执行机(并发已满 / 离线)或同项目串行 | 否 |
pending | 已派发:已选中执行机、指令已落 agent_command,等待执行端长轮询领取 | 是 |
running | 执行中:执行端已 ack | 是 |
waiting | 等待人工确认:上传完成、部署开始前暂停 | 是(run_task 仍在执行中) |
success | 成功(终态) | — |
failed | 失败(终态) | — |
canceled | 已取消(终态) | — |
waiting属于在途状态:与pending/running一样占用「同项目串行」与执行机并发名额,也纳入离线兜底与取消的处理范围。
任务详情页还会展示每个阶段的步骤状态:pending / running / success / failed / skipped / warning。
任务队列
为什么需要队列
同一项目的工作目录在执行机上是独占资源({workspaceRoot}/{项目ID}),并发执行会互相覆盖产物;执行机 capacity 也限制了单机可同时执行的任务数。因此「已有任务在执行时再次触发」不再直接拒绝,而是入队,资源释放后自动继续执行。
排队原因
task.queue_reason 记录当前的排队原因:
| 取值 | 含义 |
|---|---|
agent_busy | 执行机并发已满 |
project_running | 同项目已有任务在跑(工作目录独占) |
agent_offline | 执行机离线 |
执行机离线不失败,任务留在队列并记 agent_offline(其他候选机在线时仍可被取走),心跳恢复后自动派发。
派发流程
队列以 MySQL 为唯一依据(task.status='queued'),管理端派发器常驻运行:默认每 1 秒一轮,单轮最多处理 50 条,按 priority DESC, queue_seq ASC 取候选任务。每轮对单个任务:
- 复核项目:项目未禁用,且该项目无
pending/running任务(否则记project_running); - 求候选机:项目引用资源关联执行机的交集(未引用任何资源时为全部执行机),过滤已禁用;为空则任务直接失败并提示「没有匹配的执行机」;
- 同项目串行:项目级互斥固定为 1(不提供并行度配置,避免工作目录互相覆盖);
- 选机:候选机中选在线且
running + pending < capacity的机器,按「最闲优先」(已派发任务数最少,其次 ID 最小);无可用则记agent_busy(均满载)或agent_offline(无在线机); - 抢占并定机:原子地把
status='queued'改为pending并写入选中的执行机(影响行数为 1 才算抢到),避免多轮重复派发; - 下发指令:写
run_task指令,按选中执行机刷新 Git 私钥路径与known_hosts;执行端ack后pending → running。
队列视图(/tasks/queue)展示排队中的任务与执行机并发占用(used / capacity)、位次、优先级、排队原因,并提供置顶 / 调整优先级 / 取消操作。概览与队列列表使用同一套数据权限过滤,前端顶部「排队中」直接取列表条数,保证「数字 = 列表条数」。
并发容量与队列上限
| 配置项 | 位置 | 默认 | 说明 |
|---|---|---|---|
agent.capacity | 执行端 agent/config.yaml | 2 | 单机最大并发任务数;<= 0 视为 1 |
agent.dispatchIntervalSeconds | 管理端 config.yaml | 1 | 队列派发扫描间隔 |
agent.dispatchBatchSize | 管理端 config.yaml | 50 | 单轮最多派发的任务数 |
agent.queueLimit | 管理端 config.yaml | 50 | 全局排队上限;排队数达到上限时新触发直接失败并提示「队列已满」,避免无界堆积 |
队列操作
| 操作 | 接口 | 权限 | 行为 |
|---|---|---|---|
| 置顶 | POST /api/tasks/:id/promote | manage | priority = 全局队列最大值 + 1,全局队列内优先调度 |
| 调整优先级 | POST /api/tasks/:id/queue | manage | {priority} 直接改优先级(数值越大越先调度) |
| 取消 | POST /api/tasks/:id/cancel | manage | queued 直接置 canceled(不下发指令);pending / running / waiting 走 cancel_task 指令 |
| 删除 | DELETE /api/tasks/:id | manage | 仅终态可删,含日志与产物;queued 需先取消 |
| 查询 | GET /api/tasks?status=queued | view | 按 priority DESC, queue_seq ASC 返回并附 queuePosition |
重启恢复
队列只依赖数据库,管理端重启不丢队列:
queued保持,派发器启动后继续派发;pending(未 ack)指令仍在,执行端重连后正常领取;指令若已expired则回到queued重新派发;running由执行端注册时上报的在跑任务对账:不在列表中的置failed(原因「执行端重启中断」)。
实时日志
任务日志通过 SSE 实时推送,接口 GET /api/tasks/:id/stream:
- 前端用
fetch流式读取(EventSource无法携带Authorization头),携带fromSeq做断点续传:从当前已加载的最后一条日志序号 + 1 开始。 - 服务端事件类型:
event: log(日志分片,JSON)、event: state(任务状态 / 进度变化)、event: done(任务结束,前端据此刷新任务与产物);另有: ping心跳(约 15 秒)。 - 服务端通过约 500ms 的轮询批量取新日志,单批最多 500 条。
- 进入详情页时先调
GET /api/tasks/:id拿到任务、最近日志(尾部 200 条)与产物列表,再由此接续 SSE。 - 任务内日志
seq是一条连续递增序列:执行机自增,节点日志序号由管理端接在执行机当前序号之后分配(logSeqBase),执行机收到中转结果后再抬升自身计数。因此按seq升序即实际发生顺序,fromSeq续传不会漏推日志。 - 日志值中
masked=true的敏感变量在落库前已替换为******。 - 上传 / 拉取进度也会写入日志:执行机上传部署包时逐条目输出「正在上传「app.jar」(12.34 MB / 17.22 MB,71%,1.18 MB/s)」(约 3 秒节流,末尾必有一条「上传完成」);node 模式下由部署节点输出对应的「正在拉取…」进度,长传 / 大包期间日志不会一片空白。
- 详情页日志区支持「清屏」「重连」「放大」(浮层铺满视口查看长日志),并显示连接状态。
- 前端只保留最近 5000 行(防止 DOM 膨胀),更早的历史通过日志区顶部的「加载更早日志」按钮按
GET /api/tasks/:id/logs?beforeSeq=&limit=向前翻页补齐;仅在用户停留在底部时才自动滚动,向上翻阅历史时不会被新日志打断。
任务详情页
任务详情页路径 /tasks/:id,展示:
- 概要:项目、执行机(未派发时显示「待派发」)、部署节点(node 链路时)、触发方式与触发人、当前阶段、进度、耗时、开始 / 结束 / 创建时间、排队信息(位次、原因、优先级)、产物、镜像(Docker 项目)、说明;
- 步骤明细:各阶段与步骤的名称、状态、开始 / 结束时间、耗时、传输进度与说明;
- 产物:逐条列出归档产物(名称、来源步骤、类型与文件数、体积)并提供「下载」(
dir类型下载为 tar.gz);需该项目view权限;下载走浏览器原生下载(响应带Content-Length,有进度、不占前端内存):前端先用登录态调GET /api/tasks/:id/artifacts/:seq/ticket换取 2 分钟有效的短期票据,再由浏览器访问GET /api/tasks/:id/artifacts/:seq/download?ticket=…; - 实时日志:见上文。
详情页操作与权限(view < operate < manage):
| 操作 | 显示条件 | 权限 | 说明 |
|---|---|---|---|
| 放行 / 驳回 | 状态为 waiting | operate | 人工卡点处理,见下节 |
| 置顶 | 状态为 queued | manage | 同队列置顶 |
| 取消 | 非终态 | manage | 见队列操作 |
| 重跑 | 终态 | manage | 按项目当前配置重跑,新建任务 |
| 从「当前阶段」重跑 | 终态且任务有阶段信息 | manage | 以 fromStage 从该阶段开始 |
| 删除 | 终态 | manage | 删除任务(含日志) |
任务列表页(/tasks)支持按关键字、项目、分组、执行机、状态、触发方式与时间范围筛选,行内提供详情、放行 / 驳回、置顶、取消、重跑、删除等操作。
人工卡点
人工卡点用于「打包与上传完成后,人工确认再部署」的关键项目——deploy 阶段的动作不可逆(停旧服务、替换文件),卡点固定放在 upload 与 deploy 之间,不放行就绝不触碰目标机。
开启方式
在项目配置「部署」分区的「人工卡点」处开启并设置超时:
| 字段 | 默认 | 说明 |
|---|---|---|
enabled | false | 是否启用;关闭时执行链路与行为与之前完全一致 |
timeoutMinutes | 0 | 等待超时(分钟),0 或留空取默认 24 小时 |
- 合成 RunPlan 时把配置固化为
PlanApproval(超时秒随计划固化),因此排队期间改项目配置不影响已入队任务。 - 仅当本次会执行部署阶段时才可能生效(只打包不部署的任务不卡点)。
状态流转
| 动作 | 触发方 | 任务状态 | 通知 |
|---|---|---|---|
| 进入等待 | 执行端上报 waiting(stage=deploy,进度停在 45) | running → waiting | 首次进入时发一次「等待人工确认」 |
| 放行 | POST /api/tasks/:id/approve(需 operate) | waiting → running | 不发(任务继续,终态时按原通知) |
| 驳回 | POST /api/tasks/:id/reject(需 operate,可填原因) | waiting → failed | 按「等待人工确认」事件通知(含原因) |
| 超时 | 管理端巡检 | waiting → failed(原因「等待人工确认超时」) | 按「等待人工确认」事件通知 |
| 取消 | POST /api/tasks/:id/cancel | waiting → canceled | 沿用取消逻辑 |
| 执行机离线 / 重启中断 | 巡检兜底 | waiting → failed | 沿用离线兜底通知 |
- 放行 / 驳回需项目
operate权限(接口走数据权限校验),无权限用户按钮禁用、接口返回 403;两个接口都写审计(task.approve/task.reject,含操作人与原因)。 - 双保险但终态唯一:管理端巡检(按
waiting_at + 超时判定)是终态与通知的唯一权威;执行端本地的超时与收到驳回指令后只做本地退出(不上报终态),避免与管理端重复通知或状态互相覆盖。 - 通知事件为
task_waiting(「等待人工确认」),复用项目所属分组的通知渠道;进入等待、驳回、超时三处都按该事件通知,不再触发任务完成通知,避免同一动作两条通知。事件配置见 Webhook 与通知。
任务与日志保留策略
| 配置项 | 默认 | 说明 |
|---|---|---|
agent.taskRetentionDays | 30 | 任务保留天数,超期由巡检清理(0 表示不清理) |
agent.logRetentionDays | 0 | 日志分片额外保留天数(兜底清理,0 表示不清理) |
- 管理端巡检按保留天数清理过期任务与日志;删除任务时其日志与归档产物记录随事务一并删除。
- 构建产物归档另有独立的保留与容量配置(
artifact.retentionDays/artifact.maxTaskBytes/artifact.maxTotalBytes),详见 构建与部署 · 构建产物归档与保留。 - Webhook 投递记录、通知投递等分别按其自身配置保留,见 Webhook 与通知。