mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-23 12:05:27 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
# ADR-0198:绑定 Digest 的本机 Compose Rollout Preflight
|
||||
|
||||
- 状态:Accepted(只读 preflight 已实现;真实 apply/健康处置见 ADR-0199)
|
||||
- 日期:2026-07-29
|
||||
- 关联 RFC:QL-RFC-0001 D-184、D-185、D-186、D-187、D-188
|
||||
- 关联 ADR:ADR-0194、ADR-0195、ADR-0196、ADR-0197
|
||||
|
||||
## 背景
|
||||
|
||||
ADR-0197 已把 desired image 切换收敛为 generation CAS,但“某个 digest 已签名”和
|
||||
“该 image 能安全打开当前 deployment”是两种不同事实。旧 image 可能只支持较早
|
||||
SQLite capability;本机 tag/image ID 可能没有 registry identity;Compose 两个文件
|
||||
也可能被错误顺序、错误 project 或错误 daemon 解释。
|
||||
|
||||
原 Compose descriptor 未声明 project name。Compose 默认从配置目录名派生 project,
|
||||
所有标准 deployment 都位于名为 `service` 的目录,因此同一 Docker daemon 上的多个
|
||||
QingLong 实例可能共享 project identity。
|
||||
|
||||
在没有这些事实时直接实现自动 `up` 或自动 rollback,会把镜像供应链、Docker
|
||||
副作用、SQLite migration 和健康判断错误地包装成一个不存在的事务。
|
||||
|
||||
## 决策
|
||||
|
||||
### 1. Image config 声明可审计兼容窗口
|
||||
|
||||
AI-excluded local image 固定增加:
|
||||
|
||||
```text
|
||||
io.qinglong.local.application-config=2
|
||||
io.qinglong.local.compose-selection=1
|
||||
io.qinglong.local.sqlite-contract-min=35
|
||||
io.qinglong.local.sqlite-contract-max=35
|
||||
```
|
||||
|
||||
同时保留并复核 `io.qinglong.profile=edge,standalone`、
|
||||
`io.qinglong.ai=excluded`、OCI source/revision/version、numeric
|
||||
`65532:65532` 和唯一 entrypoint。min/max 是该 image 可打开的 SQLite contract
|
||||
闭区间,不是“最新版本”提示;当前 image 只接受 exact v35。
|
||||
|
||||
这些 label 同时进入:
|
||||
|
||||
- Dockerfile 静态 mutation audit;
|
||||
- native amd64/arm64 image inspect;
|
||||
- OCI layout 双平台 config exact audit;
|
||||
- pushed digest 的 release preflight。
|
||||
|
||||
调用方 command 中不得复制或声明这些 label。preflight 只信 Docker daemon 对 exact
|
||||
RepoDigest 返回的 image config。
|
||||
|
||||
### 2. Project 与容器 revision 身份稳定
|
||||
|
||||
基础 `compose.yaml` 的 top-level `name` 从 instance ID 生成:
|
||||
|
||||
```text
|
||||
ql3-<bounded-slug>-<domain-separated-sha256-prefix>
|
||||
```
|
||||
|
||||
`.` 会转为 `-`,摘要关闭 slug 碰撞。名称不依赖 deployment root,目录移动不会静默
|
||||
创建第二个 project;重复 instance ID 也不会在同一 daemon 上被当作两个实例。
|
||||
|
||||
`compose.image.yaml` 除 exact image 外,向 `qinglong3` service 写入:
|
||||
|
||||
```text
|
||||
io.qinglong.deployment.generation
|
||||
io.qinglong.deployment.mutation
|
||||
```
|
||||
|
||||
两者由 canonical selection 重复派生并在解析时交叉复核,不能由 operator 单独填写。
|
||||
后续 apply/recovery 可据此识别 container 对应的 durable desired revision。
|
||||
|
||||
### 3. Preflight 是现有短生命周期 CLI 的只读操作
|
||||
|
||||
入口继续复用现有 package 和 binary:
|
||||
|
||||
```text
|
||||
ql3-local-deploy compose-preflight \
|
||||
--command-file /absolute/private-command.json
|
||||
```
|
||||
|
||||
command 只含 deployment root、canonical Docker executable、canonical Unix socket、
|
||||
root acknowledgement 和 expected generation。Docker executable 必须是 root 或当前
|
||||
UID 拥有、不可 group/world write 的 canonical regular executable。socket 必须是
|
||||
root 或当前 UID 拥有、非 symlink、非 world-writable 的 canonical Unix socket。
|
||||
CLI 不使用 ambient Docker context。
|
||||
|
||||
检查顺序:
|
||||
|
||||
1. 复核 deployment/service/revision 目录与 active/archive exact;
|
||||
2. 从 application v2 读取 instance/Profile/busy timeout,再用正式 renderer 重建并
|
||||
byte-exact 复核 application config 与 Compose descriptor;
|
||||
3. 对 host SQLite 执行完整 read-only readiness:quick/foreign-key check、70 条
|
||||
migration history、schema、Plugin Package evidence 与 contract v35;
|
||||
4. 以显式 `--host unix://...` 和一次性 `0700` 空 Docker config 执行
|
||||
`image inspect`;要求本地 RepoDigests 包含 active exact reference,并复核
|
||||
OS/arch/user/entrypoint/source/revision/version/Profile/AI 与 compatibility labels;
|
||||
5. 对 base + selection 执行真实 `docker compose config --format json`,精确复核
|
||||
project、image、generation/mutation label、UID:GID、read-only root、network
|
||||
none、drop ALL、no-new-privileges、memory/PID、command、唯一 bind 和 tmpfs。
|
||||
|
||||
成功输出仅含 ready、generation、Profile、SQLite contract、architecture 和 service
|
||||
kind,不输出 root、socket、executable、image/digest 或 mutation。
|
||||
|
||||
### 4. Preflight 不执行 rollout
|
||||
|
||||
本命令不得:
|
||||
|
||||
- pull/build/push image;
|
||||
- create/start/stop/restart/remove container;
|
||||
- 打开可写 SQLite、执行 migration 或创建 backup;
|
||||
- 写 deployment receipt、修改 selection 或自动 rollback;
|
||||
- 把本地 registry evidence 宣称为正式 release。
|
||||
|
||||
正式 operator 仍须先完成 D-186 signature/attestation verify 和显式 pull,再运行本
|
||||
门。共享 release workflow 对 local pushed digest 的只读检查已由 ADR-0199 的 Edge
|
||||
与 Standalone 真实 rollout gate 包含并加强。
|
||||
|
||||
## 验收
|
||||
|
||||
- application config v2、selection v1、SQLite `35..35` 四项 label 在
|
||||
Dockerfile/native image/OCI layout/release workflow 中锁定;
|
||||
- Compose project name 对 instance 稳定,selection label 与 generation/mutation
|
||||
exact;
|
||||
- stale generation、active/archive/config/descriptor drift、非 socket、错误
|
||||
RepoDigest、标签窗口或 Compose 合并均拒绝;
|
||||
- 完整真实 SQLite v35 readiness 在 Docker 之前执行;
|
||||
- CLI 输出低敏且不产生 Docker container 副作用;
|
||||
- `local-owner-cli` 36/36、镜像/OCI/release 专项 36/36;
|
||||
- arm64 Docker Compose v5.3.1 对 Edge/Standalone 的 localhost exact manifest
|
||||
live preflight 均 compatible;
|
||||
- workspace 保持 22 packages,无新增生产依赖。
|
||||
|
||||
## 未包含
|
||||
|
||||
- `docker compose up`、active event、receipt 与失败 rollback(由 ADR-0199 实现);
|
||||
- SQLite migration 前 backup、写后 capability 识别和旧 image compatibility;
|
||||
- systemd/OpenRC 制品 compatibility/preflight;
|
||||
- 正式 GHCR 双架构 workflow 成功记录和物理路由设备门。
|
||||
Reference in New Issue
Block a user