Files
gitea-dynamic-runner/docs/runner-protocol-roadmap.md
T

4.7 KiB
Raw Blame History

Gitea Runner 协议调度器路线

目标

长期形态不依赖 workflow_job webhook 发现工作。controller 本身作为 Gitea Runner 协议客户端注册,并声明 self-hosted、pod 和 vm labels;它只在后端存在可用容量 时领取 task,然后将该 task 交给一个一次性 Pod 或 microVM 执行。

Gitea RunnerService
        │ Register / Declare / FetchTask
        ▼
dynamic-runner scheduler
        │ 已领取的 task + lease
        ├── Pod executor
        └── microVM executor
                │ logs / state / result
                └──────────────────────► Gitea

controller 使用单一 Go 二进制;默认在同一进程启用 scheduler、pod-worker 和 vm-worker,也可通过 --components 只启用其中一部分。组件是独立应用服务边界, 共享进程不意味着共享后端状态或把 assignment 降级为内存 channel。

这与“收到 webhook 后临时注册另一个 act_runner”不同。FetchTask 已经完成任务分配, 不能再期待 Gitea 把同一个 task 分配给随后启动的 runner。协议调度器必须让 executor 执行已经领取的 task,并继续完成日志、状态、心跳、取消和最终结果上报。

设计约束

  • 对 workflow 的接口保持 [self-hosted, pod] 和 [self-hosted, vm] 不变。
  • scheduler 在没有对应 backend 容量时不领取 task,避免本地形成不可控积压。
  • 每个 executor 只执行一个 task,完成后销毁。
  • SPIFFE 身份从实际领取的 task 的 repository 和 workflow job key 派生,不需要 queued 与 in-progress webhook 的二阶段关联。
  • 身份中的 task 段使用 workflow job key,而不是可带空格的展示名称;job key 必须满足 [A-Za-z_][A-Za-z0-9_-]*。slug + hash 只保留为旧名称的显式迁移后备方案。
  • scheduler 只做确定性的身份派生与 executor 绑定,不维护业务授权 policy;Zot、 OpenBao 等资源服务继续是唯一授权决策点。
  • scheduler 的 runner registration credential 不进入 executor;executor 只得到执行 当前 task 所需的短期 lease/capability。
  • JetStream 只持久化和投递 assignment,不保存 executor 生命周期状态。Pod labels/annotations 与 OpenSandbox metadata 是后端运行状态的权威来源,Gitea 是 task 终态的权威来源。
  • assignment 使用版本化 envelope 保存完整 Gitea protobuf task,并从 workflow runs-on 严格选择 pod 或 vm subject;消费者解码后重新派生 backend 与身份,拒绝被篡改的冗余字段。
  • JetStream 的 message ID 等于稳定 assignment ID gitea-task-<task-id>,仅用于发布去重, 不承担 executor 生命周期记录。
  • worker 按稳定 assignment ID reconcile 后端资源,进程内只保留并发控制等可丢弃状态; 不新增数据库,也不依赖内存中的 runner-to-executor 映射。
  • VM worker 使用 OpenSandbox 官方 Go SDK,并把 assignment ID、repository、job key 和 SPIFFE ID写入 sandbox metadata;通过 extensions.poolRef=ci-vm 使用既有 Kata Pool。
  • 结果处理顺序固定为回报 Gitea、清理后端、ACK assignment;重投时先查询 Gitea 终态, 从而关闭后端已删除但消息尚未 ACK 的崩溃窗口。
  • pod 与 vm 使用独立 durable consumer 和并发上限。worker 持有消息期间持续 reconcile 后端并发送 InProgress;只有完整完成才 DoubleAck,进程退出则保留未确认消息供 其他实例恢复,临时后端错误使用延迟 NAK。
  • Pod 与 VM 共享 task/executor 协议,只有环境创建和销毁实现不同。

实现顺序

  1. 固定当前 Gitea 版本所使用的 RunnerService protobuf 与 act_runner 版本,记录兼容 范围并建立协议契约测试。
  2. 实现只注册、Declare labels 和容量感知 FetchTask 的 scheduler spike,暂不执行 task。
  3. 从 act_runner 提取或复用 task 执行与日志上报能力,定义 scheduler 到 executor 的 单任务协议。
  4. 首先接入 Pod executor,验证成功、失败、取消、超时和 scheduler 重启。
  5. 接入 microVM executor,并复用同一 task 协议和身份派生逻辑。
  6. 双轨运行并验证后,移除 webhook receiver、临时 runner 注册和 identity binding subject。

Bootstrap 实现的退出条件

只有同时满足以下条件才能删除 webhook 路径:

  • scheduler 能通过 RunnerService 稳定领取并执行 Pod/VM task;
  • Gitea UI 中的实时日志、取消、超时和结论与官方 runner 行为一致;
  • scheduler 重启不会丢失已领取 task,也不会重复执行;
  • SPIFFE 身份只来自实际领取 task;
  • 同一套 workflow 无需修改 runs-on 即可从 bootstrap 迁移。