M8-R07: integrate WarmteLink device access into Compose

This commit is contained in:
2026-08-24 06:45:31 +02:00
parent 09abe05f66
commit c851bad829
7 changed files with 55 additions and 45 deletions
+15 -15
View File
@@ -4,12 +4,12 @@
## 安全边界与开始前备份
- 应用容器继续使用基础 `docker-compose.yml` 中的非 root `user: "1000:1000"`overlay 不设置 `user``privileged` 或额外 capability。
- Docker device cgroup 必须以 `rw` 映射,容器内固定为 `/dev/warmtelink`:这是 pyserial 3.5 在 POSIX 上以 `O_RDWR` 打开串口所需的最小系统权限,并不表示业务可写。规则绝不包含 `m`overlay 也不授予 root、`privileged` 或额外 capability。WarmteLink worker 仍只调用 serial `read` / `close`,绝不调用 `write` 或发送写命令。
- 应用容器继续使用基础 `docker-compose.yml` 中的非 root `user: "1000:1000"`基础文件和 dev 合并配置都不设置 `privileged` 或额外 capability。
- Docker device cgroup 必须以 `rw` 映射,容器内固定为 `/dev/warmtelink`:这是 pyserial 3.5 在 POSIX 上以 `O_RDWR` 打开串口所需的最小系统权限,并不表示业务可写。规则绝不包含 `m`不授予 root、`privileged` 或额外 capability。WarmteLink worker 仍只调用 serial `read` / `close`,绝不调用 `write` 或发送写命令。
- 不删除或覆盖 `app_config``app.db`、旧数据库、Docker volume 或既有 source。禁用/解绑/回滚配置不是删除历史的替代方式。
- 维护前停止写入窗口,使用宿主机的备份流程复制 `./data/app.db` 到受保护的备份位置;确认备份可用后才运行 migration。不要把生产库复制到开发机或用于测试。
## 识别稳定串口并启用 overlay
## 识别稳定串口并配置 Compose
在宿主机(不是容器)找出稳定 symlink;不要使用会在重启后变化的 `/dev/ttyUSB*` 名称:
@@ -19,29 +19,30 @@ stable_path=/dev/serial/by-id/<stable-by-id-name>
stat -c '%g %n' "$stable_path"
```
记录输出的数字 GID,而不是猜测 `dialout` 的数值。确认 path 指向预期的字符设备后,临时导出变量
记录输出的数字 GID,而不是猜测 `dialout` 的数值。确认 path 指向预期的字符设备后,在部署机本地 `.env`(不提交)设置
```bash
export WARMTELINK_DEVICE_PATH=/dev/serial/by-id/<stable-by-id-name>
export WARMTELINK_SERIAL_GID=<host-serial-gid>
docker compose -f docker-compose.yml -f docker-compose.warmtelink.yml config
docker compose -f docker-compose.yml -f docker-compose.warmtelink.yml up -d
WARMTELINK_DEVICE_PATH=/dev/serial/by-id/<stable-by-id-name>
WARMTELINK_SERIAL_GID=<host-serial-gid>
```
`config``app` 必须仍显示 `user: "1000:1000"`,并只出现从 stable by-id path 到 `/dev/warmtelink``rw` device mapping 与 `group_add` GID;不得出现 `privileged`、root user 或 `m` device permission。`rw` 仅满足 pyserial 的 POSIX `O_RDWR` 打开,不改变 worker 的只读业务行为。默认部署不带第二个 `-f`
production 使用基础 composelocal dev 使用 base 与 dev 合并文件。两种环境都会从本地 `.env` 读取这两个变量
```bash
docker compose -f docker-compose.yml up -d
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build
```
因此无串口硬件时默认 stack 不会声明不存在的 device。不得把设备路径或 GID 写回 `docker-compose.warmtelink.yml``.env.example` 或文档中的真实值
启动前可用相同文件组合运行 `docker compose ... config`。结果的 `app` 必须仍显示 `user: "1000:1000"`,并只出现从 stable by-id path 到 `/dev/warmtelink``rw` device mapping 与 `group_add` GIDmigration 不得有 device 或 group。不得出现 `privileged`、root user 或 `m` device permission。`rw` 仅满足 pyserial 的 POSIX `O_RDWR` 打开,不改变 worker 的只读业务行为。不得把设备路径或 GID 写入仓库的 `.env.example` 或文档。
同一物理串口在任意时刻只能有一个 owner。运行 Pre-M8 `p1_probe.py` 前,必须先停止 app(包括 dev stack),并在 probe 结束后再启动 app;不要让 probe 与 worker 同时打开该串口。
## Migration 与 source 配置
先在维护窗口运行 migration;它只升级 schema,绝不删除历史表或配置:
```bash
docker compose -f docker-compose.yml -f docker-compose.warmtelink.yml run --rm migration
docker compose -f docker-compose.yml run --rm migration
```
登录 Energy 页面,在 **Sources** 创建 `warmtelink_serial` source
@@ -81,14 +82,13 @@ docker compose -f docker-compose.yml -f docker-compose.warmtelink.yml run --rm m
## 安全回滚
1. 在 UI 禁用 WarmteLink source,确认状态离线且 worker 已停止;保留 channels、bindings、history、contracts 与成本账本。
2. 停止 overlay stack 后,以默认 compose 启动:`docker compose -f docker-compose.yml up -d`。这会移除容器内 device 映射,但不会删除 `./data`、数据库、配置或 volumes
3. 确认 DSMR、Modbus、电价、既有 electricity 成本和前端正常。若需要恢复 WarmteLink,按上面的 stable by-id/GID 步骤再次启用 overlay 和 source。
2. 停止 app 后,在 UI 保持 source 禁用;这不会删除 `./data`、数据库、配置或 volumes。需要恢复 WarmteLink 时,确认本地 `.env` 的 stable by-id/GID 后再重新启用 source
3. 确认 DSMR、Modbus、电价、既有 electricity 成本和前端正常。
4. schema migration 不应以 production downgrade 回滚;只有经过验证的备份恢复流程才处理灾难恢复,且必须由 operator 在隔离维护窗口执行。
## 上线验收清单
- 默认 compose 在无 serial 硬件时正常启动
- overlay 合并后 app/migration 均为非 root;只有 app 有 `/dev/warmtelink:rw` 和 serial GID,绝无 `m`、root 或 privileged。该 `rw` 仅为 pyserial 的 `O_RDWR` 打开,worker 业务仍只读,且没有真实设备/GID 被记录在仓库。
- production 与 base+dev Compose 都从本地 `.env` 获取 serial path/GIDapp/migration 均为非 root,只有 app 有 `/dev/warmtelink:rw` 和 serial GID,绝无 `m`、root 或 privileged。该 `rw` 仅为 pyserial 的 `O_RDWR` 打开,worker 业务仍只读,且没有真实设备/GID 被记录在仓库
- 下列项目是交付后由用户在备份数据库、可回滚部署、真实 serial 设备和真实 HA 环境执行的人工验收;自动化技术验收不能替代这些观察,也不得把它们伪称为已执行。
### 用户人工验收记录(交付后填写)