feat: add two-node compose deployment
This commit is contained in:
51
deploy/docker-compose.ha.yml
Normal file
51
deploy/docker-compose.ha.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
# 与 deploy/docker-compose.yml 一起使用:
|
||||
# docker compose -f deploy/docker-compose.yml -f deploy/docker-compose.ha.yml up -d mysql verify orbit-a orbit-b
|
||||
# 默认不启动 base 文件中的 orbit 单节点服务,避免重复实例。
|
||||
|
||||
x-orbit-environment: &orbit-environment
|
||||
ORBIT_HTTP_ADDR: 0.0.0.0:8080
|
||||
ORBIT_MYSQL_DSN: "${ORBIT_DB_USER}:${ORBIT_DB_PASSWORD}@tcp(mysql:3306)/${ORBIT_DB_NAME:-orbit}?parseTime=true&multiStatements=true"
|
||||
ORBIT_DB_MAX_OPEN: ${ORBIT_DB_MAX_OPEN:-20}
|
||||
ORBIT_DB_MAX_IDLE: ${ORBIT_DB_MAX_IDLE:-5}
|
||||
ORBIT_WORKFLOW_TICK_INTERVAL: ${ORBIT_WORKFLOW_TICK_INTERVAL:-1s}
|
||||
ORBIT_SCHEDULE_TICK_INTERVAL: ${ORBIT_SCHEDULE_TICK_INTERVAL:-1s}
|
||||
ORBIT_RECOVERY_TICK_INTERVAL: ${ORBIT_RECOVERY_TICK_INTERVAL:-2s}
|
||||
ORBIT_RUNTIME_BATCH_LIMIT: ${ORBIT_RUNTIME_BATCH_LIMIT:-100}
|
||||
ORBIT_SHUTDOWN_TIMEOUT: ${ORBIT_SHUTDOWN_TIMEOUT:-10s}
|
||||
ORBIT_WORKER_CONNECTION_LIMIT: ${ORBIT_WORKER_CONNECTION_LIMIT:-128}
|
||||
ORBIT_EVENT_CONNECTION_LIMIT: ${ORBIT_EVENT_CONNECTION_LIMIT:-128}
|
||||
ORBIT_DISPATCH_WORKERS: ${ORBIT_DISPATCH_WORKERS:-4}
|
||||
ORBIT_DISPATCH_CAPACITY: ${ORBIT_DISPATCH_CAPACITY:-128}
|
||||
ORBIT_CONSOLE_QUEUE_ID: ${ORBIT_CONSOLE_QUEUE_ID:-}
|
||||
ORBIT_METRICS_ADDR: ${ORBIT_METRICS_ADDR:-}
|
||||
|
||||
services:
|
||||
orbit:
|
||||
profiles:
|
||||
- single
|
||||
|
||||
orbit-a:
|
||||
image: ${ORBIT_IMAGE:-orbit:dev}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
depends_on:
|
||||
verify:
|
||||
condition: service_completed_successfully
|
||||
environment:
|
||||
<<: *orbit-environment
|
||||
ports:
|
||||
- "${ORBIT_HTTP_BIND:-127.0.0.1}:${ORBIT_HTTP_PORT_A:-8081}:8080"
|
||||
stop_grace_period: 30s
|
||||
|
||||
orbit-b:
|
||||
image: ${ORBIT_IMAGE:-orbit:dev}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
depends_on:
|
||||
verify:
|
||||
condition: service_completed_successfully
|
||||
environment:
|
||||
<<: *orbit-environment
|
||||
ports:
|
||||
- "${ORBIT_HTTP_BIND:-127.0.0.1}:${ORBIT_HTTP_PORT_B:-8082}:8080"
|
||||
stop_grace_period: 30s
|
||||
@@ -52,6 +52,10 @@
|
||||
|
||||
新增 `deploy/nginx/orbit.conf.example` 作为可选 HTTPS/WSS 入口模板:Compose 默认只将业务端口绑定到 `127.0.0.1`,Nginx 负责 TLS 终止、WebSocket 升级、健康探针代理和控制台登录来源限速;配置语法及端口边界证据见[HTTPS/WSS 入口基线验证](reports/phase-10-ingress.md)。Nginx 是部署入口,不会成为 Orbit 服务端的业务依赖,真实域名、正式证书、入口故障和公网重连仍需目标环境验收。
|
||||
|
||||
新增 `deploy/docker-compose.ha.yml` 作为两节点隔离验收覆盖:节点共享持久化 MySQL 状态但使用独立 HTTP 端口,base 服务中的单节点 Orbit 通过 profile 关闭;两个 `ready` 节点均通过后才能接入入口,滚动停止和重新拉起按[Compose 部署基线](22-compose-deployment.md)执行。该覆盖不替代跨故障域节点、数据库自动切换、负载均衡器和目标容量验收。
|
||||
|
||||
两节点覆盖已完成一次真实 Docker 回归:空库迁移和权限核验后两个节点均就绪,停止 A 后 B 保持就绪,A 重新拉起并重新核验后恢复;证据见[两节点 Compose 覆盖验证](reports/phase-11-two-node-compose.md)。该结果只关闭单机双进程启动/滚动动作,不关闭生产 HA。
|
||||
|
||||
## 2. 持续推进方式
|
||||
|
||||
每个阶段按以下顺序推进:
|
||||
|
||||
@@ -111,6 +111,44 @@ docker compose -f deploy/docker-compose.yml down
|
||||
|
||||
Compose 没有为 scratch 镜像伪造容器内 HTTP healthcheck;编排器或入口网关应调用 `/health/live`、`/health/ready` 和 `/version`。`ready` 返回 `503` 时摘除节点,保留原幂等键并等待数据库恢复。只有明确的废弃环境才使用 `docker compose down -v`,生产数据卷不得随服务停止删除。
|
||||
|
||||
## 5. 当前边界
|
||||
## 5. 两节点覆盖与滚动替换
|
||||
|
||||
需要在同一台主机验证多节点接管时,叠加 `deploy/docker-compose.ha.yml`。两个 Orbit 节点共享 MySQL、迁移账本、租约和流程检查点;每个进程仍生成独立节点标识,停止一个节点不会把另一个节点的持久化执行权一并停止:
|
||||
|
||||
```sh
|
||||
export ORBIT_HTTP_BIND=127.0.0.1
|
||||
export ORBIT_HTTP_PORT_A=8081
|
||||
export ORBIT_HTTP_PORT_B=8082
|
||||
|
||||
docker compose \
|
||||
-f deploy/docker-compose.yml \
|
||||
-f deploy/docker-compose.ha.yml \
|
||||
up -d mysql verify orbit-a orbit-b
|
||||
|
||||
curl --fail http://127.0.0.1:8081/health/ready
|
||||
curl --fail http://127.0.0.1:8082/health/ready
|
||||
```
|
||||
|
||||
入口层把 Nginx `upstream orbit_backend` 改为两个后端:
|
||||
|
||||
```nginx
|
||||
upstream orbit_backend {
|
||||
server 127.0.0.1:8081;
|
||||
server 127.0.0.1:8082;
|
||||
keepalive 64;
|
||||
}
|
||||
```
|
||||
|
||||
滚动替换时先让入口健康检查摘除一个节点,再停止并更新它;确认另一节点 `ready` 后再恢复流量,最后启动更新后的节点并等待两个后端都通过健康检查:
|
||||
|
||||
```sh
|
||||
docker compose -f deploy/docker-compose.yml -f deploy/docker-compose.ha.yml stop orbit-a
|
||||
# 更新镜像标签后重新拉起 orbit-a;迁移和 verify 仍必须先成功
|
||||
docker compose -f deploy/docker-compose.yml -f deploy/docker-compose.ha.yml up -d orbit-a
|
||||
```
|
||||
|
||||
这个覆盖配置用于单机和隔离验收;生产环境应把两个 Orbit 节点放在不同故障域,前置负载均衡器负责健康摘除,MySQL 由独立数据库高可用方案提供,不把单机 Compose 多节点样本当成生产 HA 结论。
|
||||
|
||||
## 6. 当前边界
|
||||
|
||||
该文件是可重复的单节点启动基线,不代表自动数据库故障切换、跨故障域多副本、备份恢复或容量目标已经验收。生产上线还需要真实 MySQL 拓扑、滚动替换、凭据轮换、备份恢复、压测和 RPO/RTO 证据;对应门槛以[交付核对清单](release-checklist.md)为准。
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
| [21 容器发布说明](21-container-deployment.md) | 最小镜像、迁移/核验/启动顺序、探针和发布边界 |
|
||||
| [22 Compose 部署基线](22-compose-deployment.md) | MySQL、迁移、核验、Orbit 服务和首次管理员初始化的可重复启动链路 |
|
||||
| [Nginx HTTPS/WSS 入口示例](../deploy/nginx/orbit.conf.example) | 域名、TLS、WebSocket 升级、健康探针和控制台登录限速 |
|
||||
| [Compose 两节点覆盖](../deploy/docker-compose.ha.yml) | 共享 MySQL 状态、双节点就绪和滚动替换验收入口 |
|
||||
| [两节点 Compose 覆盖验证](reports/phase-11-two-node-compose.md) | 双节点启动、单节点停止后的对等节点可用和重新加入 |
|
||||
| [09 可靠结果订阅](09-result-subscription-core.md) | 提交水位、冻结结果与可靠交付 |
|
||||
| [10 Weave决策与交付](10-workflow-engine-core.md) | 有限步骤决策和内部结果Inbox |
|
||||
| [11 Weave实例受理](11-workflow-admission.md) | 固定版本、初始图和首批任务事务 |
|
||||
|
||||
42
docs/reports/phase-11-two-node-compose.md
Normal file
42
docs/reports/phase-11-two-node-compose.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# 两节点 Compose 覆盖验证
|
||||
|
||||
验证日期:2026-09-22
|
||||
验证范围:两个 Orbit 节点共享 MySQL、独立节点就绪、停止单节点后的对等节点可用、停止节点重新加入。
|
||||
验证环境:Docker Desktop、MySQL 8.4、当前 `orbit:dev` 镜像、Go 1.27.1 构建产物。
|
||||
|
||||
## 验证入口
|
||||
|
||||
```sh
|
||||
docker compose \
|
||||
-f deploy/docker-compose.yml \
|
||||
-f deploy/docker-compose.ha.yml \
|
||||
up -d mysql verify orbit-a orbit-b
|
||||
```
|
||||
|
||||
本机已有大量 Docker 自定义网络,本次只在临时测试覆盖文件中使用默认 `bridge` 网络和 MySQL 链接,以避开本机地址池耗尽;正式的 `deploy/docker-compose.ha.yml` 保持 Compose 项目隔离网络。
|
||||
|
||||
## 结果
|
||||
|
||||
一次性 MySQL 空库完成健康检查、迁移、权限收紧和 schema 核验后,两个 Orbit 节点均启动并通过就绪探针:
|
||||
|
||||
```text
|
||||
ha-e2e=both_ready
|
||||
```
|
||||
|
||||
停止 `orbit-a` 后,`orbit-b` 继续返回就绪:
|
||||
|
||||
```text
|
||||
ha-e2e=peer_ready_after_orbit_a_stop
|
||||
```
|
||||
|
||||
重新拉起 `orbit-a`,它重新完成迁移/核验依赖并恢复就绪:
|
||||
|
||||
```text
|
||||
ha-e2e=orbit_a_rejoined
|
||||
```
|
||||
|
||||
两节点分别绑定临时主机端口 `18087` 和 `18088`;正式入口由 Nginx upstream 或负载均衡器同时指向两个节点,并以 `/health/ready` 作为摘流条件。
|
||||
|
||||
## 验收边界
|
||||
|
||||
该回归证明节点无本地权威状态、停止/重加入顺序和 Compose 依赖正确;它没有证明数据库自动切换、跨故障域、负载均衡器故障、生产证书轮换、容量余量或 RPO/RTO。任务执行和租约接管仍需在目标拓扑中按真实 Worker、慢消费者和数据库故障场景留证。
|
||||
Reference in New Issue
Block a user