docs: 完善 v1alpha1 设计与运维合同

This commit is contained in:
2026-09-10 05:23:33 +00:00
parent 4823eeb767
commit a723372c6e
10 changed files with 732 additions and 91 deletions
+45 -21
View File
@@ -17,7 +17,7 @@
## 1. 背景
homelab 中的大部分应用共享一个运行在独立 VM 上的 PostgreSQL DBMS。应用需要各自
独立的 database、owner role、login role 和密码,但不需要独立 PostgreSQL 实例。
独立的 database、作为 owner 的 login role 和密码,但不需要独立 PostgreSQL 实例。
目前这些资源依靠人工 SQL 和人工 Secret 管理,难以重复、审计和检测漂移。
本系统使用 Kubernetes CRD 作为声明式 API,持续协调外部 PostgreSQL 与 OpenBao:
@@ -69,9 +69,10 @@ v1alpha1 不负责:
| 对象 | 事实来源 | 说明 |
| --- | --- | --- |
| 期望状态 | Kubernetes CR `spec` | 用户声明的合同 |
| 最近观察结果 | Kubernetes CR `status` | 可以丢失并重建,不是外部事实来源 |
| 最近观察结果与当前阶段 | Kubernetes CR `status` | 可以丢失并重建,不是外部事实来源 |
| database/role/grant/extension | PostgreSQL catalog | 每轮 reconcile 必须重新读取 |
| 受管资源所有权与协调阶段 | PostgreSQL controller registry | 与受管 DBMS 一起备份和恢复 |
| 受管资源所有权与保留标记 | PostgreSQL controller registry | 与受管 DBMS 一起备份和恢复 |
| controller 工作流阶段 | Kubernetes CR `status.phase` | 状态机 checkpoint;可由外部事实保守重建 |
| 应用凭据 | OpenBao KV v2 | Kubernetes API 中不得出现明文 |
| Kubernetes 凭据投射 | External Secrets Operator | ExternalSecret 由本 controller 管理 |
| PostgreSQL 管理凭据 | OpenBao KV v2 | 由 `PostgreSQLInstance` 引用 |
@@ -106,7 +107,7 @@ PostgreSQL 管理 role。应用或 GitOps 流程在获得 namespace RBAC 后管
- 一个 database;
- 一个同时作为 database owner、供应用使用的 `LOGIN` role;
- 零个或多个 extension;
- 一个 OpenBao KV v2 凭据位置。
- 一个 OpenBao KV v2 凭据位置;
- 一个同 namespace ExternalSecret 及其目标 Kubernetes Secret。
Tenant 的 namespace 用于 Kubernetes RBAC 和身份识别,不代表 PostgreSQL schema。
@@ -233,19 +234,36 @@ label、trace、CR spec/status 或测试快照。
## 9. Reconcile 行为
系统采用最终一致性模型。Kubernetes、PostgreSQL 和 OpenBao 可以短暂处于不同阶段;
controller 不尝试实现跨系统事务,而是通过 PostgreSQL 中持久化的协调阶段、幂等外部
操作和每轮回读验证最终收敛。
系统采用最终一致性模型。Kubernetes、PostgreSQL、OpenBao 和 ESO 可以短暂处于不同
阶段;controller 不尝试实现跨系统事务,而是以 Kubernetes CR `status.phase` 作为
工作流 checkpoint,通过幂等外部操作和每轮回读验证最终收敛。
每个 Tenant 的 registry 记录至少经历以下单向阶段:
两个 CR 的状态机权威记录都在 `status.phase`。controller 根据 phase 选择下一项候选
动作,但 phase 不能替代外部状态检查:执行前后仍须回读 PostgreSQL catalog、registry、
OpenBao 和 Kubernetes/ESO。外部写入成功但 status 更新失败时,下一轮必须识别已完成
事实并推进 phase,不得重复生成密码或报告虚假冲突。
status 丢失时,controller 必须从 registry 的所有权记录和各外部系统实际状态保守重建
phase。若 status 被伪造或领先于实际状态,controller 必须纠正到安全阶段并补齐资源,
不能跳过验证。registry 不保存或驱动协调 phase。
Instance phase 按当前 generation 表示连接与初始化进度:
```text
Planned -> CredentialCreated -> RoleCreated -> DatabaseCreated
-> ExternalSecretCreated -> CredentialProjected -> Ready
Pending -> Validating -> InitializingRegistry -> Ready
(any phase) --------------------------------> Deleting
```
阶段用于恢复进度,但不能替代实际状态检查。controller 重启后必须同时检查 registry、
PostgreSQL catalog 和 OpenBao,再决定继续、保持 Ready 或报告 Conflict。
spec generation 改变后可以从 `Ready` 回到 `Validating`。Tenant phase 如下:
```text
Pending -> Planned -> CredentialCreated -> RoleCreated -> DatabaseCreated
-> ExternalSecretCreated -> CredentialProjected -> Ready -> Deleting
```
失败不增加 `Failed` phase;phase 保留在无法推进的步骤,由 `Ready=False` 的 Reason 和
message 表达 `Conflict`、认证失败或依赖不可用。Retain 删除完成后 CR 已不存在,因此
没有持久的 `Retained` phase。
每轮 Tenant reconcile 必须按以下逻辑执行:
@@ -266,7 +284,8 @@ PostgreSQL catalog 和 OpenBao,再决定继续、保持 Ready 或报告 Confli
要求:
- 所有步骤必须幂等;
- 每个外部写入前必须先持久化足够的操作意图,写入后必须回读并推进 registry 阶段;
- 每个外部写入前必须先在 CR status 持久化足够的操作意图,写入后必须回读并推进
`status.phase`;
- 暂时性网络、锁和依赖错误必须重试;
- 输入错误、资源冲突和禁止操作不得忙循环重试,只在 generation 或依赖状态变化后
重试;
@@ -274,7 +293,7 @@ PostgreSQL catalog 和 OpenBao,再决定继续、保持 Ready 或报告 Confli
- 用户从 `spec.extensions` 移除 extension 时不得执行卸载,必须报告该字段在 v1alpha1
中只允许追加;
- controller 重启不得影响已经签发的应用密码;
- `status` 丢失后必须可以从 PostgreSQL 和 OpenBao 重建。
- `status` 丢失后必须可以从 registry、PostgreSQL、OpenBao 和 Kubernetes/ESO 重建。
## 10. Condition 合同
@@ -363,8 +382,9 @@ v1alpha1 不接管现有 database 或 role,但必须提供可重复、可回
11. 保留旧 database、role 和备份直到回滚窗口结束,再由管理员手工清理。
回滚时停止新应用写入、恢复原名称或连接配置,并重新使用旧凭据。迁移工具不得把旧
密码、管理凭据或 dump 文件提交到 Git。真实命令、锁定方式和各现有应用验证项在实现
首个可用版本前写入独立 `docs/migration.md` 并通过临时 PostgreSQL 实例演练。
密码、管理凭据或 dump 文件提交到 Git。真实命令、锁定方式和各现有应用验证项见
[`migration.md`](migration.md),并必须在实现首个可用版本前通过临时 PostgreSQL 实例
演练。
## 13. 安全要求
@@ -382,7 +402,7 @@ v1alpha1 不接管现有 database 或 role,但必须提供可重复、可回
7. controller 不得通过 shell 或 `psql` 子进程执行用户输入。
8. 错误包装、结构化日志和 tracing 必须经过 Secret 泄露测试。
详细威胁模型和部署 policy 将在 `docs/security.md` 中定义。
详细威胁模型和部署 policy 见 [`security.md`](security.md)。
## 14. 可观测性要求
@@ -425,6 +445,8 @@ v1alpha1 至少必须提供:
URL。
17. DNS 不可用时,使用输出的 `hostaddr` 可以连接 PostgreSQL;server 证书同时覆盖
`host` 的 DNS SAN 和 `hostaddr` 的 IP SAN,两种连接目标均可通过 `verify-full`。
18. 两个 CR 的 `status.phase` 都能反映当前协调步骤;清空 status 后可以从外部事实重建,
且伪造或过期 phase 不会使 controller 跳过验证或外部操作。
单元测试验证纯决策逻辑,adapter 集成测试使用 Docker PostgreSQL/OpenBao,controller
集成测试使用 envtest,完整网络路径使用 Kind E2E。
@@ -445,8 +467,9 @@ v1alpha1 至少必须提供:
bundle,不接触 CA 私钥。bundle 可以由 ConfigMap 或现有证书同步机制投射,不允许
Tenant 或 Instance 选择其他 CA;开发环境可以显式使用 `sslMode: disable`。
- 每个 PostgreSQLInstance 在其管理 database 中维护 controller 专用 registry schema。
registry 是受管资源所有权和协调阶段的权威记录;Tenant status 只是观察缓存,
Instance status 不聚合 Tenant 清单。
registry 是受管资源所有权、安装身份和 Retain 后 unmanaged 标记的权威记录;两个
CR 的 `status.phase` 是 controller 状态机的权威 checkpoint,Instance status 不聚合
Tenant 清单。
- PostgreSQL database 和 role identifier 必须匹配 `^[a-z][a-z0-9_]{0,62}$`,不支持
需要双引号的大小写或特殊字符名称。
- External Secrets Operator 是 v1alpha1 的运行依赖。controller 管理同 namespace
@@ -458,7 +481,8 @@ v1alpha1 至少必须提供:
## 17. 批准状态
具体设计决策和本文整体已于 2026-09-10 获得批准,可以进入 API reference、测试和
实现阶段。
实现阶段。同日确认状态机修订:两个 CR 的 `status.phase` 是 controller 工作流的权威
checkpoint;PostgreSQL registry 只承担所有权、安装身份和保留状态。
## 18. 与当前脚手架的已知差异
@@ -471,7 +495,7 @@ v1alpha1 至少必须提供:
- 删除独立 `ownerRole` 字段,使 login role 成为 database owner;
- 为 Instance 增加 `hostaddr`,为 Tenant 增加目标 Secret 配置及凭据输出 status;
- 按已确认的 identifier 合同收紧校验;
- 增加 PostgreSQL controller registry,记录基于 UID 的所有权和最终一致性协调阶段;
- 增加 PostgreSQL controller registry,记录基于 UID 的所有权、安装身份和保留状态;
- 修正凭据 type 中遗留的 rotation 注释;
- 使 Condition、不可变字段和 extension 追加语义具备 API 校验或明确的 reconcile
结果。