Files

4.3 KiB
Raw Permalink Blame History

动态 Runner 设计原则

对 workflow 的接口

Runner 只向 workflow 暴露两个执行环境:

runs-on: [self-hosted, pod]
runs-on: [self-hosted, vm]
  • self-hosted 是固定前缀。
  • pod 表示一次性 Kubernetes Pod,承担常规 CI、镜像构建和 kind 等任务。
  • vm 表示一次性 microVM,承担需要独立内核、KVM、systemd 或更强隔离的任务。

执行后端是基础设施选择,不是权限角色。workflow 不需要额外声明由 controller 维护的 role 或权限 label。

一个 job,一个环境

Controller 通过 Gitea RunnerService 原子领取具体 task,再把版本化 assignment 交给 一个一次性 Pod 或 VM。executor 直接执行已经领取的 task,不再注册临时 runner 去 二次竞争任务;任务结束并回报 Gitea 后删除计算环境及其全部本地状态。

job_id 仅用于消息去重、状态追踪、实例关联和失败清理,不进入 workload 身份,也 不参与资源授权。

assignment ID 只用于消息去重、状态追踪、实例关联和失败清理,不进入 workload 身份, 也不参与资源授权。worker 通过 assignment ID 从 Kubernetes labels 或 OpenSandbox metadata 恢复 executorJetStream 不保存 executor 生命周期状态。

Pod executor 创建后,controller 使用实际 Pod UID 创建幂等 ClusterStaticEntry,将 SPIFFE ID绑定到该 Pod 的 workload selector。executor 必须等目标 SVID 可用后才执行 workflow 的第一步。

环境只提供运行边界

基础镜像只提供启动 runner 和执行 workflow 所需的最小环境。Docker、BuildKit、 kind 等工具由 pipeline 按需安装和启动,而不是由 controller 预制成常驻服务。

例如 Pod job 可以在 Pod 内启动仅供本次任务使用的 Docker daemon。该 daemon 及其 镜像、容器和缓存属于当前 job 的临时状态,随 Pod 一起销毁。Docker 创建的容器不是 独立的身份边界;需要访问凭据的操作由 Pod 中的 workflow 进程完成,并通过环境变量 或标准输入把短期凭据交给具体工具。

Workload 身份

动态 Pod 和 VM 都直接拥有自己的 SPIFFE 身份,不继承常驻 runner 的共享身份:

  • Pod 通过 Kubernetes workload attestation 取得身份。
  • VM 通过 VM 内的 SPIRE Agent 取得身份。

SPIFFE ID 由具有业务意义且稳定的 workflow 上下文派生:

spiffe://ddupan.top/ci/<owner>/<repository>/<job-key>

同一种任务在不同运行中使用相同的逻辑 SPIFFE ID;每次运行取得独立、短期的 SVID。 Pod 与 VM 是可替换的执行实现,因此默认不写入 SPIFFE ID。

job key 必须满足 [A-Za-z_][A-Za-z0-9_-]*,展示名称 name 不参与身份计算。同一 仓库内需要不同权限的任务应使用不同的 job key;workflow 文件只是编排载体,不进入 权限身份。

Self-service 与授权边界

新增 workflow 或 job 时,controller 自动为它派生身份,不维护第二份任务或角色 allowlist。能够修改仓库 CI 的主体本来就能修改该仓库已有任务,因此 controller 的 重复审批不能形成额外的安全边界,只会破坏 self-service。

身份不等于权限。新任务可以立即取得自己的 SPIFFE ID,但默认不会因此获得 Zot、 OpenBao 或其他资源的特殊权限。资源所有者在资源端按照有意义的 job 身份 配置授权策略。

非目标设计

目标架构不依赖以下机制:

  • 多个 job 共享的常驻 Docker daemon。
  • 常驻 runner Pod 的共享 SPIFFE 身份。
  • 为嵌套 CI 容器转发共享身份的 JWT broker。
  • 将 Gitea 数字 job ID 编入 SPIFFE ID。
  • controller 维护的仓库任务权限 allowlist。

仓库中的 jwt-broker 是早期方案的实验实现,在 Pod/VM 动态执行环境完成迁移后不应 部署。

实现依赖原则

基础设施协议优先使用上游维护的成熟客户端,不在 controller 内重复实现认证、连接、 资源编码或错误语义。Kubernetes 使用 client-goNATS JetStream 使用 nats.go Gitea RunnerService 使用 actionslibOpenSandbox Lifecycle API 使用官方 Go SDK SPIFFE Workload API 与 mTLS 使用 go-spiffe。自定义代码只保留领域模型、reconcile 规则及上游客户端未覆盖的最小适配层。