mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
210 lines
10 KiB
Markdown
210 lines
10 KiB
Markdown
# QingLong 3.0 release-set 部署准入
|
||
|
||
生产部署的镜像 authority 是持久 OCI catalog 中经过签名、provenance 与逐字节回读验证的 immutable release-set
|
||
reference,不是可变 version/source/catalog tag。Actions 中保留 90 天的同名 bundle 只用于便利下载。
|
||
|
||
发布入口为:
|
||
|
||
```text
|
||
ghcr.io/<owner>/qinglong3-release-catalog:v<version>-<scope>
|
||
```
|
||
|
||
该 discovery tag 只用于发现。先把它解析为
|
||
`ghcr.io/<owner>/qinglong3-release-catalog@sha256:<manifest-digest>`,验证这个 immutable reference 后,再从 JSON 的
|
||
`images[].reference` 读取完整 `ghcr.io/<owner>/<repository>@sha256:<digest>`。
|
||
|
||
## 选择 scope
|
||
|
||
| 部署类型 | release scope | 必须出现的镜像 |
|
||
| --- | --- | --- |
|
||
| 低配路由器、Edge、Standalone | `local` | `local` |
|
||
| Kubernetes/Cluster | `cluster` | `control`、`control-ai`、`worker`、`admin` |
|
||
| 同时发布两族 | `all` | 上述五个镜像 |
|
||
|
||
Local 用户不需要下载 Cluster 镜像,也不依赖 CloudNativePG 或 Worker 私有发布证据。Cluster 运维者不能拿 Local
|
||
image 的证明替代任一角色镜像;尤其 Worker 与短生命周期 Admin 必须有各自 digest。
|
||
|
||
## 工作站验真
|
||
|
||
以下命令应在可信维护工作站运行;先设置目标发布的显式值、三个经过 `realpath` 解析的工具路径,以及一个
|
||
current-owner、无 group/other 权限、无换行的短期 GitHub token file。`output_parent` 必须是已有的 owner-private
|
||
目录,`bundle` 必须尚不存在:
|
||
|
||
```sh
|
||
owner='<lowercase-owner>'
|
||
repository='<owner>/<source-repository>'
|
||
version='<source-derived-version>'
|
||
scope='local' # 或 cluster/all
|
||
source_ref='refs/tags/v<version>'
|
||
source_revision='<40-hex-git-revision>'
|
||
regctl_path='<canonical-absolute-path>/regctl'
|
||
cosign_path='<canonical-absolute-path>/cosign'
|
||
gh_path='<canonical-absolute-path>/gh'
|
||
token_file='<canonical-absolute-owner-private-token-file>'
|
||
output_parent='<canonical-absolute-owner-private-directory>'
|
||
bundle="${output_parent}/qinglong3-release-catalog-consumption-${version}-${scope}"
|
||
```
|
||
|
||
使用机器化 ceremony 完成 discovery 双次解析、immutable Cosign/GitHub provenance 验证、release-set 下载/inspection、raw
|
||
manifest/plan/receipt reconstruction 和 no-replace bundle 发布:
|
||
|
||
```sh
|
||
node scripts/ql3-release-catalog-consumption-ceremony.cjs \
|
||
--mode=create \
|
||
--version="${version}" \
|
||
--source-revision="${source_revision}" \
|
||
--source-ref="${source_ref}" \
|
||
--release-scope="${scope}" \
|
||
--repository-owner="${owner}" \
|
||
--source-repository="${repository}" \
|
||
--regctl="${regctl_path}" \
|
||
--cosign="${cosign_path}" \
|
||
--gh="${gh_path}" \
|
||
--github-token-file="${token_file}" \
|
||
--output-directory="${bundle}"
|
||
|
||
node scripts/ql3-release-catalog-consumption-ceremony.cjs \
|
||
--mode=audit \
|
||
--version="${version}" \
|
||
--source-revision="${source_revision}" \
|
||
--source-ref="${source_ref}" \
|
||
--release-scope="${scope}" \
|
||
--repository-owner="${owner}" \
|
||
--source-repository="${repository}" \
|
||
--output-directory="${bundle}"
|
||
|
||
release_set="${bundle}/qinglong3-release-set-${version}-${scope}.json"
|
||
catalog_manifest="${bundle}/qinglong3-release-catalog-manifest-${version}-${scope}.json"
|
||
consumption_report="${bundle}/qinglong3-release-catalog-consumption-${version}-${scope}.json"
|
||
```
|
||
|
||
bundle 只能包含上面三项 `0600` 文件,目录自身为 `0700`。ceremony 不使用 shell 重定向、不覆盖文件,并在每一步前与
|
||
结束前复验三个 executable;GitHub token 只进入一个 `gh attestation verify` 子进程。raw manifest 让离线 audit 能重建
|
||
catalog plan/receipt,但它不会离线重放网络签名,因此 audit 输出必须保持 `externalToolResultsReplayed:false`。
|
||
|
||
若改用 90 天 Actions bundle 的 release-set 文件,仍需另行验证该文件的 provenance;它不能替代上述 immutable catalog
|
||
ceremony,也不能与在线 ceremony 下载的文件混合后伪造成同一 three-file bundle。
|
||
|
||
## 生成离线 deployment lock
|
||
|
||
`inspect` 成功后,不要手工复制 digest,也不要直接修改仓库中的 Kustomize 占位符。deployment-lock
|
||
materializer 在可信工作站离线运行,只读取已经验证的 release set 与本地清单;它不访问 registry、不连接 Kubernetes
|
||
API,也不会执行 `kubectl apply`。
|
||
|
||
### Local / Compose
|
||
|
||
对 `local` 或 `all` scope 生成一个 canonical、0600、no-replace 的 service selection:
|
||
|
||
```sh
|
||
selection="$(pwd)/qinglong3-local-selection-${version}.json"
|
||
|
||
node scripts/ql3-deployment-lock-contract.cjs \
|
||
--mode=local-create \
|
||
--version="${version}" \
|
||
--source-revision="${source_revision}" \
|
||
--source-ref="${source_ref}" \
|
||
--release-scope="${scope}" \
|
||
--repository-owner="${owner}" \
|
||
--release-set="${release_set}" \
|
||
--allow-root-service=false \
|
||
--output="${selection}"
|
||
|
||
node scripts/ql3-deployment-lock-contract.cjs \
|
||
--mode=local-audit \
|
||
--version="${version}" \
|
||
--source-revision="${source_revision}" \
|
||
--source-ref="${source_ref}" \
|
||
--release-scope="${scope}" \
|
||
--repository-owner="${owner}" \
|
||
--release-set="${release_set}" \
|
||
--allow-root-service=false \
|
||
--selection="${selection}"
|
||
```
|
||
|
||
把已审计的 `service.image` 和 `service.allowRootService` 交给现有 Local private prepare/rollout 入口。selection
|
||
本身不修改 Compose 文件,也不启动容器。是否允许 root service 必须显式给出,不能由设备默认值推断。
|
||
|
||
### Kubernetes / Cluster / Worker
|
||
|
||
先用已审核 overlay 生成普通多文档 YAML,再把它作为 post-render 输入。以下是 Cluster Core 示例;AI、Worker、Admin
|
||
清单分别把 `required-images` 设为 `control-ai`、`worker`、`admin`,组合清单则按发布顺序使用
|
||
`control,control-ai,admin,worker`:
|
||
|
||
```sh
|
||
rendered="$(pwd)/ql3-cluster-rendered.yaml"
|
||
locked="$(pwd)/ql3-cluster-locked.yaml"
|
||
lock_report="$(pwd)/ql3-cluster-deployment-lock.json"
|
||
|
||
kubectl kustomize deploy/kubernetes/ql3-cluster/overlays/cloudnative-pg > "${rendered}"
|
||
|
||
node scripts/ql3-deployment-lock-contract.cjs \
|
||
--mode=kubernetes-create \
|
||
--version="${version}" \
|
||
--source-revision="${source_revision}" \
|
||
--source-ref="${source_ref}" \
|
||
--release-scope="${scope}" \
|
||
--repository-owner="${owner}" \
|
||
--release-set="${release_set}" \
|
||
--manifest="${rendered}" \
|
||
--required-images=control \
|
||
--output-manifest="${locked}" \
|
||
--output-report="${lock_report}"
|
||
|
||
node scripts/ql3-deployment-lock-contract.cjs \
|
||
--mode=kubernetes-audit \
|
||
--version="${version}" \
|
||
--source-revision="${source_revision}" \
|
||
--source-ref="${source_ref}" \
|
||
--release-scope="${scope}" \
|
||
--repository-owner="${owner}" \
|
||
--release-set="${release_set}" \
|
||
--manifest="${rendered}" \
|
||
--locked-manifest="${locked}" \
|
||
--report="${lock_report}" \
|
||
--required-images=control
|
||
```
|
||
|
||
materializer 只改写 Pod、Deployment、StatefulSet、DaemonSet、ReplicaSet、Job、CronJob 的
|
||
`containers`/`initContainers`/`ephemeralContainers`,以及固定名称
|
||
`ql3-plugin-package-secret-action-admission` ConfigMap 的 `data.image`。每个改写资源及其 Pod template 都绑定
|
||
release-set digest、source revision 与 version annotation;未知位置的完整 QingLong role image authority、畸形已知
|
||
container image、缺少 required role、YAML alias/cycle/非 mapping、超限输入或已有输出文件都会失败关闭。
|
||
|
||
审计成功并完成人工差异检查后,才由有权限的独立步骤执行 `kubectl apply -f "${locked}"`。不要直接 apply
|
||
`${rendered}`,也不要使用 `kubectl apply -k` 绕过 deployment lock。
|
||
|
||
## 准入检查
|
||
|
||
1. 只接受已验证 Cosign exact workflow identity 与 GitHub source tag/revision provenance 的 catalog immutable
|
||
reference;discovery tag 无 authority。
|
||
2. `schema` 必须为 `qinglong/release-set@v1`;`release.version`、`release.sourceRef`、
|
||
`release.sourceRevision`、`release.scope` 必须与变更单一致。
|
||
3. 镜像集合必须与上表精确相等;每个 `reference` 必须是 digest reference,且 owner/repository 与部署目标一致。
|
||
4. Kubernetes 必须先渲染 overlay,再用离线 post-render materializer 生成和复验 locked manifest;嵌套 overlay 的
|
||
`newName`/digest 不是最终 authority。Local 必须生成并审计 service selection。两族最终都只能消费 release set 中的
|
||
`@sha256:` reference。
|
||
5. rollout 前再次确认 catalog receipt/immutable reference 与已检查文件一致。version/source/catalog tag 都只能用于
|
||
发现;部署始终以 release set 中的镜像 digest 为准。
|
||
|
||
## 低资源设备
|
||
|
||
路由器或其他低配 Edge 设备不需要安装 Node、regctl、Cosign、GitHub CLI、Kustomize 或 materializer。维护者在可信工作站
|
||
完成上述 ceremony 和 Local selection 审计,再向设备传输已检查的 canonical JSON,并只把 `local` family 的 immutable
|
||
image reference 写入 compose/rollout。
|
||
设备不下载 Cluster 四镜像,也不加载 Kubernetes、CloudNativePG、PostgreSQL driver 或 Worker 私有发布证据。
|
||
|
||
如果设备本身不运行容器 registry client,可由工作站按 digest 拉取并通过既有离线交付渠道传送镜像;离线包的哈希与
|
||
导入后 image digest 必须继续匹配 release set,不能退回 tag。
|
||
|
||
## 发布失败与恢复
|
||
|
||
GHCR 不提供跨 repository tag 事务,release set 明确记录 `crossRepositoryAtomicity=false`。如果 promotion 中途
|
||
失败,不删除已经正确的 tag,也不重新构建镜像。使用原 source tag/revision 重跑 release workflow:它会先验证
|
||
每个 source digest 和既有 tag;既有 tag 指向同一 digest 时继续,指向其他 digest 时立即失败。只有
|
||
release-set、catalog immutable digest、两类 provenance 与 receipt 全部生成并验证后,才能宣布该 deployment family
|
||
可部署。
|
||
|
||
workflow bundle 当前保留 90 天;长期入口是 OCI catalog 的 immutable digest。GHCR 并非 WORM,release owner 仍须维护
|
||
package 可见性、读取权限和满足组织要求的 retention/备份策略。任何归档或镜像过程都不得改写 canonical JSON,并须保留
|
||
原 catalog manifest digest、receipt 与 provenance 关联。
|