Files
qinglong/docs/operations/ql3-release-set-deployment.md
T

214 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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}"
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 重定向、不覆盖文件,并在每一步前与
结束前复验三个 executableGitHub 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
`audit` 成功后,不要从 bundle 中抽出 release-set 再作为独立输入,不要手工复制 digest,也不要直接修改仓库中的
Kustomize 占位符。deployment-lock materializer 在可信工作站离线运行,必须重新审计完整 three-file bundle,并让同一次审计
读取的 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}" \
--source-repository="${repository}" \
--consumption-bundle="${bundle}" \
--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}" \
--source-repository="${repository}" \
--consumption-bundle="${bundle}" \
--allow-root-service=false \
--selection="${selection}"
```
v2 selection 同时绑定 catalog immutable reference、manifest digest、consumption report digest 与 release-set digest。把已审计的
`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}" \
--source-repository="${repository}" \
--consumption-bundle="${bundle}" \
--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}" \
--source-repository="${repository}" \
--consumption-bundle="${bundle}" \
--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、catalog
manifest、consumption report 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
referencediscovery tag 无 authority。
2. materializer 只能接受完整 `qinglong/release-catalog-consumption-ceremony@v1` bundle,不能接受旧的松散 `--release-set`;其中
`qinglong/release-set@v1``release.version``release.sourceRef``release.sourceRevision``release.scope` 必须与变更单一致。
3. 镜像集合必须与上表精确相等;每个 `reference` 必须是 digest reference,且 owner/repository 与部署目标一致。
4. Kubernetes 必须先渲染 overlay,再用离线 post-render materializer 生成和复验 v2 locked manifest;嵌套 overlay 的
`newName`/digest 不是最终 authority。Local 必须生成并审计 v2 service selection。两族输出都必须绑定同一 catalog manifest、
consumption report 与 release-set digest,并且只能消费 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 v2 selection 审计,再向设备传输已检查的 catalog-bound 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 并非 WORMrelease owner 仍须维护
package 可见性、读取权限和满足组织要求的 retention/备份策略。任何归档或镜像过程都不得改写 canonical JSON,并须保留
原 catalog manifest digest、receipt 与 provenance 关联。