Files
easyai-ai-gateway/docs/operations/k3s-ha-runbook.md
T
wangbo 7c142f5960 fix(deploy): 补齐集群备份兼容与切换前门禁
阿里云 OSS 的 S3 兼容层要求 virtual-hosted addressing,且 boto3 需要使用 Signature V2。本提交为 Barman 的归档、备份、保留与恢复统一挂载 AWS 配置,并将实际 WAL 归档和近期全备设为切换硬门禁。

同时补充 CNPG/etcdutl 固定版本安装、显式主库切换、三节点逐台停机、OSS 快照下载校验与远端临时 Secret 清理。已通过 ShellCheck、Kustomize、服务端 dry-run、迁移测试、发布脚本测试、敏感信息扫描及生产 precutover 验收。
2026-07-28 06:46:09 +08:00

153 lines
6.6 KiB
Markdown
Raw 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` 污点。
- 健康双库:同步复制,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。恢复后不自动回切;确认连接和任务稳定后再择时切回。