panxiao81 e3ff308772
test / shell (pull_request) Successful in 45s
test / python (pull_request) Successful in 1m3s
test / go (pull_request) Successful in 3m0s
ci: 为 Kata nested kubelet 补充 kmsg 设备
2026-09-21 12:21:33 +00:00
2026-09-16 18:10:02 +00:00
2026-09-18 18:18:10 +00:00
2026-09-20 17:08:51 +00:00
2026-09-16 13:06:05 +00:00
2026-09-21 06:35:06 +00:00
2026-09-21 06:35:06 +00:00
2026-09-21 08:50:18 +00:00

Gitea dynamic runner

为 Gitea Actions 按需创建一次性执行环境。对 workflow 提供两种稳定的 runner 接口:

runs-on: [self-hosted, pod]
runs-on: [self-hosted, vm]

pod 使用动态 Kubernetes Podvm 使用动态 Cloud Hypervisor microVM。每个环境 只执行一个 job,并在 job 结束后连同本地状态一起销毁。完整的设计约束见 docs/design-principles.md

集成期间可将 VM_RUNNER_LABEL=vm-dev,只接取显式使用 runs-on: [self-hosted, vm-dev] 的测试任务;生产 vm job 将保持在 Gitea pending 不会在 backend 修复过程中继续涌入。

目标 Go controller 组件:

  • scheduler:以常驻 Gitea RunnerService 身份直接领取 task,并把完整 assignment 持久化到 JetStream;一个 registration 下按配置启动多个并发 FetchTask goroutine 同时提供仅允许 SPIFFE mTLS 的 RunnerService facade。
  • pod-worker:直接在 homelab Kubernetes 创建一次性 Pod。
  • vm-worker:通过 OpenSandbox Lifecycle API 从 ci-vm Pool 创建 Kata microVM。
  • 三个组件默认在同一个 Go 进程启用。首轮集成期间不允许只启动 worker,因为 facade 的 assignment claim registry 仍是进程内状态;支持安全拆分前进程会明确拒绝该配置。
  • microvm-runner-launch:为每个任务以 direct I/O 转换出 flat qcow2 root disk、创建 NoCloud seed 和 TAP,运行 Cloud Hypervisor,退出后完整清理。
  • guest-runner:在 guest 中领取一次性 runner registration token,注册 ephemeral runner,执行一个 job 后关机。
  • opensandbox-identity:在 sandbox 集群按实际 Pod UID 创建并清理临时 SPIFFE entry;不持有 OpenSandbox API key、Gitea token 或 Bao 凭据。身份与 Pool 契约见 docs/opensandbox-runner.md
  • Pod executor:在 Kubernetes 中创建一次性 privileged PodPod 内的 workflow 使用 host executorDocker、BuildKit 和 kind 等工具由 pipeline 按需 setup。Runner 固定在 支持原生 job hooks 的 3.x 版本,在 workflow 第一步前等待实际任务对应的 SVID。
  • jwt-broker:早期共享 Kubernetes runner 的过渡实验;目标架构不部署它,每个 动态 Pod 或 VM 直接取得自己的 SPIFFE 身份。

Pod 路径由 homelab 集群中的 pod-worker 直接创建 Kubernetes Pod。OpenSandbox 只用于 VM/Kata workload;两个 backend 使用独立 durable consumer 和独立容量池。assignment 根据 runs-on 进入对应池,池满时留在 JetStream pending,不会创建超出容量的 workload;任一 执行层故障不会阻塞另一条部署。长期 RunnerService 协议路线见 docs/runner-protocol-roadmap.md

开发

python -m venv .venv
. .venv/bin/activate
pip install -e '.[test]'
pytest

go test ./...
go vet ./...

Go controller 首次集成配置

controller 默认执行 controller 子命令,runner 镜像执行 executor 子命令。所有长期 credential 都从挂载文件读取,不接受明文环境变量:

  • GITEA_RUNNER_UUID_FILEGITEA_RUNNER_TOKEN_FILEscheduler 的常驻 RunnerService registration;该 credential 不下发给 executor。
  • NATS_PRODUCER_PASSWORD_FILENATS_WORKER_PASSWORD_FILE:分别使用现有最小权限的 ci-producer publish 连接和 ci-worker pull/ACK 连接,controller 不合并权限。
  • RUNNER_FACADE_CAPABILITY_KEY_FILE:至少 32 字节的 controller HMAC key。
  • OPENSANDBOX_API_KEY_FILE:仅启用 vm-worker 时读取。

必要的非 secret 配置包括 POD_EXECUTOR_IMAGE(应使用 digest)、SPIRE_AGENT_IDRUNNER_FACADE_URLRUNNER_FACADE_SPIFFE_IDSPIFFE_ENDPOINT_SOCKET。默认 COMPONENTS=all、Pod 并发 4、VM 并发 1;首次 smoke test 应显式设为 COMPONENTS=scheduler,pod-worker,先验证 Pod 链路,避免同时消耗 VM 容量。

Pod task 的 terminal update 被 Gitea 接受后,controller 会在 Pod 上持久写入 ci.ddupan.top/terminal=true label。生命周期 reconciler 只清理同时带该 label 且已经 进入 SucceededFailed phase 的 Pod 及其同名 ClusterStaticEntrycontroller 重启不影响清理恢复,上报终态前失败的 Pod 也不会被误删。

安全边界

  • OpenSandbox API key、webhook secret 和 Gitea registration token 只从文件读取。
  • registration token 不写入 seed imageworker 通过单次 nonce endpoint 交给 guest。
  • guest 启动时从仅监听 microVM bridge 的 worker endpoint 获取固定版本 Runner 和配置 资产;基础镜像无需为 Runner 发布而重做。
  • runner-vm-bootstrap.yaml 暂时只验证 VM 调度和生命周期,不提供 SPIFFE identityVM agent attestation 完成前不得将它当作身份链路验证结果。
  • guest runner 使用 --ephemeral,每台 VM 只执行一个 job。
  • launcher 只接受 UUID instance ID 和 URL-safe nonce,所有临时文件都位于独立目录。
  • base image 不得包含 runner identity、registration token、SSH 密码或 host key。
  • LXC 内运行 Cloud Hypervisor 必须为 guest memory 启用 shared=on。默认的 private memfd 映射会在 guest 写入后同时产生 shmem 与 anonymous CoW charge,使 LXC cgroup 对 guest RAM 接近双倍计费。

homelab 的 Kubernetes、OpenBao、LXC、bridge 和容量配置保留在 panxiao81/homelab-infra

按需启动 Cloud Hypervisor microVM 的 Gitea Actions runner autoscaler

S
Description
按需创建 Pod 与 Cloud Hypervisor microVM 的 Gitea Actions 动态 Runner
Readme
564 KiB
Languages
Go 64.5%
Python 30.1%
Shell 4.3%
Dockerfile 1.1%