Skip to content
bitzorcas
中EN

Guide

部署形态选择

在 systemd 单机、docker-compose 和 Kubernetes 三条发布路径之间做出选择,并理解三 Host 的迁移、启动与回滚顺序。

Last updated

BitzOrcas 的生产 Host 由 API、JobHost 和 Gateway 三个独立进程组成。仓库不把任何一种部署拓扑声明成唯一正式方式,而是为三条常见路径提供配套脚本和清单:单机 systemd、单机 docker-compose,以及 Kubernetes。三条路径都要满足同一组不变约束——迁移必须在常驻服务启动前完成、三服务按依赖顺序启停、/health/ready 失败必须阻断流量。

三条路径对照

维度systemd 单机docker-compose 单机Kubernetes
编排单元bitzorcas-{api,jobhost,gateway}-<env> 三个 unitapi jobhost gateway 三个 serviceDeployment + Service + Job
入口脚本scripts/deploy/deploy.sh;零停机切流另有 deploy-blue-green.shdeploy/docker-compose.app.yml 叠加层deploy/kubernetes/*.yaml
打包scripts/deploy/pack.* 三件套,单 zip 含三 Host同 deploy/container/*.Dockerfile 构建三镜像同 systemd/compose 镜像或 digest
迁移执行deploy.sh 内联调用 --init-schema + --init-quartz-schema一次性 migrate-schema/migrate-quartz servicemigrate-job.yaml 必须成功完成
配置来源/etc/bitzorcas/<env>.env(EnvironmentFile=)deploy/app.env + compose environmentconfigmap.yaml + secret.yaml
回滚deploy.sh 自动备份;蓝绿用 deploy-rollback.sh 回切上一颜色docker compose down 后恢复镜像 tagkubectl rollout undo
适合规模单实例、资源敏感、不跑 Docker daemon单机一体化、环境可复现、客户机一致多副本、自动扩缩、故障域隔离

共同的不变约束

无论选择哪条路径,部署流程都必须保证以下顺序,否则迁移与启动会竞争,或健康检查误判就绪:

基础设施就绪
DB / Redis / RabbitMQ

API 迁移
--init-schema

JobHost 迁移
--init-quartz-schema

启动 JobHost

启动 API

启动 Gateway

/health/ready 全绿

  1. 数据库、Redis、RabbitMQ 先就绪。
  2. API 迁移器执行 --init-schema(自动 CREATE DATABASE + 业务表 + 审计分表 + 幂等种子)。
  3. JobHost 迁移器执行 --init-quartz-schema(Quartz AdoJobStore 持久化表)。
  4. 启动 JobHost,再启动 API,最后启动 Gateway(Gateway 反向代理 API,依赖 API 健康)。
  5. 三服务各自的 /health/ready 返回 200 后才切流量。

停止顺序相反:Gateway → API → JobHost。

systemd 单机部署

准备

服务器一次性准备:

Terminal window
# ① 安装 .NET 10 Runtime(迁移器和常驻进程都需要,不需 SDK)
# Ubuntu 示例;CentOS/RHEL 用 dnf
sudo apt-get install -y dotnet-runtime-10.0
dotnet --version # 应显示 10.0.x
# ② 创建目录结构
sudo mkdir -p /www/{apps,releases,backups,scripts} /etc/bitzorcas
# ③ 放置部署脚本(来自代码仓库 scripts/deploy/)
sudo cp scripts/deploy/deploy.sh /www/scripts/deploy.sh
sudo chmod +x /www/scripts/deploy.sh
# ④ 生成 deployment-id(community.small 的人数计量锚,首次设置后不可更改)
sudo mkdir -p /www/apps/<env>/.bitzorcas/license
cat /proc/sys/kernel/random/uuid | tr -d '-' \
| sudo tee /www/apps/<env>/.bitzorcas/license/deployment-id

配置环境文件

/etc/bitzorcas/<env>.env 同时被 deploy.sh(load_env_file 解析供迁移器,不能 source 含非法 shell 标识符的键)和三个 systemd unit(EnvironmentFile=)引用,因此三服务共享同一份配置:

Terminal window
sudo cp scripts/deploy/env.example /etc/bitzorcas/<env>.env
sudo chmod 600 /etc/bitzorcas/<env>.env # 含数据库密码与 JWT 密钥,必须仅 root 可读
sudo nano /etc/bitzorcas/<env>.env

必填项见 scripts/deploy/env.example 注释,关键是:

  • ConnectionStrings__Default:数据库连接串
  • PiiEncryption__SearchHashKey:openssl rand -base64 32,API 与 JobHost 必须同一稳定值
  • Jwt__Secret:openssl rand -base64 48,≥32 字符
  • Licensing__Runtime__PolicyId:community.small.v1(≤30 人阶段)或商业策略
  • Frontend__BaseUrl / Cors__AllowedOrigins__0:与浏览器地址栏完全一致(含协议与端口)
  • Gateway__KnownProxies__0:生产 ingress CIDR,禁止只填 loopback;预览可 127.0.0.1/8
  • ReverseProxy__Clusters__<api|signalr|files>__Destinations:集群 id 禁止连字符(systemd 会忽略 api-cluster)
  • OpenApi__Enabled / OpenApi__RequireAuthentication / OpenApi__PersistAuthentication:Production 通常关闭文档;共享预览如开启,必须要求产品文档会话,并保持 Scalar 认证持久化为 false
  • OpenApi__Servers__0__Url:文档面开启时填写公开 Origin,IP 入口必须包含非默认端口

数据库初始化模式(deploy.sh 读取 env;不要把「跳过演示用户」理解成「完全不跑种子」):

模式 / 变量默认含义
BITZORCAS_DEPLOY_DB_MODE(未设时由下方旧开关推导)schema-only / platform-seed / full-seed,推荐首选
schema-only—--init-schema --no-seed:建库/建表/补字段 + 审计分表 + CAP Outbox,完全不跑任何种子(含数据字典)。开发日常发版推荐
platform-seed推导默认跑平台种子(字典/模块等),SkipSeedIds 跳过演示用户/角色/租户。仍会跑字典等种子
full-seed—平台种子 + Demo 用户/租户/Host 账号,需全部 USER__*__PASSWORD(_HASH)
BITZORCAS_INIT_NO_SEED0兼容写法:1 → 等同 schema-only
BITZORCAS_SKIP_DEMO_SEED1兼容写法:未设 DEPLOY_DB_MODE 时,1 → platform-seed,0 → full-seed
BITZORCAS_SCHEMA_FULL_INIT01 时 Schema 强制全量 CodeFirst;默认按 SQL Server 目录只补缺表/缺列/缺索引
USER__ADMIN__PASSWORD 等空仅 full-seed(或 SKIP_DEMO_SEED=0)必填

打包

本地打包三 Host 为单个 zip(内含 api/ jobhost/ gateway/ 三个子目录 + manifest.txt):

Terminal window
# 首次部署:强制全量
./scripts/deploy/pack.sh <env> <version> --full
# 后续增量:必须先把服务器 /www/apps/<env>/manifest.txt
# 下载为 publish 目录中的 manifest-server-<env>-*.txt,再打包(不要 --full)
scp <user>@<server>:/www/apps/<env>/manifest.txt \
../publish/<分支>/manifest-server-<env>-$(date +%m%d%H%M).txt
./scripts/deploy/pack.sh <env> <version>
# Windows PowerShell(脚本 UTF-8,启动时强制控制台 UTF-8)
.\scripts\deploy\pack.ps1 -Env <env> -Version <version>
.\scripts\deploy\pack-frontend.ps1 -Environment <env> -Version <version>

增量只信任 manifest-server-<env>-*.txt。本地 manifest-baseline-* 仅审计用;没有服务器基线时会安全回退为全量包。产物输出到 <项目上级>/publish/<分支>/bitzorcas-<env>-<version>.zip。

部署

Terminal window
# 上传到服务器
scp bitzorcas-<env>-<version>.zip <user>@<server>:/www/releases/
# 默认 platform-seed:跑字典等平台种子,跳过演示用户(不是「仅建表」)
ssh <user>@<server> "sudo /www/scripts/deploy.sh /www/releases/bitzorcas-<env>-<version>.zip <env>"
# 开发迭代推荐:仅建表/补字段,完全不跑种子(含字典)
ssh <user>@<server> "sudo BITZORCAS_DEPLOY_DB_MODE=schema-only /www/scripts/deploy.sh /www/releases/bitzorcas-<env>-<version>.zip <env>"
# 兼容旧写法:
# ssh <user>@<server> "sudo BITZORCAS_INIT_NO_SEED=1 /www/scripts/deploy.sh /www/releases/bitzorcas-<env>-<version>.zip <env>"
# 首次演示环境(需 USER__*__PASSWORD):
# ssh <user>@<server> "sudo BITZORCAS_DEPLOY_DB_MODE=full-seed /www/scripts/deploy.sh /www/releases/bitzorcas-<env>-<version>.zip <env>"

deploy.sh 内部按共同不变约束执行:前置检查 → 停止三服务(Gateway→API→JobHost)→ 端口释放 → 备份 → 解压 → 写入 Gateway appsettings.Deploy.json(下游地址)→ API schema(按 DEPLOY_DB_MODE 决定是否跑种子)→ JobHost --init-quartz-schema → 生成三个 systemd unit → 启动(JobHost→API→Gateway)→ 健康检查(失败打印 journal)→ 失败自动回滚 → 成功后安装服务器 manifest.txt。

Schema 侧:SQL Server 目录探测后,对象齐全则跳过 CodeFirst;仅部分表/列/索引缺失时只对受影响模型 InitTables;BITZORCAS_SCHEMA_FULL_INIT=1 或探测失败则全量。探测只校验对象存在性,不比较列类型/长度。

判断成功

端点期望
systemctl status bitzorcas-api-<env>active (running)
curl localhost:8080/health/ready200
curl localhost:8081/health/ready200(JobHost 仅暴露 health,无业务 API)
curl localhost:8082/health/ready200(Gateway,对外入口)
curl localhost:8080/health/licensecommunity.small 显示 LIC.COMMUNITY_SMALL + 当前人数
/openapi/v1.json 与 /scalar/v1Production 为 404;受外层保护的预览按配置返回 200

日志:journalctl -u 'bitzorcas-*-<env>' -f

1Panel 机器需要零停机切 Gateway 时,不要继续用 deploy.sh 原地覆盖。先按 1Panel OpenResty 单机蓝绿 把 location 改成 named upstream,再:

Terminal window
# ① 先接线 named upstream,再发空闲颜色;回切不碰数据库。
sudo /www/scripts/setup-1panel-openresty.sh <env>
sudo BITZORCAS_DEPLOY_DB_MODE=schema-only \
/www/scripts/deploy-blue-green.sh /www/releases/bitzorcas-<env>-<version>.zip <env>
sudo /www/scripts/deploy-rollback.sh <env>

回滚

deploy.sh 在每次部署前做增量备份到 /www/backups/<env>/,健康检查失败时自动回滚(恢复备份 → 重跑迁移 → 重启)。手动回滚:

Terminal window
# 先恢复上一版文件并校正两套数据库结构,再恢复对外流量。
sudo systemctl stop bitzorcas-gateway-<env> bitzorcas-api-<env> bitzorcas-jobhost-<env>
sudo tar -xzf /www/backups/<env>/backup_<timestamp>.tar.gz -C /www/apps/<env>
sudo dotnet /www/apps/<env>/api/BitzOrcas.Api.dll --init-schema
sudo dotnet /www/apps/<env>/jobhost/BitzOrcas.JobHost.dll --init-quartz-schema
sudo systemctl start bitzorcas-jobhost-<env> bitzorcas-api-<env> bitzorcas-gateway-<env>

docker-compose 单机部署

适合不想要裸进程管理、希望环境可复现的单机场景。仓库根 docker-compose.yml 只编排基础设施(SQL Server / RabbitMQ / Redis / AgileConfig / Qdrant),应用栈由 deploy/docker-compose.app.yml 叠加层补充。

Terminal window
# ① 准备配置
cp .env.example .env # 基础设施凭据
cp deploy/app.env.example deploy/app.env # 应用配置(License/DataProtection/CORS)
# ② 构建三镜像
docker build -f deploy/container/Dockerfile -t bitzorcas-api:local .
docker build -f deploy/container/JobHost.Dockerfile -t bitzorcas-jobhost:local .
docker build -f deploy/container/Gateway.Dockerfile -t bitzorcas-gateway:local .
# ③ 启动(迁移器 service 自动先跑,成功后 api/jobhost/gateway 依次起)
docker compose -f docker-compose.yml -f deploy/docker-compose.app.yml up -d

depends_on + condition: service_completed_successfully 保证迁移器先于常驻服务完成。第一阶段(community.small)使用 ASPNETCORE_ENVIRONMENT=Development,因为 Production 环境受 RuntimeConfigurationGuard 强制完整生产依赖(见 production-configuration)。

Kubernetes 部署

多副本、自动扩缩、故障域隔离的生产场景。清单在 deploy/kubernetes/:

Terminal window
# 填充 configmap.yaml 的所有 replace-with-* 占位符
# 创建 bitzorcas-api-runtime Secret + 独立的 license envelope Secret
kubectl apply -f deploy/kubernetes/namespace.yaml
kubectl apply -f deploy/kubernetes/configmap.yaml
# 迁移 Job 必须成功完成,才能 apply Deployments
kubectl apply -f deploy/kubernetes/migrate-job.yaml
kubectl wait --for=condition=complete job/bitzorcas-schema-migrator -n bitzorcas
kubectl apply -f deploy/kubernetes/api.yaml
kubectl apply -f deploy/kubernetes/jobhost.yaml
kubectl apply -f deploy/kubernetes/gateway.yaml

K8s 路径的完整契约见 deploy/kubernetes/README.md:运行时副本不在启动时跑 schema、readOnlyRootFilesystem、deployment-id 必须跨副本一致、PII HMAC 轮换不支持滚动发布。

License 衔接

三条路径都使用同一套从 community.small(≤30 活跃自然人用户,免签名 License)切换到商业 License(完整 Platform 能力)的步骤:

  1. 部署 BitzOrcas.LicenseSigner(独立内网服务,持有 KMS/HSM 私钥,仅 Bearer 认证)。
  2. 在环境配置中追加可信公钥 Licensing__Runtime__TrustedPublicKeys__<key-id>。
  3. 通过 LicenseSigner 控制面四眼审批签发,产出 runtime-license.json(绑定当前 deployment-id)。
  4. 切换 Licensing__Runtime__PolicyId 到商业策略,重启 API 与 JobHost。

切换后 /health/license 从 LIC.COMMUNITY_SMALL 变为 Valid,30 人上限解除。详见商业运行许可证。

常见故障

现象首先检查
deploy.sh 迁移失败中止/etc/bitzorcas/<env>.env 的连接串;迁移输出日志
API 启动后立即退出journalctl -u bitzorcas-api-<env>;常见为 Redis/RabbitMQ 不可达或 PII HMAC 缺失
JobHost 启动失败Quartz schema 是否已迁移(--init-quartz-schema)
Gateway 返回 502API 是否 ready;systemctl status bitzorcas-api-<env> 与 API 日志
/health/license 返回 503License 策略或人数超限;community.small 上限 30 活跃用户
健康检查超时触发自动回滚deploy.sh 输出的失败服务;通常是 DB/Redis/MQ 依赖不可达
compose 迁移器卡住migrate-schema service 日志;数据库连接串或 SA 密码
Gateway Production 启动拒绝Gateway__KnownProxies 是否显式指定 ingress CIDR(禁止只填 loopback)
Scalar 调试请求丢失公开端口边缘代理是否使用 $http_host 并发送 X-Forwarded-Port;OpenAPI Server 是否正确

另见

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%