Files
Genarrative/docs/technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md

32 KiB
Raw Blame History

生产部署计划

更新时间2026-05-02

当前落地进度

已落地生产基础设施骨架与首批生产 Jenkinsfile

  • deploy/systemd/spacetimedb.service
  • deploy/systemd/genarrative-api.service
  • deploy/nginx/genarrative.conf
  • deploy/nginx/genarrative-dev-http.conf
  • deploy/nginx/snippets/genarrative-maintenance.conf
  • deploy/env/api-server.env.example
  • scripts/deploy/maintenance-on.sh
  • scripts/deploy/maintenance-off.sh
  • scripts/deploy/maintenance-status.sh
  • scripts/build-production-release.sh
  • scripts/jenkins-checkout-source.sh
  • scripts/deploy/production-web-deploy.sh
  • scripts/deploy/production-api-deploy.sh
  • scripts/deploy/production-stdb-publish.sh
  • jenkins/Jenkinsfile.production-web-build
  • jenkins/Jenkinsfile.production-web-deploy
  • jenkins/Jenkinsfile.production-api-build
  • jenkins/Jenkinsfile.production-api-deploy
  • jenkins/Jenkinsfile.production-stdb-module-build
  • jenkins/Jenkinsfile.production-stdb-module-publish
  • jenkins/Jenkinsfile.production-full-build-and-deploy
  • jenkins/Jenkinsfile.production-server-provision
  • jenkins/Jenkinsfile.production-database-export
  • jenkins/Jenkinsfile.production-database-import
  • npm run build:production-release

旧 Jenkins 一体化发布链对应的 Jenkinsfile 已从仓库移除,生产构建和发布入口统一切到 jenkins/Jenkinsfile.production-*scripts/deploy-rust-remote.sh 等旧发布包脚本暂保留为历史迁移参考,不再作为生产 Jenkins Job 的入口。

目标

将当前部署方式调整为单机生产推荐方案:生产运行路径不使用 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/<version>/:每次发布的完整版本目录。
  • /opt/genarrative/current:指向当前生效版本的软链接。
  • /srv/genarrative/web:指向 /opt/genarrative/current/web,供 Nginx 托管静态站点。
  • /etc/genarrative/api-server.envapi-server 生产环境变量文件。
  • /var/lib/genarrative/maintenance/enabled:维护模式开关文件。
  • /stdbSpacetimeDB 程序、配置与数据根目录。

生产密钥

/etc/genarrative/api-server.env 中的生产密钥指所有只能存在于生产服务器、不能进入 Git、不能进入构建产物的敏感配置。典型内容包括

  • LLM 或第三方服务 API Key。
  • 短信服务 Access Key 与 Secret。
  • 后台登录、会话、签名、加密相关密钥。
  • 生产 SpacetimeDB 地址与数据库名。
  • 只允许生产使用的回调地址、白名单或内部令牌。

该文件由服务器配置流水线或人工初始化创建,权限建议为 root:genarrative0640。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 到 HTTPSproduction-https 模式只保留 301 重定向。
  • /maintenance.html:维护中页面。

移除这些公网反向代理:

  • /api/*
  • /generated-*
  • 公网 /healthz
  • 其他旧的一体化 web server 代理入口。

SpacetimeDB 公网路由默认保持收敛,只按实际前端 SDK 需要暴露最小集合。禁止开放可远程发布数据库或管理实例的通用入口。

Nginx 配置文件分为两类:

  • deploy/nginx/genarrative.conf:生产正式域名 HTTPS 配置,genarrative.example.com 只是占位域名,安装时必须替换为真实 SERVER_NAME,并要求 /etc/letsencrypt/live/<SERVER_NAME>/fullchain.pemprivkey.pem 已存在。
  • deploy/nginx/genarrative-dev-http.conf:开发服无域名时的 HTTP-only 配置,只允许 DEPLOY_TARGET=development 使用。没有域名时,SERVER_NAME 填开发机 IP 或临时主机名。它仍复用同一套静态目录、后台 API 反代和 SpacetimeDB SDK 最小公网路由,不恢复旧 /api/*/generated-* 或公网 /healthz

维护模式

维护模式由 /var/lib/genarrative/maintenance/enabled 控制:

  • 文件存在:进入维护模式。
  • 文件不存在:退出维护模式。

行为:

  • 网站静态资源发布不进入维护模式。
  • api-server 发布、SpacetimeDB 模块发布、数据库导入、服务器配置变更必须进入维护模式。
  • 普通页面在维护模式下展示 /maintenance.html
  • /admin/api/* 在维护模式下返回 503。
  • 静态资源仍允许访问,避免维护页样式和资源加载失败。
  • 发布成功后自动解除维护模式。
  • 发布失败时保持维护模式,并通过邮件通知人工处理。

构建产物

生产发布包构建入口:

npm run build:production-release -- --name <version>

每次构建产物按版本号归档:

build/<version>/
├─ web/
│  ├─ index.html
│  ├─ assets/
│  ├─ maintenance.html
│  └─ admin/
├─ web.tar.gz
├─ web.tar.gz.sha256
├─ api-server
├─ api-server.sha256
├─ spacetime_module.wasm
├─ spacetime_module.wasm.sha256
├─ release-manifest.json
├─ scripts/
│  ├─ database-export.mjs
│  ├─ database-import.mjs
│  ├─ spacetime-migration-common.mjs
│  ├─ maintenance-on.sh
│  ├─ maintenance-off.sh
│  └─ maintenance-status.sh
├─ deploy/
│  ├─ systemd/
│  │  ├─ spacetimedb.service
│  │  └─ genarrative-api.service
│  ├─ nginx/
│  │  ├─ genarrative.conf
│  │  ├─ genarrative-dev-http.conf
│  │  └─ snippets/genarrative-maintenance.conf
│  └─ env/api-server.env.example
└─ README.md

web/ 可以保留在构建目录中供本地 smoke test 与人工排查使用,但 Jenkins Web Build 归档和 Web Deploy 传输必须以 web.tar.gz 为主,避免把大量静态碎文件逐个传回 Jenkins controller。api-serverspacetime_module.wasm 是单文件产物,默认直接归档单文件与对应 .sha256,不强制压缩。

不再生成旧产物:

  • web-server.mjs
  • 旧的一体化 start.sh
  • 旧的一体化 stop.sh

Jenkins 节点

Jenkins 可运行在 Windows 或其他机器上,本机 Windows 只作为人工触发入口;构建与发布动作只允许由 Jenkins 调度到 Linux agent 执行。当前已接入的 Linux agent 是开发/构建机,同时也是 development 环境部署机。

开发/构建/开发部署实例

  • Jenkins Job 参数不暴露真实节点名、IP 或带 IP 的标签。
  • 构建 Job 固定使用 label expressionlinux && genarrative-build
  • 当前开发/构建/开发部署 agent 必须同时配置 linuxgenarrative-build 两个标签;非 Linux 节点不能承担构建或部署。
  • 用途:拉代码、安装依赖、构建主站、构建后台、构建 api-server、构建 SpacetimeDB wasm、归档产物并执行 DEPLOY_TARGET=development 的开发环境部署。

生产/发布实例

  • Jenkins Job 参数不暴露真实节点名、IP 或带 IP 的标签。
  • 生产机已作为独立 Linux Jenkins agent 接入,节点名使用脱敏名称 genarrative-release-deploy-01,调度标签只使用 linuxgenarrative-release-deploy
  • 生产机真实连接地址只允许保存在 Jenkins 节点 SSH launcher 的 host 字段中不能写入节点名、调度标签、Job 参数默认值或文档推荐命令。
  • 发布 Job 通过 DEPLOY_TARGET 选择逻辑部署目标,再在 Jenkinsfile 内部映射到 Linux-only 脱敏调度表达式:development -> linux && genarrative-buildrelease -> linux && genarrative-release-deploy
  • 用途:服务器配置、发布静态网站、发布 api-server、发布 SpacetimeDB 模块、数据库导入导出、维护模式切换。

Git 仓库访问

Jenkins controller 与 Linux agent 看到的 Git 服务地址不同,必须拆成两层配置:

  • Jenkins Job 的 Pipeline script from SCM 由 controller 执行SCM URL 使用 controller 可访问的公网地址:http://82.157.175.59:3000/GenarrativeAI/Genarrative.git
  • Jenkinsfile 内部的源码、脚本 checkout 在 Linux agent 上执行,GIT_REMOTE_URL 使用 agent 本机可访问地址:http://127.0.0.1:3000/GenarrativeAI/Genarrative.git

因此生产 Jenkinsfile 不使用 checkout scm 作为构建源码入口,而是显式 checkout([$class: 'GitSCM', userRemoteConfigs: [[url: "${GIT_REMOTE_URL}"]], ...])。后续 scripts/jenkins-checkout-source.sh 会继续把 origin 设置为 GIT_REMOTE_URL,并按 SOURCE_BRANCH / COMMIT_HASH 拉取和校验目标提交。

127.0.0.1 只代表当前执行该阶段的 Linux agent 自身;如果 release agent 与 Git 服务不在同一台机器,必须把对应 Jenkinsfile 的 GIT_REMOTE_URL 改成 release agent 可访问的内网地址,不能让 release 发布阶段回退到 controller 公网拉取。

SSH PEM 凭证

在 Jenkins 中使用 SSH Username with private key 类型添加 PEM 私钥:

  • genarrative-dev-ssh-key:开发/构建实例 SSH 凭证。
  • genarrative-prod-root-ssh:当前开发/构建实例已使用的 SSH 凭证;生产/发布实例复用同一个凭证。

推荐使用非 root 用户,例如 jenkins。该用户只通过 sudoers 获得必要命令权限,例如 systemctl restart genarrative-apinginx -t、维护脚本、发布目录切换等。

Jenkins 流水线

生产 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

已落地的生产流水线脚本文件:

  • jenkins/Jenkinsfile.production-web-build
  • jenkins/Jenkinsfile.production-web-deploy
  • jenkins/Jenkinsfile.production-api-build
  • jenkins/Jenkinsfile.production-api-deploy
  • jenkins/Jenkinsfile.production-stdb-module-build
  • jenkins/Jenkinsfile.production-stdb-module-publish
  • jenkins/Jenkinsfile.production-full-build-and-deploy
  • jenkins/Jenkinsfile.production-server-provision
  • jenkins/Jenkinsfile.production-database-export
  • jenkins/Jenkinsfile.production-database-import

Genarrative-Database-ExportGenarrative-Database-Import 的生产版 Jenkinsfile 已落地;旧的数据库导入导出 Jenkinsfile 已删除,避免继续沿用旧部署目录和旧一体化发布链假设。

构建流水线运行在当前 Linux agent 的脱敏 label expression linux && genarrative-build。发布、导入导出和服务器配置流水线通过 DEPLOY_TARGET 映射到 Linux-only 脱敏部署表达式;其中 development 映射到当前 Linux 开发/构建/开发部署 agent 的 linux && genarrative-buildrelease 映射到独立 Linux 生产部署 agent 的 linux && genarrative-release-deploy,不能复用当前开发/构建/开发部署 agent。真实机器名、IP 和带 IP 的 Jenkins label 只允许留在 Jenkins 节点连接配置中,不能暴露为 Job 参数默认值、调度标签或文档推荐值。

发布流水线通过 Jenkins copyArtifacts(...) 从对应构建 Job 获取归档产物,因此 Jenkins 需要安装并启用 Copy Artifact 插件。数据库导入流水线的手动上传模式使用 stashedFile 文件参数,因此 Jenkins 还需要安装并启用 File Parameter 插件。所有生产 Pipeline 日志必须带时间戳以便审计Jenkins 需要安装 Timestamper 插件,并在全局配置中启用 Enabled for all Pipeline builds。生产发布不能退回到读取构建 workspace 本地目录的旧模式。

所有发布流水线必须提供 DEPLOY_TARGET 参数,用于选择逻辑部署目标:

  • 默认值:development,用于当前 Linux 开发/构建/开发部署 agent 上的开发环境部署,对应脱敏调度表达式 linux && genarrative-build
  • 备选值:release,对应生产发布目标,由独立 Linux 生产部署 agent 执行。

发布流水线的 agent 必须使用 DEPLOY_TARGET 在 Jenkinsfile 内部映射到脱敏 label expression并且表达式必须包含 linux避免在参数页面暴露真实节点名、IP 或带 IP 的 Jenkins label也避免非 Linux 节点执行构建或部署。release 目标还必须要求 CONFIRM_RELEASE_DEPLOY_AGENT=trueGenarrative-Full-Build-And-Deploy 也必须透传同一个 DEPLOY_TARGET,确保 Stdb publish、API deploy、Web deploy 三个部署动作落到同一类目标部署环境。

Rust 构建缓存与磁盘控制

Jenkins 在 agent 上执行构建时会为不同 Job 或不同构建创建独立 workspaceRust 默认把编译产物写入仓库内 server-rs/target/,会导致每个 workspace 都保留一份完整 target 目录,占用大量磁盘。生产构建流水线必须把 Rust 缓存固定到 workspace 外的稳定目录。

如果构建 agent 使用 root 账户执行,缓存目录不应写死为 /var/lib/jenkins。推荐优先使用单独数据盘,例如 /data/jenkins-cache/genarrative/;如果没有数据盘,可使用 /var/cache/genarrative-build/

mkdir -p /var/cache/genarrative-build/{api-server,stdb-module}
chmod 700 /var/cache/genarrative-build

API 构建流水线建议设置:

environment {
  CARGO_HOME = '/var/cache/genarrative-build/api-server/cargo-home'
  CARGO_TARGET_DIR = '/var/cache/genarrative-build/api-server/cargo-target/prod-release'
  CARGO_INCREMENTAL = '0'

  RUSTC_WRAPPER = 'sccache'
  SCCACHE_DIR = '/var/cache/genarrative-build/api-server/sccache'
  SCCACHE_CACHE_SIZE = '30G'
}

Stdb module 构建流水线建议设置:

environment {
  CARGO_HOME = '/var/cache/genarrative-build/stdb-module/cargo-home'
  CARGO_TARGET_DIR = '/var/cache/genarrative-build/stdb-module/cargo-target/prod-release'
  CARGO_INCREMENTAL = '0'

  RUSTC_WRAPPER = 'sccache'
  SCCACHE_DIR = '/var/cache/genarrative-build/stdb-module/sccache'
  SCCACHE_CACHE_SIZE = '30G'
}

如使用数据盘,则把上述路径替换为:

environment {
  CARGO_HOME = '/data/jenkins-cache/genarrative/<component>/cargo-home'
  CARGO_TARGET_DIR = '/data/jenkins-cache/genarrative/<component>/cargo-target/prod-release'
  CARGO_INCREMENTAL = '0'

  RUSTC_WRAPPER = 'sccache'
  SCCACHE_DIR = '/data/jenkins-cache/genarrative/<component>/sccache'
  SCCACHE_CACHE_SIZE = '30G'
}

其中 <component> 使用 api-serverstdb-module。API 与 Stdb module 并行构建时不能共享同一个 CARGO_HOMECARGO_TARGET_DIR,否则容易在 Cargo package cache 或 target 目录上出现 Blocking waiting for file lock on package cache 等锁等待。

Rust 构建流水线还必须在真正执行 cargo 前 source scripts/jenkins-prepare-cargo-env.sh。该脚本会把 HOME 临时切到组件级缓存目录,显式导出组件级 CARGO_HOMECARGO_TARGET_DIRSCCACHE_DIR,并在 ${CARGO_HOME}/config.toml 写入可用的 Cargo sparse registry 配置。这样可以避免构建 agent 使用 root 账户时继续读取 /root/.cargo/config 中失效的全局镜像配置,例如错误的 replace-with = "tuna" 导致 config.json not found in registry

server-rs/.cargo/config.toml 只保留 Linux release 目标的 linker/rustflags 等仓库级构建配置,不在仓库级 config.toml 里重定义 agent 全局镜像源。不要把这些约束写到单个 crate 的 Cargo.toml,因为 Cargo 不会从 crate manifest 的 [target.x86_64-unknown-linux-gnu] 读取构建器配置。

scripts/build-production-release.sh 必须尊重 CARGO_TARGET_DIR,不能硬编码从 server-rs/target/ 拷贝 Rust 产物。脚本中的产物路径应按以下口径计算:

CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-${SERVER_RS_DIR}/target}"
API_BINARY_SOURCE="${CARGO_TARGET_DIR}/x86_64-unknown-linux-gnu/release/api-server"
WASM_SOURCE="${CARGO_TARGET_DIR}/wasm32-unknown-unknown/release/spacetime_module.wasm"

并发与清理规则:

  • 同一个 Rust 构建 Job 建议使用 disableConcurrentBuilds(),避免同一组件的多个 release 构建同时写入同一最终产物路径。
  • 如果 Linux agent 未安装 sccacheRust 构建流水线必须自动取消 RUSTC_WRAPPER,回退到直接使用 rustc,不能因为缺少可选缓存工具阻断真实构建。
  • 生产发布流水线只能消费 build/<version>/ 或 Jenkins 归档产物,不允许从共享 cargo-target 目录直接发布。
  • SCCACHE_CACHE_SIZE 必须设置上限,避免编译缓存无限增长。
  • /var/cache/genarrative-build/*/cargo-target 或数据盘对应目录配置定期清理,建议保留最近 14 到 30 天。
  • Jenkins Job 必须配置构建记录和归档产物保留策略,避免历史 release 包长期堆积。

统一源码版本参数

所有构建流水线、发布流水线和 Genarrative-Full-Build-And-Deploy 都必须支持以下参数:

  • SOURCE_BRANCH:源码分支,默认 master,代表 origin/master 最新提交。
  • COMMIT_HASH:可选 Git commit hash留空时使用 origin/<SOURCE_BRANCH> 最新 commit填写时必须是 7 到 40 位十六进制 hash并且该 commit 必须属于 origin/<SOURCE_BRANCH>
  • DEPLOY_TARGET:逻辑部署目标选择参数。发布流水线和 Genarrative-Full-Build-And-Deploy 必填;构建流水线仅在 PUBLISH_AFTER_BUILD=true 时用于触发下游发布。

执行规则:

  • 流水线先按 Jenkins SCM 配置 checkout 仓库,再执行 git fetch --tags --prune origin "+refs/heads/<SOURCE_BRANCH>:refs/remotes/origin/<SOURCE_BRANCH>"
  • 如果工作区是浅克隆,流水线必须尝试 git fetch --unshallow --tags,确保能验证目标 commit 与分支关系。
  • COMMIT_HASH 为空时detached checkout 到 refs/remotes/origin/<SOURCE_BRANCH> 当前最新 commit。
  • COMMIT_HASH 非空时,先解析到完整 commit再用 git merge-base --is-ancestor <commit> refs/remotes/origin/<SOURCE_BRANCH> 校验该提交属于指定分支,校验通过后 detached checkout。
  • 流水线日志必须输出最终 SOURCE_BRANCH 与实际 SOURCE_COMMIT
  • 构建产物必须写入 release-manifest.json,至少包含 versionsource_branchsource_commitbuilt_at 和组件类型,供发布、回滚和审计使用。

构建流水线使用上述参数决定实际构建源码。发布流水线也暴露同名参数,但只用于选择本次发布使用的部署脚本、配置模板和 smoke test 逻辑;被发布的应用文件仍必须来自 Jenkins 归档产物或指定 release 包,不允许在发布流水线中重新构建。

当构建流水线以 PUBLISH_AFTER_BUILD=true 触发下游发布流水线时,必须把 SOURCE_BRANCH 和实际解析出的 SOURCE_COMMIT 作为下游 COMMIT_HASH 传递,确保部署逻辑和刚生成的产物可追溯到同一源码版本。

构建流水线支持参数 PUBLISH_AFTER_BUILD

  • false:只构建并归档产物。
  • true:构建成功后触发对应发布流水线。

发布流水线必须从归档产物获取文件,不依赖构建 workspace 的本地状态。

流水线职责

Genarrative-Server-Provision

用于生产服务器一次性或低频配置:

  • 创建 spacetimedbgenarrative 等系统用户。
  • 创建 /stdb/opt/genarrative/srv/genarrative/etc/genarrative/var/lib/genarrative/maintenance
  • 安装或更新 SpacetimeDB。
  • 安装 systemd unit。
  • 可选安装 Nginx 配置和维护模式 snippet。
  • 安装 Nginx 配置时执行 nginx -t
  • 启用并启动 spacetimedb.servicegenarrative-api.service

该流水线属于高风险操作,默认要求人工确认后执行。 已落地的 Jenkinsfile 为 jenkins/Jenkinsfile.production-server-provision。该流水线默认 DRY_RUN=true只打印将执行的初始化动作真正写入系统用户、目录、systemd、环境文件并启动服务时必须设置 DRY_RUN=false 且勾选 CONFIRM_PROVISION。当 DEPLOY_TARGET=release 时,还必须勾选 CONFIRM_RELEASE_DEPLOY_AGENT,并通过 linux && genarrative-release-deploy 调度到独立 release 部署 agent。

首次真实初始化默认保持 NGINX_CONFIG_MODE=none先完成系统用户、目录、SpacetimeDB、systemd unit 与 /etc/genarrative/api-server.env 落盘。开发服没有域名时,使用 DEPLOY_TARGET=development + NGINX_CONFIG_MODE=development-http 安装 deploy/nginx/genarrative-dev-http.conf,并把 SERVER_NAME 填为开发机 IP 或临时主机名。等正式域名确定,并且目标机已经存在 /etc/letsencrypt/live/<SERVER_NAME>/fullchain.pem/etc/letsencrypt/live/<SERVER_NAME>/privkey.pem 后,再把 SERVER_NAME 改成真实域名,并设置 NGINX_CONFIG_MODE=production-https 安装 Nginx HTTPS 配置。流水线会拒绝 release 目标安装 development-http,也会拒绝用占位域名或缺失证书安装 production-https

若误用占位域名执行过真实初始化,失败通常发生在 nginx -t,错误表现为找不到 /etc/letsencrypt/live/genarrative.example.com/fullchain.pemprivkey.pem。新版初始化在 NGINX_CONFIG_MODE=none 时会检测并禁用上一轮留下的占位域名 Nginx 配置,避免它继续影响后续 nginx -t

Web Build / Deploy

构建:

  • 先按 SOURCE_BRANCH / COMMIT_HASH 解析并 checkout 目标源码,默认构建 origin/master 最新 commit。
  • 默认执行 npm ci 安装前端依赖,确保 Jenkins 新 workspace 中存在 vite 等构建工具。
  • 构建主站静态文件。
  • 构建后台前端base path 为 /admin/
  • 生成或复制 maintenance.html
  • web/ 打包为 web.tar.gz,生成 web.tar.gz.sha256
  • 归档 web.tar.gzweb.tar.gz.sha256release-manifest.jsonweb/ 展开目录不作为 Jenkins 主归档对象。

发布:

  • 先按 SOURCE_BRANCH / COMMIT_HASH 解析并 checkout 部署脚本源码,默认使用 origin/master 最新 commit上游构建触发时使用上游传入的实际构建 commit。
  • 获取 web.tar.gzweb.tar.gz.sha256,先校验 checksum再解压到 /opt/genarrative/releases/<version>/web
  • 更新 /opt/genarrative/current/srv/genarrative/web 指向。
  • 执行 Nginx 配置测试和静态页面 smoke test。
  • 不进入维护模式。

Api Build / Deploy

构建:

  • 先按 SOURCE_BRANCH / COMMIT_HASH 解析并 checkout 目标源码,默认构建 origin/master 最新 commit。
  • 编译 Rust api-server
  • 归档单一可执行文件、必要运行说明和 release-manifest.json

发布:

  • 先按 SOURCE_BRANCH / COMMIT_HASH 解析并 checkout 部署脚本源码,默认使用 origin/master 最新 commit上游构建触发时使用上游传入的实际构建 commit。
  • 进入维护模式。
  • 解包到 /opt/genarrative/releases/<version>/api-server
  • 更新 /opt/genarrative/current
  • 重启 genarrative-api.service
  • 检查本机 /healthz
  • 导出产物归档成功后解除维护模式。
  • 失败时保留维护模式并发邮件。

Stdb Module Build / Publish

构建:

  • 先按 SOURCE_BRANCH / COMMIT_HASH 解析并 checkout 目标源码,默认构建 origin/master 最新 commit。
  • 使用 spacetime build 构建 spacetime_module.wasm
  • 归档 wasm、发布脚本和 release-manifest.json

发布:

  • 先按 SOURCE_BRANCH / COMMIT_HASH 解析并 checkout 发布脚本源码,默认使用 origin/master 最新 commit上游构建触发时使用上游传入的实际构建 commit。
  • 进入维护模式。
  • 将 wasm 上传到生产实例。
  • 在生产实例本机执行 spacetime publish -s local --bin-path spacetime_module.wasm <database-name>
  • 成功后执行必要 smoke test。
  • 成功后解除维护模式。
  • 失败时保留维护模式并发邮件。

Full Build-And-Deploy

  • 先解析一次最终 SOURCE_COMMIT,所有下游构建和发布都使用同一个分支与 commit。
  • 并行执行 Web / API / Stdb 三条构建流水线。
  • 构建全部成功后,按顺序执行 Stdb publish、API deploy、Web deploy并把同一个 DEPLOY_TARGET 透传给三条发布流水线。
  • 每条下游构建都只消费自己的归档产物,不直接复用别的 workspace。
  • 生产 Web 发布只处理 web.tar.gz 与 checksumAPI 发布只处理 api-server 与 checksumStdb 发布只处理 spacetime_module.wasm 与 checksum。

数据库导出与导入

导出

Genarrative-Database-Export 用于人工导出生产数据:

  • 已落地 Jenkinsfilejenkins/Jenkinsfile.production-database-export
  • 通过 DEPLOY_TARGET 选择逻辑导出目标;development 映射到 linux && genarrative-buildrelease 映射到 linux && genarrative-release-deploy
  • release 导出必须勾选 CONFIRM_RELEASE_DEPLOY_AGENT,避免当前开发/构建/开发部署 agent 冒充 release 部署机。
  • 进入维护模式,避免导出期间继续写入。
  • 从目标机器本机 SpacetimeDB 导出指定数据库数据,默认连接 SPACETIME_SERVER=local,自托管 root-dir 默认 /stdb
  • 产物归档到 Jenkins并可额外保存到 SERVER_BACKUP_DIRECTORY
  • 敏感 token 与 bootstrap secret 只通过 Jenkins Secret Text 凭据 ID 注入,不作为明文 Job 参数。
  • 成功后解除维护模式。
  • 失败时保留维护模式并邮件通知。

导入

Genarrative-Database-Import 用于人工导入或恢复数据:

  • 已落地 Jenkinsfilejenkins/Jenkinsfile.production-database-import
  • 通过 DEPLOY_TARGET 选择逻辑导入目标;development 映射到 linux && genarrative-buildrelease 映射到 linux && genarrative-release-deploy
  • release 导入必须勾选 CONFIRM_RELEASE_DEPLOY_AGENT,避免当前开发/构建/开发部署 agent 冒充 release 部署机。
  • 通过 INPUT_SOURCE 选择数据源,pipeline_archiveGenarrative-Database-Export 归档复制 INPUT_FILEmanual_upload 使用本次构建上传的 MANUAL_INPUT_FILE;两种方式互斥,Prepare 阶段会直接拦截混填。
  • DRY_RUN 默认开启;真正写入数据时必须勾选 CONFIRM_IMPORT,并让 CONFIRM_DATABASECONFIRM_INPUT_FILE 分别完全匹配 DATABASE 与实际输入文件。
  • REPLACE_EXISTING=trueDRY_RUN=false 时必须额外勾选 CONFIRM_REPLACE_EXISTING
  • 进入维护模式。
  • 导入前先生成一次安全备份。
  • 执行导入。
  • 执行数据校验和服务 smoke test。
  • 成功后解除维护模式。
  • 失败时保留维护模式并邮件通知。
  • pipeline_archive 模式默认使用导出流水线 Genarrative-Database-Export;只需要填写 EXPORT_BUILD_NUMBER_TO_IMPORT 时,归档输入路径自动解析为 database-exports/spacetime-migration-<导出构建号>.json。如果导出时覆盖过 WORKSPACE_EXPORT_DIRECTORYEXPORT_NAME,再显式填写归档内相对路径 INPUT_FILE
  • manual_upload 模式需要上传 MANUAL_INPUT_FILE,并在 CONFIRM_INPUT_FILE 中填写原始文件名;此模式不再填写 EXPORT_BUILD_NUMBER_TO_IMPORTINPUT_FILEEXPORT_JOB_NAME 的默认值会被忽略。
  • 敏感 token 与 bootstrap secret 只通过 Jenkins Secret Text 凭据 ID 注入,不作为明文 Job 参数。

数据库表结构变更必须同步检查并更新 migration.rs,不能只发布 wasm。

全量构建并发布

Genarrative-Full-Build-And-Deploy 编排:

  1. SOURCE_BRANCH / COMMIT_HASH 解析一次最终 SOURCE_COMMIT,默认 origin/master 最新 commit。
  2. 并行触发 Genarrative-Web-BuildGenarrative-Api-BuildGenarrative-Stdb-Module-Build,三条构建都必须使用同一个 SOURCE_BRANCHSOURCE_COMMIT
  3. 三条构建全部成功后,按顺序触发 Genarrative-Stdb-Module-PublishGenarrative-Api-DeployGenarrative-Web-Deploy,同样继续透传同一个 SOURCE_BRANCHSOURCE_COMMITDEPLOY_TARGET
  4. 最后执行生产 smoke test。

网站最后发布,避免后台或主站提前指向尚未完成发布的后端能力。

这种编排能够在不牺牲版本一致性的前提下缩短总耗时,同时避免 Web / API / Stdb 彼此等待构建资源。并行构建时仍要保证每条构建流水线独立归档自己的产物,发布阶段只能消费归档结果,不能回到构建 workspace 重新取文件。

Genarrative-Full-Build-And-Deploy 额外作为定时任务运行Jenkins 在每天凌晨 4 点自动触发一次,默认按 SOURCE_BRANCH=master 解析最新提交,并以 DEPLOY_TARGET=development 把全量构建结果部署到开发机。该定时任务不改写手动触发参数语义,只是给主线全量发布补一个固定的夜间全量刷新入口。

回滚

  • 网站回滚:将 /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/genarrative-dev-http.conf
  • deploy/nginx/snippets/genarrative-maintenance.conf
  • deploy/env/api-server.env.example
  • scripts/deploy/maintenance-on.sh
  • scripts/deploy/maintenance-off.sh
  • scripts/deploy/maintenance-status.sh
  • scripts/build-production-release.sh
  • scripts/jenkins-checkout-source.sh
  • scripts/deploy/production-web-deploy.sh
  • scripts/deploy/production-api-deploy.sh
  • scripts/deploy/production-stdb-publish.sh
  • jenkins/Jenkinsfile.production-web-build
  • jenkins/Jenkinsfile.production-web-deploy
  • jenkins/Jenkinsfile.production-api-build
  • jenkins/Jenkinsfile.production-api-deploy
  • jenkins/Jenkinsfile.production-stdb-module-build
  • jenkins/Jenkinsfile.production-stdb-module-publish
  • jenkins/Jenkinsfile.production-full-build-and-deploy
  • jenkins/Jenkinsfile.production-server-provision
  • 删除旧 JenkinsfileJenkinsfile.build-and-deployJenkinsfile.deployJenkinsfile.database-exportJenkinsfile.database-import
  • 更新旧部署文档,标记旧一体化脚本为废弃或迁移对象。
  • jenkins/Jenkinsfile.production-database-export
  • jenkins/Jenkinsfile.production-database-import

参考

  • SpacetimeDB 官方自托管文档:https://spacetimedb.com/docs/how-to/deploy/self-hosting/
    • 该文档建议 Ubuntu 24.04、spacetimedb 专用用户、/stdb root-dir、systemd 托管,以及 Nginx/HTTPS。
    • 默认公网路由只开放 TypeScript SDK 必需的 /v1/identity/v1/database/<database>/subscribe其他发布、SQL、管理类入口保持本机可用。