feat(deploy): 在线更新 - 新增后台检查、强制备份迁移与失败自动回退
This commit is contained in:
@@ -5,7 +5,15 @@ description: 影策后端核心持久化表与状态边界
|
||||
|
||||
# 后端数据库
|
||||
|
||||
后端通过 GORM 管理 SQLite 与 PostgreSQL 表结构。服务启动时由 `backend/internal/database/schema.go` 执行迁移;业务写路径必须通过 service 和 repository 的状态约束,不能直接依赖前端状态。
|
||||
后端通过 GORM 管理 SQLite 与 PostgreSQL 表结构。结构变化由 `migrate-schema` 按版本顺序执行并写入 `schema_migrations`;受管生产部署在 Backend 启动前运行独立迁移服务,Backend 只校验当前 Schema 是否与二进制兼容。业务写路径必须通过 service 和 repository 的状态约束,不能直接依赖前端状态。
|
||||
|
||||
## 结构迁移与版本
|
||||
|
||||
| 表 | 用途 | 关键边界 |
|
||||
| --- | --- | --- |
|
||||
| `schema_migrations` | 保存已成功应用的结构版本、名称、校验和与时间 | 已应用版本的校验和发生变化时拒绝启动;数据库版本高于当前二进制支持版本时拒绝旧程序连接 |
|
||||
|
||||
容器内命令 `migrate-schema up` 应用缺失迁移,`migrate-schema status` 输出当前和期望版本,`migrate-schema verify` 只检查兼容性。`CANVAS_AUTO_MIGRATE=true` 仅用于开发和旧部署兼容;受管生产部署设置为 `false`,并由 Compose 的一次性 `migrate` 服务先完成迁移。后续迁移必须新增版本,不得修改已经发布迁移的实现或校验和。
|
||||
|
||||
## 技能包与版本
|
||||
|
||||
|
||||
13
docs/content/docs/backend/meta.json
Normal file
13
docs/content/docs/backend/meta.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"title": "开发文档",
|
||||
"root": true,
|
||||
"defaultOpen": true,
|
||||
"pages": [
|
||||
"local-development",
|
||||
"code-map",
|
||||
"backend-database",
|
||||
"system-update",
|
||||
"protocol-plugins",
|
||||
"canvas-data-structure"
|
||||
]
|
||||
}
|
||||
60
docs/content/docs/backend/system-update.mdx
Normal file
60
docs/content/docs/backend/system-update.mdx
Normal file
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: 在线更新与安全回退
|
||||
description: Host Updater、Release 检查、数据库备份迁移、健康验证与异常回退机制。
|
||||
---
|
||||
|
||||
# 在线更新与安全回退
|
||||
|
||||
管理后台的“系统配置 → 系统更新”只负责管理员鉴权、展示状态和发起操作。真正的 Docker、数据库备份与服务切换由宿主机上的 `open-ai-canvas-host-updater` 执行,业务后端不挂载 Docker Socket。
|
||||
|
||||
```text
|
||||
管理员浏览器 → Backend 管理 API → 带 Token 的 Unix Socket → Host Updater → Docker Compose
|
||||
```
|
||||
|
||||
## 安全更新顺序
|
||||
|
||||
1. 从 GitHub Releases 读取包含预览版在内的最高语义版本,并与 `.env` 中固定的 `CANVAS_IMAGE_TAG` 比较。
|
||||
2. 下载目标版本的 `docker-compose.deploy.yml`,执行 `docker compose config --quiet`,检查部署文件、磁盘空间和当前版本。
|
||||
3. 创建 `backup-YYYYMMDD-HHMMSS.zip.partial`,其中包含 PostgreSQL custom-format dump、后端数据目录 tar 和元数据;写入完成后 `fsync`、原子改名,并复核 ZIP 结构和 SHA-256。
|
||||
4. 拉取目标 backend/web 镜像并要求存在仓库 `sha256` 摘要。任何失败都在停服前中止。
|
||||
5. 停止 web/backend,等待 Backend 的 SIGTERM drain 完成;PostgreSQL 和 Redis 保持运行。
|
||||
6. 用目标 backend 镜像独立执行 `migrate-schema up`。整批迁移在事务和 PostgreSQL advisory lock 内完成,迁移名称与 checksum 必须匹配。
|
||||
7. 原子更新 `CANVAS_IMAGE_TAG` 和 Compose 文件,启动目标版本。
|
||||
8. 连续检查 `/api/health/ready` 的数据库、运行时、schema 和 build version,并经过稳定窗口后才标记成功。
|
||||
|
||||
更新状态写入 `/var/lib/open-ai-canvas-updater/state.json`。Backend 或浏览器在切换期间断线不会终止宿主机操作,恢复后可继续读取日志。
|
||||
|
||||
## 自动回退
|
||||
|
||||
停服后的迁移、启动或健康验证失败会触发自动回退:
|
||||
|
||||
1. 停止目标 web/backend;
|
||||
2. 恢复更新前保存的 Compose 文件;
|
||||
3. 再次校验备份 ZIP 和 SHA-256;
|
||||
4. 使用 `pg_restore --clean --if-exists` 恢复 PostgreSQL;
|
||||
5. 恢复旧 `CANVAS_IMAGE_TAG`,启动旧服务并重新进行稳定窗口验证。
|
||||
|
||||
回退任一步失败时状态变为 `manual_intervention`,不伪造成功。管理后台仅在存在旧版本和已校验备份时开放人工回退按钮。
|
||||
|
||||
## 安装与升级约束
|
||||
|
||||
GHCR 镜像部署必须把 `CANVAS_IMAGE_TAG` 固定到具体 Release,不可使用 `latest`。安装更新器:
|
||||
|
||||
```bash
|
||||
cd /opt/open-ai-canvas
|
||||
curl -fsSL https://raw.githubusercontent.com/ddcat-ai/open-ai-canvas/main/scripts/install-host-updater.sh | sudo bash
|
||||
sudo docker compose --env-file .env -f docker-compose.deploy.yml up -d --force-recreate backend web --wait
|
||||
```
|
||||
|
||||
脚本会下载与当前 Release 匹配的 Linux amd64/arm64 二进制和 `SHA256SUMS`,生成随机 `CANVAS_UPDATER_TOKEN`,创建 systemd 服务、Socket 目录、状态目录和 `backups/`。Token 只保存在服务器 `.env` 与 root-only 更新器环境文件中。
|
||||
|
||||
## 运维检查
|
||||
|
||||
```bash
|
||||
sudo systemctl status open-ai-canvas-updater
|
||||
sudo journalctl -u open-ai-canvas-updater -n 200 --no-pager
|
||||
sudo ls -lh /opt/open-ai-canvas/backups
|
||||
sudo sha256sum /opt/open-ai-canvas/backups/*.zip
|
||||
```
|
||||
|
||||
备份文件包含业务数据,应限制为 root 访问并纳入异机备份策略。数据卷本身不是备份。
|
||||
@@ -5,6 +5,15 @@ description: 影策已实现功能的手工验证清单与未确认边界
|
||||
|
||||
# 待测试
|
||||
|
||||
## 服务升级运行基础
|
||||
|
||||
- 完整架构、状态机、Release 清单、备份和回退边界见 `docs/design/dynamic-update.md`。
|
||||
- [x] 后端新增权威 BuildInfo、`schema_migrations`、独立 `migrate-schema` 命令、分层健康检查以及可停止 Worker;专项 Go 测试通过。
|
||||
- [x] 生产 Compose 在 Backend 前运行一次性迁移服务,并使用 `CANVAS_AUTO_MIGRATE=false` 阻止业务进程隐式修改 Schema;Backend 收到 SIGTERM 后进入 drain、停止领取任务并等待活动任务。
|
||||
- [ ] 使用 PostgreSQL 部署执行一次无迁移启动和一次跨版本迁移,确认 migrate 成功后 Backend 才启动,checksum 漂移和高版本 Schema 均会阻止旧 Backend 就绪。
|
||||
- [ ] 在存在图片、视频和文本长任务时发送 SIGTERM,确认 readiness 立即返回 503、新任务被拒绝、已领取任务在宽限期内完成,超时后租约可由重启实例恢复。
|
||||
- [ ] 构建带版本参数的前后端镜像,确认 `/api/system/version`、管理后台版本、OCI Commit 和 Release Tag 一致。
|
||||
|
||||
## 素材库网格密度
|
||||
|
||||
- [x] 素材库桌面端缩小卡片并改为每行 6 张;新增素材占首格,默认每页 35 项,可切换 70/105 项,首屏正好组成 6×6 完整网格;中窄屏按 4/3/1 列收敛,前端生产构建通过。
|
||||
@@ -501,6 +510,19 @@ description: 影策已实现功能的手工验证清单与未确认边界
|
||||
- [ ] 确认浅色/深色主题下标题、分类选中态、搜索焦点环、键帽和滚动区域清晰可见。
|
||||
- [ ] 在窄窗口确认分类可横向滚动、快捷键列表改为单列,弹窗不超出视口且长内容仅在列表区滚动。
|
||||
|
||||
## 管理后台在线更新
|
||||
|
||||
管理后台已新增“系统配置 → 系统更新”,并通过宿主机 Host Updater 执行强制备份、迁移、切换、健康验证和失败回退;以下场景需要在真实 Linux Docker Compose 服务器完成最终确认:
|
||||
|
||||
- [x] 后端 Host Updater、Unix Socket 客户端、管理员 API、版本比较和状态持久化相关测试通过。
|
||||
- [x] 前端 TypeScript 检查、生产构建、侧栏入口、更新器未安装空态、更新器连接态、GitHub Release 日志、浅色/深色主题浏览器验收通过。
|
||||
- [x] Compose 配置校验、systemd 安装脚本语法和 Release 二进制构建命令通过。
|
||||
- [ ] 在生产更新前生成 PostgreSQL + 数据目录 ZIP,确认 `.partial` 不会被当作有效备份,最终 ZIP 可打开且 SHA-256、元数据、`database.dump`、`backend-data.tar` 均有效。
|
||||
- [ ] 从首个在线更新版本升级到包含 schema v2 的测试版本,确认旧服务先 drain,迁移服务独占执行,目标 build version 和 schema version 在稳定窗口内一致。
|
||||
- [ ] 人为让目标版本健康检查失败,确认自动恢复旧 Compose、旧镜像和数据库备份;旧版本因 schema 过高拒绝启动时,必须在数据库恢复完成后才能重新健康。
|
||||
- [ ] 在拉取镜像、创建备份、迁移、启动和健康检查阶段分别模拟失败,确认停服前失败不会改变运行版本,停服后失败进入回退,回退失败明确显示“需人工介入”。
|
||||
- [ ] 更新期间刷新页面、关闭浏览器和 Backend 短暂不可用,确认宿主机操作不中断,恢复后日志与最终状态完整。
|
||||
|
||||
## 管理后台存储资源
|
||||
|
||||
管理后台已新增资源列表、筛选、去重容量统计、预览下载和安全批量删除;以下场景仍需使用真实资源数据确认:
|
||||
|
||||
127
docs/design/dynamic-update.md
Normal file
127
docs/design/dynamic-update.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# 服务动态更新设计
|
||||
|
||||
## 目标与边界
|
||||
|
||||
动态更新面向使用官方 Docker Compose 部署的自托管实例。管理员可在后台查看当前版本、GitHub Release 新版本和更新日志,并发起一次受控升级。升级必须满足:同一时刻只有一个更新任务、数据库和数据目录有可恢复备份、迁移失败不启动新服务、健康检查失败自动恢复旧镜像、业务 Backend 不直接持有 Docker Socket。
|
||||
|
||||
源码部署、Kubernetes、Portainer 或用户自行修改过编排文件的实例第一阶段只提供版本检查和手工升级指引,不自动接管宿主机。
|
||||
|
||||
## 组件边界
|
||||
|
||||
```text
|
||||
管理后台
|
||||
-> Backend 更新 API(鉴权、状态展示、审计)
|
||||
-> Host Updater(宿主机最小权限守护进程)
|
||||
-> GitHub Release / OCI Registry
|
||||
-> Docker Compose
|
||||
-> PostgreSQL / 数据卷备份
|
||||
```
|
||||
|
||||
- Backend 负责管理员权限、Release 信息缓存、更新任务展示和审计,不执行 Docker 命令。
|
||||
- Host Updater 负责下载、验签、备份、迁移、切换、健康验证和回退;只接受本机 Unix Socket 或随机高强度 Token 鉴权请求。
|
||||
- `migrate-schema` 是唯一结构迁移入口。生产 Backend 使用 `CANVAS_AUTO_MIGRATE=false`,只校验 Schema 与当前二进制兼容。
|
||||
- 官方 Release 发布不可变镜像摘要和更新清单;Updater 不执行 Release 文本中的任意命令或脚本。
|
||||
|
||||
## Release 清单
|
||||
|
||||
每个正式 Release 附带机器可读的 `update-manifest.json`,至少包含:
|
||||
|
||||
| 字段 | 用途 |
|
||||
| --- | --- |
|
||||
| `version`、`channel`、`publishedAt` | 版本比较和稳定版/测试版通道 |
|
||||
| `minimumUpdaterVersion` | 阻止旧 Updater 执行不理解的新协议 |
|
||||
| `images` | Web、Backend 镜像及不可变 digest |
|
||||
| `schemaVersion`、`minimumRollbackSchemaVersion` | 升级前兼容性和可回退性判断 |
|
||||
| `composeRevision` | 确认当前部署模板是否受支持 |
|
||||
| `releaseNotesUrl` | 后台展示更新日志 |
|
||||
| `signature` | 对规范化清单做签名,防止供应链篡改 |
|
||||
|
||||
版本比较使用语义化版本,正式通道默认忽略 prerelease。GitHub API 使用 ETag 和超时缓存,网络失败只显示“检查失败”,不会影响当前服务。
|
||||
|
||||
## 升级状态机
|
||||
|
||||
```text
|
||||
idle -> checking -> preflight -> draining -> backup -> pulling
|
||||
-> migrating -> switching -> verifying -> succeeded
|
||||
| | |
|
||||
+---------- failed --------+
|
||||
|
|
||||
rolling_back
|
||||
|
|
||||
rolled_back / manual_intervention
|
||||
```
|
||||
|
||||
更新任务和每一步结果持久化到宿主机,不只保存在正在被替换的 Backend 中。Updater 使用文件锁保证单实例执行,并为每次更新生成 operation ID,重复点击只返回同一任务。
|
||||
|
||||
### 预检
|
||||
|
||||
1. 校验管理员请求、更新通道、Release 签名、镜像 digest 和磁盘空间。
|
||||
2. 确认当前 Compose revision、数据目录、数据库连接和备份目录可访问。
|
||||
3. 检查当前 Schema、目标 Schema 与回退兼容窗口;不满足时在停机前拒绝更新。
|
||||
4. 拉取目标镜像但不切换,确认目标 Backend 内含期望版本的 `migrate-schema`。
|
||||
|
||||
### 排空与备份
|
||||
|
||||
Updater 先让 Backend 进入 drain。Readiness 立即变为 503,新生成任务返回可重试错误;周期 Worker 停止领取任务,已执行任务在 `CANVAS_SHUTDOWN_TIMEOUT` 内完成。随后执行 PostgreSQL 一致性备份、SQLite 文件快照和数据目录增量快照,并记录备份校验和。备份失败不得继续迁移。
|
||||
|
||||
### 迁移与切换
|
||||
|
||||
目标镜像以一次性容器执行 `migrate-schema up`。迁移整批运行在事务中,PostgreSQL 使用事务级 advisory lock;迁移实现与 `schema_migrations` 记录的名称或 checksum 不一致时立即失败。迁移成功后才切换 Web/Backend 镜像,并保持旧镜像 digest 与旧 Compose 配置可用。
|
||||
|
||||
### 健康验证
|
||||
|
||||
切换后依次验证:
|
||||
|
||||
1. `/api/health/startup` 成功;
|
||||
2. `/api/health/ready` 连续多次成功;
|
||||
3. `/api/system/version` 与目标 Release、Schema 版本一致;
|
||||
4. 可选的登录态只读冒烟检查成功。
|
||||
|
||||
单次 200 不视为升级成功。只有在稳定窗口内连续通过后才标记成功并清理临时资源。
|
||||
|
||||
## 回退策略
|
||||
|
||||
回退分为两类:
|
||||
|
||||
- **镜像回退**:目标版本未修改 Schema,或新 Schema 仍兼容旧 Backend,直接恢复旧镜像和 Compose 配置并再次验证健康状态。
|
||||
- **数据回退**:迁移包含破坏性或不可向后兼容变更,停止所有业务容器后恢复数据库和数据目录快照,再启动旧镜像。该过程会丢弃升级开始后的新写入,因此升级期间必须保持写流量关闭。
|
||||
|
||||
迁移设计默认采用 expand/contract:先新增字段/表并保持旧版本可读,至少跨一个发布周期后再清理旧结构。无法做到向后兼容的 Release 必须在清单中标记维护窗口和数据回退要求,后台需要二次确认。自动回退失败时保持服务停止并输出明确的备份位置、失败步骤和人工恢复命令,不反复重试破坏现场。
|
||||
|
||||
## 后台能力
|
||||
|
||||
第一阶段后台提供:当前版本、目标版本、发布时间、更新日志、通道、检查时间、升级前检查结果、更新进度、失败原因、回退结果和审计记录。只有超级管理员可发起更新;普通管理员只读。危险状态不提供“强制跳过迁移/备份/验签”入口。
|
||||
|
||||
建议 API:
|
||||
|
||||
- `GET /api/admin/system/update-status`
|
||||
- `POST /api/admin/system/check-update`
|
||||
- `POST /api/admin/system/update`
|
||||
- `GET /api/admin/system/update-operations/:id`
|
||||
- `POST /api/admin/system/update-operations/:id/rollback`
|
||||
|
||||
## 分阶段实施
|
||||
|
||||
### P0:可升级运行基础
|
||||
|
||||
- 权威 BuildInfo 与 `/api/system/version`;
|
||||
- 分层健康检查和 Worker drain;
|
||||
- 版本化、事务化的 `schema_migrations` 与独立迁移命令;
|
||||
- 生产 Compose 的 migrate job、停止宽限期和 Release 版本门禁。
|
||||
|
||||
### P1:只读检查更新
|
||||
|
||||
- Backend 缓存 GitHub Release/manifest;
|
||||
- 管理后台展示版本、更新日志、部署类型和自动更新可用性;
|
||||
- 无 Host Updater 的部署只展示手工更新指引。
|
||||
|
||||
### P2:Host Updater 与手动升级
|
||||
|
||||
- 独立最小权限进程、操作锁、持久化状态、备份、镜像切换和健康回退;
|
||||
- 后台实时展示进度,先在 PostgreSQL 官方 Compose 上灰度;
|
||||
- SQLite 和自定义数据目录在备份/恢复验证完成后开放。
|
||||
|
||||
### P3:自动调度
|
||||
|
||||
- 支持维护窗口、稳定版通道、失败通知和延迟发布;
|
||||
- 默认关闭无人值守升级;涉及破坏性迁移的版本始终要求人工确认。
|
||||
Reference in New Issue
Block a user