|
| 1 | +# Start-Container Config Isolation Design |
| 2 | + |
| 3 | +## Context |
| 4 | + |
| 5 | +`scripts/pwsh/devops/start-container.ps1` 目前通过全局环境变量驱动 `docker compose`,并在脚本运行过程中直接把 `.env`、`.env.local`、`-Env` 与解析后的默认值写入当前 PowerShell 进程环境。这个模型对大多数服务是方便的,但会带来两个问题: |
| 6 | + |
| 7 | +1. 同一 PowerShell 会话内,不同服务之间会通过进程环境互相污染。例如先启动 `minio` 或 `rustfs` 后残留的 `DEFAULT_USER=root`,会影响后续 `postgre` 或 `paradedb` 的解析结果。 |
| 8 | +2. 脚本把“读取配置来源”和“应用环境变量”耦合在一起,导致后续脚本难以复用,也让排查最终值来源变得困难。 |
| 9 | + |
| 10 | +最近一次 `postgre` / `paradedb` 默认用户修复已经把服务级默认值收口到 `postgres`,但当前优先级仍允许通用 `DEFAULT_*` 被历史会话状态覆盖。这说明问题的根因不是默认值函数本身,而是配置来源与环境隔离模型。 |
| 11 | + |
| 12 | +## Goals |
| 13 | + |
| 14 | +- 修复 `start-container.ps1` 的环境污染问题,避免同一会话内前一次调用影响后一次调用。 |
| 15 | +- 保留现有易用性:继续支持 `-DefaultUser`、`-DefaultPassword`、`-Env`、`.env`、`.env.local`。 |
| 16 | +- 为 `psutils` 沉淀一套可复用的“配置解析 + 作用域环境执行”基础能力。 |
| 17 | +- 让 `postgre` / `paradedb` 继续兼容 `DEFAULT_*` 用法,但只接受“本次调用明确提供的来源”。 |
| 18 | +- 提供足够的调试信息,能回答“某个键最终来自哪里”。 |
| 19 | + |
| 20 | +## Non-Goals |
| 21 | + |
| 22 | +- 不重写 `start-container.ps1` 的整体 CLI 形态。 |
| 23 | +- 不要求所有服务立即改为专用变量。 |
| 24 | +- 不自动迁移已有 PostgreSQL/ParadeDB 数据目录中的用户、密码或默认库。 |
| 25 | +- 不在第一版支持复杂 JSON 映射、嵌套对象展开或变量插值。 |
| 26 | + |
| 27 | +## Approach Options |
| 28 | + |
| 29 | +### Option 1: 仅在 `start-container.ps1` 中快照并恢复环境 |
| 30 | + |
| 31 | +进入脚本时记录相关环境变量,退出时统一恢复。 |
| 32 | + |
| 33 | +优点是改动最小,但环境隔离能力仍沉在单个脚本中,后续其他脚本容易重复实现,因此不推荐。 |
| 34 | + |
| 35 | +### Option 2: 在 `psutils` 中新增双层通用能力 |
| 36 | + |
| 37 | +新增一个通用配置解析器和一个通用作用域环境执行器,`start-container.ps1` 只负责声明来源、优先级和服务特例。 |
| 38 | + |
| 39 | +这是推荐方案,因为它既能修复当前问题,又能沉淀公共基础设施,兼顾复用性与兼容性。 |
| 40 | + |
| 41 | +### Option 3: 改为临时 env 文件驱动 `docker compose` |
| 42 | + |
| 43 | +每次调用先渲染临时 env 文件,再用 `docker compose --env-file` 执行。 |
| 44 | + |
| 45 | +优点是来源非常显式,但实现更重,dry-run、文件清理和兼容性处理也更复杂,因此本次不采用。 |
| 46 | + |
| 47 | +## Design |
| 48 | + |
| 49 | +### 1. `psutils` 配置解析器 |
| 50 | + |
| 51 | +在 `psutils` 中新增通用配置解析器,例如 `Resolve-ConfigSources`。该函数只负责读取和合并配置,不直接写入当前进程环境。 |
| 52 | + |
| 53 | +第一版支持以下来源类型: |
| 54 | + |
| 55 | +- `Hashtable` |
| 56 | +- `EnvFile` |
| 57 | +- `JsonFile` |
| 58 | +- `ProcessEnv` |
| 59 | + |
| 60 | +调用方显式传入来源列表和优先级顺序。解析器返回一个结构化对象,至少包含: |
| 61 | + |
| 62 | +- `Values`:最终合并后的键值对 |
| 63 | +- `Sources`:每个键最终命中的来源标识 |
| 64 | +- `Trace`:可选调试信息,记录候选值和覆盖顺序 |
| 65 | + |
| 66 | +第一版 JSON 只支持顶层 key-value 对象,例如: |
| 67 | + |
| 68 | +```json |
| 69 | +{ |
| 70 | + "DEFAULT_USER": "postgres", |
| 71 | + "DEFAULT_PASSWORD": "12345678", |
| 72 | + "COMPOSE_PROJECT_NAME": "dev-paradedb" |
| 73 | +} |
| 74 | +``` |
| 75 | + |
| 76 | +不支持嵌套展开、数组映射或变量插值。 |
| 77 | + |
| 78 | +### 2. `psutils` 作用域环境执行器 |
| 79 | + |
| 80 | +在 `psutils` 中新增通用作用域环境执行器,例如 `Invoke-WithScopedEnvironment`。该函数接收一组环境变量覆盖值和一个 `ScriptBlock`,在执行前注入进程级环境变量,执行结束后无论成功或失败都恢复原值。 |
| 81 | + |
| 82 | +其职责与配置解析器严格分离: |
| 83 | + |
| 84 | +- 不关心配置来自哪里 |
| 85 | +- 只负责临时应用与恢复 |
| 86 | +- 支持变量原本不存在时在退出后清理 |
| 87 | + |
| 88 | +该能力与现有 PostgreSQL 工具中的临时环境恢复模式保持一致,但上提为公共基础设施。 |
| 89 | + |
| 90 | +### 3. `start-container.ps1` 的接入方式 |
| 91 | + |
| 92 | +`start-container.ps1` 改为“解析本次调用配置,再局部执行 compose”的模式: |
| 93 | + |
| 94 | +1. 收集本次调用显式来源 |
| 95 | + - CLI 参数 |
| 96 | + - `-Env` |
| 97 | + - 脚本目录下的 `.env` |
| 98 | + - 脚本目录下的 `.env.local` |
| 99 | + - 服务默认值 |
| 100 | +2. 调用 `Resolve-ConfigSources` |
| 101 | +3. 根据服务类型构造本次调用的 compose 环境变量集合 |
| 102 | +4. 使用 `Invoke-WithScopedEnvironment` 包裹 `Invoke-DockerCompose` |
| 103 | + |
| 104 | +脚本不再把解析后的 `DEFAULT_*`、`DATA_PATH`、`COMPOSE_PROJECT_NAME` 等值长期写回当前会话环境。 |
| 105 | + |
| 106 | +### 4. `postgre` / `paradedb` 的特例规则 |
| 107 | + |
| 108 | +本次不改变用户的使用习惯。数据库服务仍继续支持: |
| 109 | + |
| 110 | +- `-DefaultUser` |
| 111 | +- `-DefaultPassword` |
| 112 | +- `-Env @{ DEFAULT_USER = ... }` |
| 113 | +- `.env` / `.env.local` 中的 `DEFAULT_*` |
| 114 | + |
| 115 | +但它们默认不再读取“历史会话残留的 `ProcessEnv`”。换句话说,数据库服务继续兼容 `DEFAULT_*`,但只接受“本次调用明确提供的来源”。 |
| 116 | + |
| 117 | +普通服务可继续维持当前行为,允许按既有方式消费通用 `DEFAULT_*`。 |
| 118 | + |
| 119 | +### 5. 旧数据目录保护性提示 |
| 120 | + |
| 121 | +针对 `postgre` 与 `paradedb`,在启动前检查对应数据目录中是否存在 `PG_VERSION`。若存在,输出明确提示: |
| 122 | + |
| 123 | +- 当前用户名、密码、库名配置仅影响新初始化实例 |
| 124 | +- 已有数据目录不会自动迁移内部角色、密码或默认库 |
| 125 | +- 如需变更旧实例内部状态,需要手工迁移或重新初始化数据目录 |
| 126 | + |
| 127 | +该提示只负责澄清行为,不阻止启动。 |
| 128 | + |
| 129 | +### 6. 调试与可观测性 |
| 130 | + |
| 131 | +当用户需要排查来源时,脚本应能基于配置解析器返回的 `Sources` / `Trace` 输出信息,例如: |
| 132 | + |
| 133 | +- `DEFAULT_USER` 最终来自 `-Env` |
| 134 | +- `COMPOSE_PROJECT_NAME` 最终来自 `.env.local` |
| 135 | +- `DEFAULT_PASSWORD` 最终来自服务默认值 |
| 136 | + |
| 137 | +第一版可先提供内部结构和测试,不强制在常规输出中展示所有 trace;后续需要时可扩展为 `-Verbose` 或专门调试输出。 |
| 138 | + |
| 139 | +## Priority Rules |
| 140 | + |
| 141 | +配置优先级不在解析器内部写死,而由调用方传入。 |
| 142 | + |
| 143 | +对 `start-container.ps1` 的默认推荐顺序为: |
| 144 | + |
| 145 | +1. CLI 显式参数与 `-Env` |
| 146 | +2. `.env.local` |
| 147 | +3. `.env` |
| 148 | +4. 服务默认值 |
| 149 | +5. `ProcessEnv`(仅普通服务启用;数据库服务默认关闭) |
| 150 | + |
| 151 | +其中数据库服务通过关闭 `ProcessEnv` 来源来避免历史会话污染,而不是彻底放弃 `DEFAULT_*` 兼容性。 |
| 152 | + |
| 153 | +## Testing Strategy |
| 154 | + |
| 155 | +### `psutils` 配置解析器 |
| 156 | + |
| 157 | +- `.env` 与 `.env.local` 的优先级覆盖 |
| 158 | +- JSON 与 env 文件的覆盖顺序 |
| 159 | +- `ProcessEnv` 关闭时不参与解析 |
| 160 | +- `Trace` 正确记录最终命中来源 |
| 161 | +- 缺失文件的默认忽略行为 |
| 162 | + |
| 163 | +### `psutils` 作用域环境执行器 |
| 164 | + |
| 165 | +- 正常执行后恢复环境 |
| 166 | +- 异常执行后仍恢复环境 |
| 167 | +- 原本不存在的变量在退出后被移除 |
| 168 | + |
| 169 | +### `start-container.ps1` |
| 170 | + |
| 171 | +- `postgre` / `paradedb` 不再读取历史会话残留 `DEFAULT_*` |
| 172 | +- `postgre` / `paradedb` 继续接受 `.env`、`.env.local`、`-Env`、CLI 参数中的 `DEFAULT_*` |
| 173 | +- 普通服务继续保留当前通用变量行为 |
| 174 | +- 检测到 `PG_VERSION` 时输出迁移提示 |
| 175 | +- 现有默认用户测试按新优先级更新 |
| 176 | + |
| 177 | +## Compatibility and Migration |
| 178 | + |
| 179 | +此改动属于脚本行为收敛,不是数据迁移: |
| 180 | + |
| 181 | +- 普通服务现有使用方式基本保持不变 |
| 182 | +- `postgre` / `paradedb` 的命令行和 `.env` 用法保持不变 |
| 183 | +- 唯一被刻意收紧的是数据库服务对“历史会话残留环境”的读取行为 |
| 184 | + |
| 185 | +因此: |
| 186 | + |
| 187 | +- 依赖 shell 中长期保留 `DEFAULT_USER` 驱动数据库服务的用法会变化 |
| 188 | +- 通过本次调用的 `-Env`、`.env`、`.env.local`、CLI 参数显式提供值的用法不受影响 |
| 189 | + |
| 190 | +## Risks |
| 191 | + |
| 192 | +- 如果配置解析器抽象过重,可能增加脚本理解成本。 |
| 193 | +- 如果数据库服务与普通服务的优先级差异没有写清楚,可能造成新的认知偏差。 |
| 194 | +- 如果测试只覆盖解析器,不覆盖 `start-container.ps1` 接入层,仍可能出现“解析正确、接入错误”的问题。 |
| 195 | + |
| 196 | +对应缓解方式: |
| 197 | + |
| 198 | +- 第一版只支持 env/json/process hashtable 四类来源 |
| 199 | +- 明确把数据库服务特例写进文档与测试 |
| 200 | +- 在 `psutils` 与 `start-container.ps1` 两层同时加测试 |
| 201 | + |
| 202 | +## Recommended Outcome |
| 203 | + |
| 204 | +本次推荐采用: |
| 205 | + |
| 206 | +- `psutils` 新增通用配置解析器 |
| 207 | +- `psutils` 新增通用作用域环境执行器 |
| 208 | +- `start-container.ps1` 改为“解析本次调用配置,再局部注入环境执行 compose” |
| 209 | +- `postgre` / `paradedb` 继续兼容 `DEFAULT_*`,但只接受本次调用显式来源 |
| 210 | +- 新增旧 `PGDATA` 检测提示与来源追踪测试 |
| 211 | + |
| 212 | +这样既能修掉当前 `start-container.ps1` 的环境污染问题,又不会把常用的通用变量模型整体推翻。 |
0 commit comments