mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 00:17:47 +08:00
feat(ql3): gate cluster releases through published catalogs
This commit is contained in:
@@ -6,10 +6,35 @@
|
||||
- 目标版本:QingLong 3.x
|
||||
- 作者:QingLong Maintainers
|
||||
- 创建日期:2026-07-17
|
||||
- 最后更新:2026-08-17
|
||||
- 最后更新:2026-08-18
|
||||
- 讨论范围:架构与演进路线,不包含最终 UI 视觉方案
|
||||
|
||||
最新增量证据(2026-08-17):
|
||||
最新增量证据(2026-08-18):
|
||||
|
||||
- D-344/ADR-0436(已接受;首份真实公开 catalog evidence 待实际 release tag):镜像发布流水线不再停在 durable catalog 与
|
||||
receipt。`release-set` 成功后新增独立只读 `release-catalog-deployment-live`,仅在 `cluster|all` scope 运行;它不继承 publisher
|
||||
文件或登录态,而是从公开 discovery ref 重新解析稳定 digest,以 exact Cosign workflow identity 和 GitHub source tag/revision
|
||||
provenance 验证 immutable catalog,下载并离线重审 exact-three-file consumption bundle,再把同一次 audit 得到的 release-set 对象
|
||||
交给 deployment-lock materializer。生成的 Kubernetes v2 lock 绑定真实 release-set、catalog manifest 与 consumption report digest,
|
||||
随后在 digest-reviewed 三节点 K3s 上执行 7-resource、四角色零副本 install、Head commit、UID/resourceVersion 围栏 ConfigMap 退役与两个
|
||||
receipt audit。consumer 只有 `contents|packages|attestations:read`,无 Docker login、package/tag/attestation write 或签名 authority;
|
||||
regctl/Cosign 使用公开读取,package visibility 错误会阻断发布。配置必须一次给齐 bundle/source revision/ref/scope/owner/repository 六项,
|
||||
缺一项或 Local scope 都在 K3s 启动前失败关闭;普通 PR live 保留显式 `synthetic_live_fixture`,不混淆为公开发布证据。新增工作只存在于
|
||||
Cluster release runner,没有新增 package、生产依赖、controller、CRD、RBAC、Pod、数据库、migration、listener、timer 或常驻进程;
|
||||
Local/Edge/Standalone 发布、制品与稳态资源保持不变。定向发布/部署契约 109/109;完整 backend 1,344 项为 1,342 pass/
|
||||
2 条件 skip/0 fail,18-package clean build/test 退出 0。14 项有效架构/发布/部署审计 compatible,package boundary 仍为 18 packages、
|
||||
`singleSourcePackages=[]`、`shallowSourcePackages=[]`;14 档 Local artifact 全部 compatible 且字节数与 D-343 完全一致。catalog-bound
|
||||
三节点 K3s live 在 linux/arm64 上得到 3 个 Ready node,以一份真实执行六步 ceremony、可完整离线重审的 fixture bundle 物化同一
|
||||
release-set;Head generation `1→2`、inventory `7→6`,install/retirement receipt audit、UID/resourceVersion DELETE preconditions、
|
||||
Unix socket proxy、server-side dry-run/apply、目标 absent 与清理全部通过。release-set/catalog manifest/consumption report digest 分别为
|
||||
`sha256:e407cddb6348f96a8fef0822589c73415912be7397205669f2072d70c5bda035`、
|
||||
`sha256:74abea141364570ac803bdc9dd6da1dcbe10ea694e6b7e0f3f7a56a2f84ba55a`、
|
||||
`sha256:2a9b93dcf19984c90a628b8987a04620f31ec0acb62c672bfe912c19bc458b2b`,最终 retirement receipt digest 为
|
||||
`sha256:e20532f2f2cd3fce3e7985e4bb3b4b75dc87ca3b59d7f9530ad9d8e02694d1f3`,Docker container/network 残留为 0。
|
||||
PostgreSQL 18.6/arm64 HA 为 142/142 gates、timeline `1→2`,独立 evidence audit compatible;mode-0600 报告 SHA-256 为
|
||||
`796e4b85a9f45c3b63537010057c1c91c6011ecfab2f2f289763b4915037d9dd`,container/network/volume 残留为 0。仓库 fixture 证明代码路径与
|
||||
真实 Kubernetes mutation,但不能冒充 GHCR 在线验签;第一份公开 GHCR/Cosign/GitHub 成功记录仍必须由实际受保护 `v3` release tag
|
||||
dispatch 产生。
|
||||
|
||||
- D-343/ADR-0435(已接受;真实公开 catalog 运行仍待 release Gate):在 D-342 的 resource-inventory-closed Head 上增加独立
|
||||
`cluster.deployment.retirement.preflight|apply|receipt.audit`。退役目标只能来自 current locked manifest 与 committed active inventory,
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# ADR-0436:发布后 Catalog-bound Kubernetes Release Gate
|
||||
|
||||
- 状态:Accepted
|
||||
- 日期:2026-08-18
|
||||
- 关联 RFC:QL-RFC-0001 D-336、D-338、D-339、D-341、D-343、D-344
|
||||
- 关联 ADR:ADR-0428、ADR-0430、ADR-0431、ADR-0433、ADR-0434、ADR-0435
|
||||
|
||||
## 上下文
|
||||
|
||||
ADR-0428 至 ADR-0435 已分别建立持久 OCI catalog、可信工作站消费、catalog-bound deployment lock、目标侧 Kubernetes
|
||||
install/upgrade/rollback、完整 inventory Head 与 UID/resourceVersion 围栏退役。但发布流水线仍在 durable catalog 与 receipt 上传后结束;
|
||||
独立 K3s live 门使用合成 lock,只能证明目标侧状态机,不能证明新发布的公开 catalog 能被一个没有发布权限的下游消费者重新发现、验签、
|
||||
下载、重建 deployment lock 并送入同一目标 ceremony。
|
||||
|
||||
把发布者 job 内已经读取过的文件直接交给 K3s 会掩盖 package visibility、discovery、Cosign workflow identity、GitHub source
|
||||
tag/revision provenance、OCI raw manifest round-trip 或跨 job 权限错误。反过来,给低配 Local/Edge 设备安装 regctl、Cosign、GitHub CLI、
|
||||
Node 或 K3s 又会破坏分层部署边界。因此缺口应由发布时的一次性 Cluster release gate 闭合,而不是进入产品运行时。
|
||||
|
||||
## 决策
|
||||
|
||||
1. `ql3-image-release.yml` 在 `release-set` 成功后增加独立 `release-catalog-deployment-live` job。它仅为 `cluster|all` scope 运行;
|
||||
`local` scope 明确跳过。job 使用 GitHub-hosted runner、30 分钟硬超时和只读 `contents|packages|attestations` 权限,不拥有 package write、
|
||||
OIDC signing、attestation write、Docker login、tag promotion 或 catalog mutation authority。
|
||||
2. 下游 job 不下载发布 job 的 release-set artifact 作为部署 authority。它从
|
||||
`ghcr.io/<owner>/qinglong3-release-catalog:v<version>-<scope>` 重新发现 digest,前后两次解析必须稳定;随后只使用 immutable catalog
|
||||
reference,按 ADR-0430 验证 exact Cosign workflow identity 与 GitHub source tag/revision provenance,并发布 exact-three-file、
|
||||
owner-private consumption bundle。regctl 与 Cosign 不接收 registry credential;公开读取失败即阻断 Cluster release。
|
||||
3. 工具链固定为 checksum-pinned `regctl@0.11.5`、immutable-action 安装的 Cosign、canonical GitHub CLI、checksum-verified kubectl
|
||||
v1.34.3 和 digest-reviewed K3s v1.34.3+k3s1。短期 GitHub token 只通过 mode-0600 文件进入 GitHub provenance verifier,ceremony
|
||||
后立即删除;bundle 与最终 evidence 不包含 credential。
|
||||
4. Kubernetes live contract 增加严格 all-or-none 的 release identity 环境输入。配置完整时先离线重审 consumption bundle,再把其中同一
|
||||
release-set 对象交给既有 `createKubernetesLock`,生成绑定 release-set/catalog manifest/consumption report digest 的 v2 lock;缺少
|
||||
任一 identity 字段、scope 为 local、角色镜像不唯一或 bundle 漂移都在启动 K3s 前失败关闭。普通 PR/live workflow 不提供这些字段,
|
||||
继续使用明确标注为 `synthetic_live_fixture` 的状态机 fixture。
|
||||
5. release gate 使用一个有界 7-resource manifest:Namespace、四个零副本角色 Deployment、Plugin Package admission ConfigMap 和一个
|
||||
可退役 ConfigMap。真实 catalog 中 `control|control-ai|admin|worker` 的 immutable image reference 被写入 API Server,但零副本门不拉取
|
||||
或启动业务镜像;镜像图、签名和 provenance 由上游发布/消费门负责,目标门负责证明 catalog authority 到 server-side apply、Head、
|
||||
inventory 与 retirement receipt 的连续性。
|
||||
6. 隔离的三节点 K3s 执行 install preflight/apply/receipt audit,再对目标 ConfigMap 执行 UID/resourceVersion-preconditioned retirement
|
||||
preflight/apply/receipt audit。成功证据必须声明 `verified_release_catalog`、三个 catalog/release digest、source revision/ref/scope、
|
||||
Head 收敛、目标 absent 与清理完成;不保存 bundle 正文、token、kubeconfig、command 或目标集群长期 credential。
|
||||
7. 该 Gate 不新增 workspace package、生产依赖、数据库、migration、controller、CRD、webhook、ServiceAccount、Pod、listener、timer
|
||||
或常驻进程。Local/Edge/Standalone 的镜像、导入图和稳态资源不变;集群用户也只在发布流水线承担一次性验证成本。
|
||||
8. 第一份真实成功 evidence 只能由实际受保护 `v3` release tag 的 workflow dispatch 产生。仓库内 fixture、离线 bundle 和本机 K3s
|
||||
回归可以验证代码与失败关闭语义,但不能冒充尚未发生的公开 GHCR/Cosign/GitHub 在线 ceremony。
|
||||
|
||||
## 部署与资源影响
|
||||
|
||||
- 只在 Cluster 发布时新增一次 GitHub-hosted runner job;三节点 K3s、kubectl proxy、私有目录和 Docker network/container 全部短生命周期。
|
||||
- 四个业务 Deployment 固定 `replicas=0`,不下载或启动 QingLong 业务镜像;因此该 Gate 的成本由 K3s control-plane 与 API 操作主导,
|
||||
不把完整 Cluster 数据面启动时间混入 supply-chain 判定。
|
||||
- Local-only 发布无该 job;路由器、NAS、Edge 与 Standalone 用户不安装 catalog 工作站工具,也不运行 Kubernetes。
|
||||
- 发布者与消费者权限分离。consumer 不能修复 package visibility 或 tag;它只能让发布失败,并由维护者修正发布配置后对同一 tag 重跑。
|
||||
|
||||
## 被拒绝的替代方案
|
||||
|
||||
### 在 `release-set` publisher job 内直接运行 K3s
|
||||
|
||||
拒绝。它复用发布者登录态、文件和写权限,不能证明一个真实下游只读消费者可以独立取得 catalog。
|
||||
|
||||
### 从 90 天 workflow artifact 生成 deployment lock
|
||||
|
||||
拒绝。短期 artifact 不是长期发现入口,也绕过 immutable OCI manifest、Cosign 与 GitHub OCI provenance 的重新验证。
|
||||
|
||||
### 只解析 mutable discovery tag 一次
|
||||
|
||||
拒绝。tag 只有发现权;ceremony 必须固定 immutable digest,并在下载与验证后再次确认 discovery 未变化。
|
||||
|
||||
### 为 Local/Edge 也运行 K3s 或下发验签工具
|
||||
|
||||
拒绝。Local image 已有独立 Compose digest rollout gate;Cluster K3s 门不能成为低资源部署的发布或稳态依赖。
|
||||
|
||||
### 在 release gate 启动四类真实业务 Pod
|
||||
|
||||
拒绝。业务启动还依赖数据库、Secret、Policy 与各自 lifecycle gate,会把 supply-chain/目标 mutation 证明扩展成不稳定的全栈验收。此门只验证
|
||||
immutable image authority 被目标 API 接受和 Head/receipt 闭包,不重复镜像构建与各产品 live contract。
|
||||
|
||||
## 验证
|
||||
|
||||
- `ql3ClusterImageReleaseAudit` 冻结 job dependency、scope、只读权限、11 个精确步骤、三套 pinned 工具、公开 catalog create+offline audit、
|
||||
digest-reviewed K3s、catalog-bound live 输入、隔离清理与 content-free evidence,并包含移除 Gate、错误 Local 条件和 consumer 写权限的负向用例;
|
||||
- `ql3DeploymentLockContract` 验证完整/部分环境选择、cluster/all scope、同一 audited release-set 到 v2 lock 的物化、四角色引用、7-resource
|
||||
closure、五个受影响资源的 catalog annotations 与退役目标存在;
|
||||
- 发布/部署定向契约 109/109;完整 backend 1,344 项为 1,342 pass/2 条件 skip/0 fail,18-package clean build/test 退出 0;
|
||||
- 14 项有效架构/发布/部署审计与 14 档 Local artifact 全部 compatible;package boundary 为 18,`singleSourcePackages=[]`、
|
||||
`shallowSourcePackages=[]`,所有 Local artifact 字节数与 D-343 一致;
|
||||
- 使用真实执行六步 catalog ceremony 的可离线重审 fixture bundle,在 linux/arm64 三节点 K3s v1.34.3+k3s1 上完成 catalog-bound
|
||||
lock、7-resource install、Head `1→2`、inventory `7→6` 和 UID/resourceVersion 围栏退役;两个 receipt audit、target absent、Unix
|
||||
socket proxy 与 cleanup 全部通过,最终 retirement receipt digest 为
|
||||
`sha256:e20532f2f2cd3fce3e7985e4bb3b4b75dc87ca3b59d7f9530ad9d8e02694d1f3`;
|
||||
- PostgreSQL 18.6/arm64 HA 为 142/142、timeline `1→2`,独立 evidence audit compatible,报告 SHA-256 为
|
||||
`796e4b85a9f45c3b63537010057c1c91c6011ecfab2f2f289763b4915037d9dd`,所有 HA Docker 资源清理为零;
|
||||
- 真实公开 registry/Cosign/GitHub ceremony 仍必须由第一份实际受保护 release tag 运行记录闭合,本机 fixture 不计作线上成功证据。
|
||||
|
||||
## 规范依据
|
||||
|
||||
- [GitHub artifact attestations verification](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/verifying-the-authenticity-of-artifacts)
|
||||
- [Sigstore Cosign verify](https://docs.sigstore.dev/cosign/verifying/verify/)
|
||||
- [OCI image-spec manifest](https://github.com/opencontainers/image-spec/blob/main/manifest.md)
|
||||
- [Kubernetes server-side apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/)
|
||||
@@ -439,6 +439,7 @@
|
||||
| [ADR-0433](./ADR-0433-catalog-bound-kubernetes-target-deployment-ceremony.md) | Catalog-bound Kubernetes 目标部署 Ceremony | Accepted |
|
||||
| [ADR-0434](./ADR-0434-resource-inventory-closed-kubernetes-deployment-head.md) | Resource-inventory-closed Kubernetes Deployment Head | Accepted |
|
||||
| [ADR-0435](./ADR-0435-uid-resource-version-fenced-kubernetes-resource-retirement.md) | UID/resourceVersion 围栏的 Kubernetes 资源退役 Ceremony | Accepted |
|
||||
| [ADR-0436](./ADR-0436-post-publication-catalog-bound-kubernetes-release-gate.md) | 发布后 Catalog-bound Kubernetes Release Gate | Accepted(首份真实公开 catalog evidence 待实际 release tag) |
|
||||
|
||||
## 规则
|
||||
|
||||
|
||||
@@ -13,6 +13,19 @@ ghcr.io/<owner>/qinglong3-release-catalog:v<version>-<scope>
|
||||
`ghcr.io/<owner>/qinglong3-release-catalog@sha256:<manifest-digest>`,验证这个 immutable reference 后,再从 JSON 的
|
||||
`images[].reference` 读取完整 `ghcr.io/<owner>/<repository>@sha256:<digest>`。
|
||||
|
||||
## 发布流水线内置 Cluster 下游门
|
||||
|
||||
`ql3-image-release.yml` 在 durable catalog、签名、provenance、receipt 和 90 天便利 bundle 全部发布后,才启动独立
|
||||
`release-catalog-deployment-live`。该 job 只处理 `cluster|all`;Local-only 发布不会启动 K3s。它没有 publisher 的 package/attestation
|
||||
写权限或 registry 登录态,而是按下文工作站协议重新消费公开 catalog,离线重建 deployment lock,并在隔离三节点 K3s 上完成 install、
|
||||
Head commit、一个显式 ConfigMap 的 UID/resourceVersion 围栏退役和两个 receipt audit。任何公开读取、exact workflow identity、source
|
||||
tag/revision、digest、scope、角色闭包、server-side apply/Head 或清理失败都会让 Cluster release 失败。
|
||||
|
||||
该门的四个业务 Deployment 都为零副本:它证明真实 catalog image authority 已进入目标 API、lock、Head 与 receipt 闭包,不重复下载和启动
|
||||
完整业务数据面。应用启动、数据库、Secret、Worker 与 AI lifecycle 继续由各自 live/release gate 负责。成功 artifact 只保存 content-free
|
||||
JSON evidence;consumption bundle、token、kubeconfig 和 command 文件不上传。首份真实成功记录必须来自实际受保护 release tag,普通 PR 中的
|
||||
synthetic live fixture 不能替代它。
|
||||
|
||||
## 选择 scope
|
||||
|
||||
| 部署类型 | release scope | 必须出现的镜像 |
|
||||
@@ -393,7 +406,9 @@ GHCR 不提供跨 repository tag 事务,release set 明确记录 `crossReposit
|
||||
失败,不删除已经正确的 tag,也不重新构建镜像。使用原 source tag/revision 重跑 release workflow:它会先验证
|
||||
每个 source digest 和既有 tag;既有 tag 指向同一 digest 时继续,指向其他 digest 时立即失败。只有
|
||||
release-set、catalog immutable digest、两类 provenance 与 receipt 全部生成并验证后,才能宣布该 deployment family
|
||||
可部署。
|
||||
可部署。对于 `cluster|all`,还必须等待只读 catalog consumer 与 catalog-bound K3s deployment/retirement Gate 成功;publisher 成功而
|
||||
consumer 失败时不能宣布 Cluster release。不要给 consumer 临时增加写权限“修复”可见性或 tag,应修正 GHCR package visibility/retention
|
||||
或发布配置后,对同一受保护 tag 重跑完整工作流并重新验证 exact digest。
|
||||
|
||||
workflow bundle 当前保留 90 天;长期入口是 OCI catalog 的 immutable digest。GHCR 并非 WORM,release owner 仍须维护
|
||||
package 可见性、读取权限和满足组织要求的 retention/备份策略。任何归档或镜像过程都不得改写 canonical JSON,并须保留
|
||||
|
||||
Reference in New Issue
Block a user