From 879a53bf8dc365f64a30d61a2544a0684c6d5d15 Mon Sep 17 00:00:00 2001 From: kdletters <61648117+kdletters@users.noreply.github.com> Date: Sat, 2 May 2026 02:58:14 +0800 Subject: [PATCH] docs: add production deployment plan --- 生产部署计划.md | 316 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 生产部署计划.md diff --git a/生产部署计划.md b/生产部署计划.md new file mode 100644 index 00000000..c5f72169 --- /dev/null +++ b/生产部署计划.md @@ -0,0 +1,316 @@ +# 生产部署计划 + +更新时间:2026-05-02 + +## 目标 + +将当前部署方式调整为单机生产推荐方案:生产运行路径不使用 Docker,不再使用旧的一体化启动脚本,由 systemd 托管 SpacetimeDB 与 Rust `api-server`,由 Nginx 托管主站、后台前端与必要反向代理。 + +本计划用于重新创建 Jenkins 流水线、服务器环境配置、网站发布、`api-server` 发布、SpacetimeDB 模块发布,以及数据库人工导入导出流程。 + +## 生产架构 + +- Nginx 作为唯一公网入口,负责 HTTPS、静态站点、后台静态页面、维护页与 `/admin/api/` 反向代理。 +- SpacetimeDB 作为系统服务运行,监听 `127.0.0.1:3000`,数据根目录为 `/stdb`。 +- Rust `api-server` 作为系统服务运行,监听 `127.0.0.1:8082`,只被 Nginx 的 `/admin/api/` 访问。 +- 主站与后台前端构建为静态文件,发布到服务器固定目录,不放入 Jenkins 目录,也不跟随 Docker 镜像。 +- 除网站静态发布外,`api-server` 发布、SpacetimeDB 模块发布、数据库导入、服务器配置变更都必须先进入维护模式。 + +## 服务器目录 + +- `/opt/genarrative/releases//`:每次发布的完整版本目录。 +- `/opt/genarrative/current`:指向当前生效版本的软链接。 +- `/srv/genarrative/web`:指向 `/opt/genarrative/current/web`,供 Nginx 托管静态站点。 +- `/etc/genarrative/api-server.env`:`api-server` 生产环境变量文件。 +- `/var/lib/genarrative/maintenance/enabled`:维护模式开关文件。 +- `/stdb`:SpacetimeDB 程序、配置与数据根目录。 + +## 生产密钥 + +`/etc/genarrative/api-server.env` 中的生产密钥指所有只能存在于生产服务器、不能进入 Git、不能进入构建产物的敏感配置。典型内容包括: + +- LLM 或第三方服务 API Key。 +- 短信服务 Access Key 与 Secret。 +- 后台登录、会话、签名、加密相关密钥。 +- 生产 SpacetimeDB 地址与数据库名。 +- 只允许生产使用的回调地址、白名单或内部令牌。 + +该文件由服务器配置流水线或人工初始化创建,权限建议为 `root:genarrative`、`0640`。Jenkins 构建任务不能读取该文件;只有生产发布或服务启动需要读取。 + +## systemd 服务 + +### SpacetimeDB + +- 服务名:`spacetimedb.service` +- 运行用户:`spacetimedb` +- 工作目录:`/stdb` +- 启动命令:`/stdb/spacetime --root-dir=/stdb start --listen-addr=127.0.0.1:3000` +- 对外暴露:默认不直接暴露公网端口。 + +该方案与 SpacetimeDB 官方自托管文档一致:使用 Ubuntu、专用用户、`/stdb` 根目录、systemd 服务和 Nginx。 + +### api-server + +- 服务名:`genarrative-api.service` +- 运行用户:`genarrative` +- 工作目录:`/opt/genarrative/current` +- 可执行文件:`/opt/genarrative/current/api-server` +- 环境文件:`/etc/genarrative/api-server.env` +- 监听地址:`127.0.0.1:8082` + +`api-server` 不放入 Docker,也不直接暴露公网端口。发布时替换版本目录并重启 `genarrative-api.service`。 + +## Nginx 规则 + +只保留必要入口: + +- `/`:主站静态页面。 +- `/admin/`:后台前端静态页面,后台构建时使用 `/admin/` 作为 base path。 +- `/admin/api/`:反向代理到 `http://127.0.0.1:8082/admin/api/`。 +- HTTP 到 HTTPS:只保留 301 重定向。 +- `/maintenance.html`:维护中页面。 + +移除这些公网反向代理: + +- `/api/*` +- `/generated-*` +- 公网 `/healthz` +- 其他旧的一体化 web server 代理入口。 + +SpacetimeDB 公网路由默认保持收敛,只按实际前端 SDK 需要暴露最小集合。禁止开放可远程发布数据库或管理实例的通用入口。 + +## 维护模式 + +维护模式由 `/var/lib/genarrative/maintenance/enabled` 控制: + +- 文件存在:进入维护模式。 +- 文件不存在:退出维护模式。 + +行为: + +- 网站静态资源发布不进入维护模式。 +- `api-server` 发布、SpacetimeDB 模块发布、数据库导入、服务器配置变更必须进入维护模式。 +- 普通页面在维护模式下展示 `/maintenance.html`。 +- `/admin/api/*` 在维护模式下返回 503。 +- 静态资源仍允许访问,避免维护页样式和资源加载失败。 +- 发布成功后自动解除维护模式。 +- 发布失败时保持维护模式,并通过邮件通知人工处理。 + +## 构建产物 + +每次构建产物按版本号归档: + +```text +build// +├─ web/ +│ ├─ index.html +│ ├─ assets/ +│ ├─ maintenance.html +│ └─ admin/ +├─ api-server +├─ spacetime_module.wasm +├─ scripts/ +│ ├─ spacetime-publish-prod.sh +│ ├─ database-export.sh +│ ├─ database-import.sh +│ ├─ maintenance-on.sh +│ ├─ maintenance-off.sh +│ └─ maintenance-status.sh +├─ deploy/ +│ ├─ systemd/ +│ │ ├─ spacetimedb.service +│ │ └─ genarrative-api.service +│ ├─ nginx/ +│ │ ├─ genarrative.conf +│ │ └─ snippets/maintenance.conf +│ └─ env/api-server.env.example +└─ README.md +``` + +不再生成旧产物: + +- `web-server.mjs` +- 旧的一体化 `start.sh` +- 旧的一体化 `stop.sh` + +## Jenkins 节点 + +Jenkins 可运行在 Windows 或其他机器上,但构建与发布动作使用 Linux agent。 + +### 开发/构建实例 + +- 节点名:`genarrative-dev` +- 标签:`genarrative-linux dev build` +- 用途:拉代码、安装依赖、构建主站、构建后台、构建 `api-server`、构建 SpacetimeDB wasm、归档产物。 + +### 生产/发布实例 + +- 节点名:`genarrative-prod` +- 标签:`genarrative-linux prod deploy` +- 用途:服务器配置、发布静态网站、发布 `api-server`、发布 SpacetimeDB 模块、数据库导入导出、维护模式切换。 + +### SSH PEM 凭证 + +在 Jenkins 中使用 `SSH Username with private key` 类型添加 PEM 私钥: + +- `genarrative-dev-ssh-key`:开发/构建实例 SSH 凭证。 +- `genarrative-prod-ssh-key`:生产/发布实例 SSH 凭证。 + +推荐使用非 root 用户,例如 `jenkins`。该用户只通过 sudoers 获得必要命令权限,例如 `systemctl restart genarrative-api`、`nginx -t`、维护脚本、发布目录切换等。 + +## Jenkins 流水线 + +计划重新创建以下流水线: + +1. `Genarrative-Server-Provision` +2. `Genarrative-Web-Build` +3. `Genarrative-Web-Deploy` +4. `Genarrative-Api-Build` +5. `Genarrative-Api-Deploy` +6. `Genarrative-Stdb-Module-Build` +7. `Genarrative-Stdb-Module-Publish` +8. `Genarrative-Database-Export` +9. `Genarrative-Database-Import` +10. `Genarrative-Full-Build-And-Deploy` + +构建流水线运行在 `build && dev`,发布、导入导出和服务器配置流水线运行在 `deploy && prod`。 + +构建流水线支持参数 `PUBLISH_AFTER_BUILD`: + +- `false`:只构建并归档产物。 +- `true`:构建成功后触发对应发布流水线。 + +发布流水线必须从归档产物获取文件,不依赖构建 workspace 的本地状态。 + +## 流水线职责 + +### Genarrative-Server-Provision + +用于生产服务器一次性或低频配置: + +- 创建 `spacetimedb`、`genarrative` 等系统用户。 +- 创建 `/stdb`、`/opt/genarrative`、`/srv/genarrative`、`/etc/genarrative`、`/var/lib/genarrative/maintenance`。 +- 安装或更新 SpacetimeDB。 +- 安装 systemd unit。 +- 安装 Nginx 配置和维护模式 snippet。 +- 执行 `nginx -t`。 +- 启用并启动 `spacetimedb.service` 与 `genarrative-api.service`。 + +该流水线属于高风险操作,默认要求人工确认后执行。 + +### Web Build / Deploy + +构建: + +- 构建主站静态文件。 +- 构建后台前端,base path 为 `/admin/`。 +- 生成或复制 `maintenance.html`。 +- 归档 `web/` 产物。 + +发布: + +- 解包到 `/opt/genarrative/releases//web`。 +- 更新 `/opt/genarrative/current` 与 `/srv/genarrative/web` 指向。 +- 执行 Nginx 配置测试和静态页面 smoke test。 +- 不进入维护模式。 + +### Api Build / Deploy + +构建: + +- 编译 Rust `api-server`。 +- 归档单一可执行文件和必要运行说明。 + +发布: + +- 进入维护模式。 +- 解包到 `/opt/genarrative/releases//api-server`。 +- 更新 `/opt/genarrative/current`。 +- 重启 `genarrative-api.service`。 +- 检查本机 `/healthz`。 +- 成功后解除维护模式。 +- 失败时保留维护模式并发邮件。 + +### Stdb Module Build / Publish + +构建: + +- 使用 `spacetime build` 构建 `spacetime_module.wasm`。 +- 归档 wasm 与发布脚本。 + +发布: + +- 进入维护模式。 +- 将 wasm 上传到生产实例。 +- 在生产实例本机执行 `spacetime publish -s local --bin-path spacetime_module.wasm `。 +- 成功后执行必要 smoke test。 +- 成功后解除维护模式。 +- 失败时保留维护模式并发邮件。 + +## 数据库导出与导入 + +### 导出 + +`Genarrative-Database-Export` 用于人工导出生产数据: + +- 运行在 `deploy && prod`。 +- 进入维护模式,避免导出期间继续写入。 +- 从本机 SpacetimeDB 导出指定数据库数据。 +- 产物归档到 Jenkins,并可额外保存到服务器备份目录。 +- 成功后解除维护模式。 +- 失败时保留维护模式并邮件通知。 + +### 导入 + +`Genarrative-Database-Import` 用于人工导入或恢复数据: + +- 运行在 `deploy && prod`。 +- 必须要求人工确认目标数据库、导入文件和是否覆盖。 +- 进入维护模式。 +- 导入前先生成一次安全备份。 +- 执行导入。 +- 执行数据校验和服务 smoke test。 +- 成功后解除维护模式。 +- 失败时保留维护模式并邮件通知。 + +数据库表结构变更必须同步检查并更新 `migration.rs`,不能只发布 wasm。 + +## 全量构建并发布 + +`Genarrative-Full-Build-And-Deploy` 顺序: + +1. 触发 `Genarrative-Web-Build`。 +2. 触发 `Genarrative-Api-Build`。 +3. 触发 `Genarrative-Stdb-Module-Build`。 +4. 触发 `Genarrative-Stdb-Module-Publish`。 +5. 触发 `Genarrative-Api-Deploy`。 +6. 触发 `Genarrative-Web-Deploy`。 +7. 执行生产 smoke test。 + +网站最后发布,避免后台或主站提前指向尚未完成发布的后端能力。 + +## 回滚 + +- 网站回滚:将 `/srv/genarrative/web` 或 `/opt/genarrative/current` 切回上一版本并 reload Nginx。 +- `api-server` 回滚:将 `/opt/genarrative/current` 切回上一版本并重启 `genarrative-api.service`。 +- SpacetimeDB 模块回滚:发布上一版本 `spacetime_module.wasm`。 +- 数据回滚:使用导入流水线恢复指定备份,必须进入维护模式。 + +## 待落地文件 + +后续工程落地时需要新增或改造: + +- `deploy/systemd/spacetimedb.service` +- `deploy/systemd/genarrative-api.service` +- `deploy/nginx/genarrative.conf` +- `deploy/nginx/snippets/maintenance.conf` +- `deploy/env/api-server.env.example` +- `scripts/deploy/maintenance-on.sh` +- `scripts/deploy/maintenance-off.sh` +- `scripts/deploy/maintenance-status.sh` +- 新 Jenkinsfile 或 Job DSL,用于上述 10 条流水线。 +- 更新旧部署文档,标记旧一体化脚本为废弃或迁移对象。 + +## 参考 + +- SpacetimeDB 官方自托管文档:https://spacetimedb.com/docs/how-to/deploy/self-hosting/