Files
wangbo 3053ba4925 feat(acceptance): 增加生产同构媒体压力验收模式
引入动态流量门禁、隔离验收身份与协议级 Gemini/Volces 模拟器,覆盖双站点 API、Worker、PostgreSQL、River、账务、回调和媒体物化链路。

新增 P24/P28/P32 容量阶梯、Worker 强杀恢复、真实小流量 canary、CAS 放量和失败保持 validation 的生产编排;Worker 执行槽、连接池、媒体并发和双站点副本数改为环境配置。

验证:Go 全量测试、真实 PostgreSQL 迁移集成测试、迁移安全检查、OpenAPI 生成、ShellCheck、Kustomize、gofmt 和 git diff --check。
2026-07-30 23:06:19 +08:00

156 lines
6.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# EasyAI Gateway 三节点 K3s 高可用运行手册
## 范围与目标
本手册覆盖宁波、香港、洛杉矶三节点迁移。宁波和香港承载 API、Web、River Worker 与
CloudNativePG;洛杉矶只作为 K3s server/etcd 仲裁节点,并带
`easyai.io/witness=true:NoSchedule` 污点。
生产上线前的双节点高媒体负载、Worker 强杀和 P24/P28/P32 容量搜索见
[生产同构验收模式与高媒体压力测试](production-acceptance.md)。
- 健康双库:同步复制,RPO=0。
- 单库降级:`dataDurability: preferred` 保持写入,`archive_timeout=60s`,灾难 RPO 目标不超过 5 分钟。
- 数据库故障 RTO:不超过 5 分钟。
- 应用发布:香港验收后再滚动宁波,计划内零停机。
- 公网入口:双 NGINX 加人工 AliDNS 切换。接入带健康检查的 GTM 前,不宣称公网入口自动高可用。
所有命令从仓库根目录执行。生产凭据仅从 `0600``.env.local`
`.local-secrets/cluster/` 读取,不进入 Git、发布 manifest 或验收输出。
## 实施顺序
### 1. 源码与镜像门禁
```bash
cd apps/api && env -u AI_GATEWAY_TEST_DATABASE_URL go test ./... -count=1
cd apps/api && go vet ./...
pnpm install --frozen-lockfile
pnpm lint
pnpm test
pnpm build
shellcheck -x -P . scripts/cluster/*.sh deploy/kubernetes/easyai-ai-gateway-cluster-release
kubectl kustomize deploy/kubernetes/production >/dev/null
./tests/ci/migrations-test.sh
node scripts/ci-validate-migrations.mjs <当前线上完整GitSHA>
```
代码提交并推送到 `origin/main` 后,发布 digest 固定镜像:
```bash
./scripts/publish-release-images.sh --components auto
```
### 2. WireGuard 与 K3s
```bash
./scripts/cluster/bootstrap-wireguard.sh
./scripts/cluster/bootstrap-k3s.sh backup
./scripts/cluster/harden-node-network.sh
./scripts/cluster/bootstrap-k3s.sh install
./scripts/cluster/verify-k3s-quorum.sh --execute
./scripts/cluster/verify-cluster.sh precutover
```
`backup` 会在卸载旧 K3s 前,将 SQLite、manifests 和 staging 资源归档并校验后上传
OSS `easyai-ai-gateway/production/pre-ha/``install` 固定
`v1.36.2+k3s1`,使用 embedded etcd、静态 Secret 加密、500ms heartbeat 和 5s election timeout。
K3s containerd 同时配置 Docker Hub、GHCR 与 Quay 的镜像端点;已有集群需要滚动刷新时执行:
```bash
./scripts/cluster/configure-k3s-registries.sh
```
`verify-k3s-quorum.sh` 会依次停止并恢复洛杉矶、香港、宁波 K3s,确认每次剩余两个
server 都保持 etcd quorum。宁波停机可能令香港数据库副本晋升;首次切换前如需恢复宁波
初始主库,显式执行 `promote-cnpg-primary.sh`,故障恢复流程不得自动回切。
### 3. 平台和入口预置
```bash
./scripts/cluster/bootstrap-platform.sh prepare dist/releases/<SHA>.json
./scripts/cluster/install-cnpg-plugin.sh
./scripts/cluster/install-etcdutl.sh
./scripts/cluster/install-nginx-edges.sh prepare
./scripts/cluster/install-certificate-sync.sh
./scripts/cluster/install-legacy-cleaner.sh
```
`prepare` 将应用副本保持在 0,但启动 CNPG 双实例、WAL 归档和每日备份。OSS 的 Barman
链路使用 `s3.oss-cn-shanghai.aliyuncs.com`、virtual-hosted addressing 和 boto3
Signature V2`AWS_CONFIG_FILE` 通过 CNPG projected ConfigMap 同时挂载到主库、副本及恢复
集群。预检会强制产生并归档一个新 WAL,并要求 48 小时内至少存在一份完成的全量备份。
SecretStore
的 4 个值从旧 volume 直接导入 Kubernetes Secret,脚本只校验数量与总字节数,不打印值。
首次切换前如滚动安装令主库落在香港,可显式切回宁波。故障晋升后禁止自动执行该命令:
```bash
./scripts/cluster/promote-cnpg-primary.sh easyai-postgres-1
```
### 4. 02:0004:00 切换
先执行只读预检:
```bash
./scripts/cluster/migrate-production.sh preflight dist/releases/<SHA>.json
```
进入北京时间窗口后执行:
```bash
./scripts/cluster/migrate-production.sh cutover dist/releases/<SHA>.json
```
脚本依次启用维护页、停止旧 API、完整备份旧 PostgreSQL、上传 OSS、恢复 CNPG、比对数据库
版本/扩展/catalog/表数/序列/关键行数/抽样 ID、运行 digest 固定 migrator、冻结 Worker 启动
两地应用、做 NodePort 验收,再切换双 NGINX。公开流量提交后,先启用香港 Worker,再启用宁波
Worker。脚本等待切换后的 CNPG 全量备份状态变为 `completed` 后,才停止旧 Docker
PostgreSQL;数据卷继续保留,不自动删除。
在公共流量提交前,任一步失败或耗时超过 10 分钟都会恢复旧 Docker 与旧 NGINX。公共流量
提交后禁止自动切回旧数据库,避免双写分叉。
## 上线验收
```bash
./scripts/cluster/verify-cluster.sh postcutover
./scripts/cluster/run-cross-node-file-e2e.sh
./scripts/cluster/failure-drill.sh api --execute
./scripts/cluster/failure-drill.sh database --execute
./scripts/cluster/failure-drill.sh witness --execute
./scripts/cluster/restore-backup-drill.sh --execute
./scripts/cluster/verify-etcd-backup.sh
```
跨节点 E2E 通过宁波私有 NGINX 固定进入宁波 API,关闭宁波 River Worker,由香港 Worker
执行真实 multipart 图片编辑任务;校验输入和输出 URL 在两节点下载的长度与 SHA-256
一致,并检查 `river_job.attempted_by`、唯一成功 attempt、唯一结算/扣费及无重复回调。
数据库故障演练会强制删除当前主 Pod,确认 5 分钟内晋升;在副本恢复前写入一条专用
`system_settings` 探针,恢复同步后删除。备份恢复演练会从 OSS 创建一个独立 CNPG Cluster
验证数据库登录、schema、行数和抽样值后删除临时 Cluster 与 PVC。
## 发布与回滚
切换成功后,本地环境自动设置 `AI_GATEWAY_DEPLOY_MODE=kubernetes`。日常发布仍使用:
```bash
./scripts/deploy-production-release.sh dist/releases/<SHA>.json
./scripts/deploy-production-release.sh --status
./scripts/deploy-production-release.sh --rollback <历史完整SHA>
```
集群 helper 从 `easyai-gateway-release` ConfigMap 读取当前 release,数据库迁移前先等待一次
CNPG 备份,然后香港滚动、验收,再滚动宁波。`--rollback` 只恢复应用 digest,绝不逆向执行迁移。
## 旧文件与公网故障
宁波每小时执行一次清理,以当前时间减文件 `mtime` 严格大于 86,400 秒为删除条件。未过期
文件由宁波只读 NGINX 提供,香港通过 WireGuard 回源。清理结果持续为 0 个存量文件后,删除
两个 legacy static include 和宁波 18080 origin;历史过期 URL 返回 410。
公网入口故障时,先使用 `curl --resolve` 确认香港入口健康,再人工将 `ai.51easyai.com`
AliDNS A 记录切到香港 IP。恢复后不自动回切;确认连接和任务稳定后再择时切回。