合并 master 的预览部署控制面
保留 Spine 序列帧与 Jenkins 容器预览决策记录 合入 preview-deployer-web、preview-deployer-server 及部署脚本
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
# Jenkins 容器预览部署控制面技术方案
|
||||
|
||||
日期:`2026-08-15`
|
||||
|
||||
## 目标
|
||||
|
||||
为内网同事提供独立 SPA,用于填写源码分支和可选 commit,触发 Jenkins 构建并发布完整 Docker Compose 预览栈。页面持续刷新排队、构建、发布和卸载状态,成功后展示容器健康状态与 Web 内网地址。
|
||||
|
||||
控制面不替代 Jenkins:Jenkins 继续负责源码检出、全量镜像构建、SpacetimeDB 模块发布、容器启动、健康验证、卸载和构建日志。浏览器不得持有 Jenkins 用户名、API Token、Crumb 或 Docker 权限。
|
||||
|
||||
## 架构
|
||||
|
||||
```text
|
||||
同事浏览器
|
||||
-> preview-deployer SPA
|
||||
-> preview-deployer-server 同源 API
|
||||
-> 固定 Jenkins Job shared/Genarrative-Preview-Deployer
|
||||
-> 独立 Compose project + 端口租约 + preview-result.json
|
||||
```
|
||||
|
||||
- SPA 只通过内网 `http://192.168.35.82/build/` 提供页面,API 使用同源 `/api/preview-deployer/`;不配置公网域名,不复用后台管理员 Token 或业务 API 登录态。
|
||||
- `preview-deployer-server` 只代理固定 Jenkins Job,Jenkins 凭据只从服务端环境变量读取,不进入前端 bundle、JSON 响应或日志。
|
||||
- Jenkins Job 使用仓库现有 `deploy/container/` 全量容器资产,并通过独立 Compose project 支持多个分支实例并存;为保护端口和共享 Docker 状态,构建动作在首版中串行排队。
|
||||
- 页面只读取控制面归一化后的状态,不解析 Jenkins console,也不直连 Jenkins API。
|
||||
|
||||
## 部署身份与端口
|
||||
|
||||
`deploymentId` 由规范化分支名与分支名摘要确定,同一分支稳定得到同一 ID;commit 不进入 ID,因此同一分支重新构建或指定不同 commit 时复用同一预览实例和 Web 端口。
|
||||
|
||||
Web 端口池固定为 `8400..8499`:
|
||||
|
||||
1. Jenkins 在全局文件锁内读取受控状态目录。
|
||||
2. 已登记分支复用原端口;新分支选择状态表、宿主监听和 Docker 映射均未占用的端口。
|
||||
3. Jenkins 为实例使用独立 Compose project,例如 `genarrative-preview-a81f39c2d43e76ab`。
|
||||
4. 端口租约必须在容器完成真实绑定前持续受锁保护;失败构建不得把端口分配给第二个实例。
|
||||
5. 卸载成功后删除该实例的 Compose 栈、受控状态和端口租约。
|
||||
|
||||
同一分支重新构建时先完成新镜像构建,再停止旧 Compose 栈并启动新版本;源码或镜像构建失败不会提前删除原有健康实例。容器切换后的失败仍会在页面明确显示,不伪装成旧版本继续运行。
|
||||
|
||||
页面每次刷新运行中实例时,控制服务直接探测受控 Web URL;后续容器异常会将健康状态更新为 `unhealthy`,不会永久沿用 Jenkins 部署完成时的快照。
|
||||
|
||||
目标分支只提供应用构建上下文。Compose 编排文件、Dockerfile 入口和 Jenkins 执行脚本固定取自受保护的 `master` 控制器 checkout,避免普通分支替换编排文件挂载宿主路径或启用特权容器。该 Job 仍只应开放给受信任开发者,并建议在隔离构建节点运行。
|
||||
|
||||
SpacetimeDB 与 OTLP 不映射宿主端口;Jenkins 通过受控 Compose 网络中的 SpacetimeDB 容器地址完成模块发布,运行服务之间继续使用 Compose DNS。页面只展示 Web 内网地址 `http://<预览宿主>:<webPort>`。
|
||||
|
||||
## Jenkins 参数与产物
|
||||
|
||||
固定 Job:`shared/Genarrative-Preview-Deployer`。
|
||||
|
||||
参数:
|
||||
|
||||
- `ACTION`:`DEPLOY`、`UNINSTALL` 或 `STATUS`。
|
||||
- `SOURCE_BRANCH`:部署时必填。
|
||||
- `COMMIT_HASH`:部署时可选,7 到 40 位十六进制。
|
||||
- `DEPLOYMENT_ID`:卸载和状态查询时必填。
|
||||
|
||||
分支名只允许数字、字母、点、下划线、短横线和斜杠,不允许首尾斜杠或连续点号。Jenkins 必须复用 `scripts/jenkins-checkout-source.sh`,验证 commit 真实属于目标远端分支;SPA 和代理服务的输入校验不能代替流水线校验。
|
||||
|
||||
每次运行归档 `preview-result.json`,至少包含:
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"deploymentId": "feature-login-a81f39c2",
|
||||
"branch": "feature/login",
|
||||
"requestedCommit": "",
|
||||
"resolvedCommit": "0123456789abcdef",
|
||||
"status": "running",
|
||||
"health": "healthy",
|
||||
"webPort": 8403,
|
||||
"webUrl": "http://192.168.35.82:8403",
|
||||
"composeProject": "genarrative-preview-feature-login-a81f39c2",
|
||||
"updatedAt": "2026-08-15T08:00:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
控制面只读取固定 artifact 路径和受限字段;不得把完整 console 内容返回浏览器。
|
||||
|
||||
## 控制面 API
|
||||
|
||||
- `POST /api/preview-deployer/session`:使用控制面访问口令建立 `HttpOnly + SameSite=Strict` 会话。
|
||||
- `GET /api/preview-deployer/session`:查询当前会话状态。
|
||||
- `DELETE /api/preview-deployer/session`:退出。
|
||||
- `GET /api/preview-deployer/deployments`:列出由控制面触发和恢复的部署。
|
||||
- `POST /api/preview-deployer/deployments`:提交 `{ branch, commitHash? }`。
|
||||
- `GET /api/preview-deployer/deployments/{id}`:刷新队列、构建与 artifact 状态。
|
||||
- `POST /api/preview-deployer/deployments/{id}/uninstall`:触发固定 Job 的卸载动作。
|
||||
|
||||
页面状态统一为 `queued / building / deploying / running / uninstalling / stopped / failed / cancelled`,健康状态统一为 `pending / healthy / unhealthy / unknown`。
|
||||
|
||||
## 安全边界
|
||||
|
||||
- 服务端缺少控制面访问口令或 Jenkins service account 凭据时必须拒绝启动,不允许退化成匿名写接口。
|
||||
- Jenkins service account 只授予 `shared/Genarrative-Preview-Deployer` 的 `Job/Read`、`Job/Build` 和读取构建产物所需权限,不授 `Overall/Administer`、`Job/Configure` 或 `Job/Delete`。
|
||||
- 后端固定 Jenkins origin、Job 路径和参数白名单;客户端不能传 URL、Job 名、Compose project、容器名、宿主端口或 Jenkins 凭据。
|
||||
- Jenkins POST 支持动态 Crumb;API Token 即使免 Crumb,也不能把 Token 放进 URL 或日志。
|
||||
- API 默认只接受同源请求,写请求校验 Origin;内网本身不作为认证。
|
||||
- 同一 deployment 的发布和卸载串行执行;重复请求必须幂等或明确返回冲突。
|
||||
- 卸载只接受受控状态中存在且 ID 完全匹配的实例,并进行二次确认;禁止执行任意 Docker、Git、shell 或 Compose project 参数。
|
||||
|
||||
## 验收
|
||||
|
||||
- 后端:输入校验、登录会话、Origin、Crumb、Jenkins `401/403/404/5xx`、queue 到 build 状态机、artifact schema、卸载所有权和幂等测试。
|
||||
- 前端:登录、分支与可选 commit、自动刷新、排队/构建/成功/失败状态、内网链接、卸载确认和刷新恢复测试。
|
||||
- Jenkins:两个分支依次发布后在不同端口并存;同一分支换 commit 优先复用端口;非分支 commit 被拒绝;卸载只删除目标实例并释放端口。
|
||||
- 通用:`npm run check:encoding`、相关 typecheck/build/test、Rust 定向测试和 `git diff --check`。
|
||||
Reference in New Issue
Block a user