Files
easyai-ai-gateway/docs/operations/production-acceptance.md
T
wangbo 53589fe3ef fix(cluster): 以宁波专用节点替换深圳 Worker
移除深圳节点及中继拓扑,新增第二台宁波 K3s agent 的全互联 WireGuard 接入和严格 UFW 门禁。\n\nWorker Deployment 与容量控制器仅选择 easyai.io/worker=true 节点,使原宁波混部节点退出 Worker 资源预算,生产基线恢复为宁波专用节点与香港节点各一实例。\n\n已通过 Go 全量测试、go vet、gofmt、迁移安全检查、bash -n、ShellCheck、发布脚本测试和 Kubernetes 清单渲染。
2026-08-01 10:47:01 +08:00

231 lines
14 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.
# 生产同构验收模式与高媒体压力测试
## 目标与边界
生产同构验收使用当前生产 API、双站点 Worker、PostgreSQL、River、候选路由、限流、
账务、回调和文件物化链路。只有候选确定后的上游连接会被克隆并指向集群内的协议模拟器,
因此不会绕过真实 Gemini 与 Volces 客户端实现。
验收脚本是显式生产写操作,会暂停新正式任务、调整 Worker 容量并在恢复场景删除一个
Worker Pod。它不会自动发布新版本;只能针对已经发布、完整 SHA 和 digest 固定的 manifest
执行。默认先通过[本地三节点同构验收](local-isomorphic-acceptance.md);用户明确授权在线验收时,
可以记录审计豁免并跳过本地阶段,但不能把跳过记录成通过。
## 隔离与流量门禁
Gateway 流量模式保存在数据库中,支持 `live``validation`
- `live`:正常接收正式任务,带验收 Header 的请求被拒绝。
- `validation`:普通新任务返回 `503 validation_in_progress`;已有正式任务继续排空。
- 验收请求必须同时匹配当前 Run ID、随机 Run Token,以及 Run 中登记的专属 API Key/User
身份对;任意错配都被拒绝。
- 普通生产请求体不能打开 `simulation`
- `X-EasyAI-Acceptance-Upstream: real` 只对已授权验收身份有效,用于最后两条真实小流量请求。
验收请求使用以下 Header,Token 只存在于私有环境和进程内,不写入数据库、报告或日志:
```text
X-EasyAI-Acceptance-Run: <run-id>
X-EasyAI-Acceptance-Token: <run-token>
X-EasyAI-Acceptance-Upstream: real
```
Run 记录保存 release SHA、API/Worker image digest、容量档位和脱敏报告。切回 `live` 必须
同时匹配 Run ID、流量模式 revision、release SHA 与两个 digest,并且 Run 已标记为
`passed`。CAS 任一项变化都会拒绝切换。
## 验收负载
Gemini 原生图片编辑调用 `generateContent`,输入 `contents.parts` 同时包含文本和
`inlineData`,并设置 `responseModalities=["IMAGE"]`。压测端边读 Base64 边解码和计算
SHA-256,不集中保存响应:
| Profile | 请求数 | 输入图片 | 输出图片 | 模拟延迟 |
|---|---:|---:|---:|---:|
| `gemini-baseline` | 1000 | 256 KiB | 256 KiB | 4 秒 |
| `gemini-large` | 128 | 2 MiB | 4 MiB | 815 秒 |
| `gemini-peak` | 32 | 8 MiB | 8 MiB | 1530 秒 |
多参考图视频使用正式 `content` 结构,按 60%/30%/10% 分配 3/6/9 图,包含 JPEG、PNG、
WebP、合法 4K 图片和越过 Seedance 2.0 官方输入边界的 `6144x2160` 图片。官方边界为
宽高均在 `300..6000 px` 且宽高比在 `0.4..2.5`;4K 本身不应被误判为越界。128 组输入
组合避免只命中相同缓存;每四个任务至少一个携带越界图片,并要求在到达上游前完成缩放、
补边或重编码。
- `video-throughput`:1200 个任务并发提交,提交窗口不超过 10 秒。
- `video-recovery`:96 个 2–3 分钟任务,运行中删除香港 Worker Pod,验证远程任务恢复、
租约续期和重复提交保护。
- `real-canary`:一条 Gemini 带图编辑和一条多参考图视频,使用真实候选与真实上游。
模拟器实现 Gemini `generateContent` 与 Volces
`/contents/generations/tasks` 提交、查询、取消协议,还提供格式混合的图片池和独立回调
收集器。它校验参考图数量、角色、格式、尺寸与内容哈希,并统计重复上游提交和重复回调。
默认通过 `AI_GATEWAY_ACCEPTANCE_IDENTITY_SHARDS=32` 幂等准备 32 个仅属于验收用户组的
身份、API Key 和独立钱包,并按全局请求序号轮询使用。每次负载执行都有独立 execution ID
它同时进入幂等键和输入图片哈希,因此三次重复验收既不会重放旧任务,也不会命中前一轮的
输入资产缓存。这样可模拟真实多租户并发,避免把 1000
个任务全部压在同一钱包行锁上;每个任务仍执行真实预留、结算和重复扣费检查。该值可在
`1..128` 内通过环境变量调整,Run 只保存 Key/User ID 对,Key Secret 仅通过 stdin 传入
双站压测进程,不进入 Pod spec、数据库 Run 配置、报告或日志。
## Worker 配置
执行槽、连接池和媒体并发进入 Pod 环境变量,副本数由发布工具更新 Deployment
```text
AI_GATEWAY_ASYNC_WORKER_INSTANCE_HARD_LIMIT
AI_GATEWAY_DATABASE_MAX_CONNS
AI_GATEWAY_DATABASE_MIN_IDLE_CONNS
AI_GATEWAY_DATABASE_MAX_CONN_IDLE_SECONDS
AI_GATEWAY_MEDIA_MATERIALIZATION_CONCURRENCY
AI_GATEWAY_MEDIA_REQUEST_CONCURRENCY
AI_GATEWAY_MEDIA_IMAGE_NORMALIZATION_CONCURRENCY
AI_GATEWAY_WORKER_REPLICAS_NINGBO
AI_GATEWAY_WORKER_REPLICAS_HONGKONG
```
`AI_GATEWAY_WORKER_REPLICAS_*` 是发布工具配置,不会传入容器。容量档位先固定宁波、香港
各一个 2 GiB Worker,再按实时内存、CPU 和 PostgreSQL 预算验证 `1+2`、条件允许时的
`2+2`,以及安全 drain 回到 `1+1`
生产基线使用两个独立物理 Worker 节点:第二台宁波服务器承载 `easyai-worker-ningbo`
香港服务器承载 `easyai-worker-hongkong`。两个 Deployment 都要求节点带
`easyai.io/worker=true`,原宁波混部节点不再承载 Worker。默认验收配置为:
```bash
AI_GATEWAY_ACCEPTANCE_BASE_REPLICAS_NINGBO=1
AI_GATEWAY_ACCEPTANCE_BASE_REPLICAS_HONGKONG=1
AI_GATEWAY_ACCEPTANCE_AUTOSCALING_MIN_REPLICAS_NINGBO=1
AI_GATEWAY_ACCEPTANCE_AUTOSCALING_MIN_REPLICAS_HONGKONG=1
AI_GATEWAY_ACCEPTANCE_AUTOSCALING_MAX_REPLICAS_NINGBO=2
AI_GATEWAY_ACCEPTANCE_AUTOSCALING_MAX_REPLICAS_HONGKONG=2
```
资源前置门禁和容量控制器只采样 `easyai.io/worker=true` 节点,避免把原宁波混部节点的
可分配资源错误计入弹性上限。每站点能否升到两个副本仍由实测 RSS、CPU 和 PostgreSQL
连接预算决定,不因新增节点而预先承诺。
| 档位 | 单实例执行槽 | 数据库池 | 媒体并发 | 全局执行槽 |
|---|---:|---:|---:|---:|
| P24 | 24 | 32 | 24 | 48 |
| P28 | 28 | 36 | 28 | 56 |
| P32 | 32 | 40 | 32 | 64 |
`AI_GATEWAY_MEDIA_IMAGE_NORMALIZATION_CONCURRENCY` 单独约束 6K 等超限参考图的解码、缩放和
重新编码,避免执行槽升档时把大图峰值内存同比放大。2 GiB Worker 的起始值为 `2`;它不占用
视频提交后的上游等待槽,只有实测单 Pod RSS 和节点预算允许时才单独升档。
API Pod 使用独立发布配置 `AI_GATEWAY_API_DATABASE_MAX_CONNS`,生产同构验收默认使用 `31`
也可通过 `AI_GATEWAY_ACCEPTANCE_API_DATABASE_MAX_CONNS` 覆盖本次验收值,无需修改 Go 代码。
所有 API/Worker Pool 的 `AI_GATEWAY_DATABASE_MIN_IDLE_CONNS` 固定为 `4`,仅预热必要连接,
`AI_GATEWAY_DATABASE_MAX_CONN_IDLE_SECONDS``30`,突发扩出的空闲连接会及时回落,
避免四个 Pod 在空闲时占满 PostgreSQL 连接预算。各档表中的数据库池仍表示单个 Worker
的最大连接数。中止旧 Run 后还必须等待未提交任务、退款 outbox、回调 outbox 和连接高水位
全部收敛,才会启动下一轮负载。
每档依次运行全部五个模拟 Profile 三次。失败时恢复上一个已完整通过的档位,流量保持
`validation`,不会自动放开正式请求。
每次容量配置滚动后不会立即造压测流量。脚本先等待默认 60 秒的控制面稳定窗口,要求三地
`readyz/etcd`、CNPG 同步副本和节点内存正常,窗口内没有 API Server handler timeout、
etcd peer I/O timeout、WAL receiver timeout 或 CNPG 控制面连接异常;同时按 etcd counter
增量计算各节点 backend commit 和 WAL fsync 平均延迟。默认门槛分别为 50 ms 和 15 ms
可通过 `AI_GATEWAY_ACCEPTANCE_POST_ROLLOUT_STABILITY_SECONDS`
`AI_GATEWAY_ACCEPTANCE_ETCD_BACKEND_COMMIT_MAX_MS`
`AI_GATEWAY_ACCEPTANCE_ETCD_WAL_FSYNC_MAX_MS` 调整。最长等待 240 秒仍不稳定时直接阻断,
不会把发布滚动造成的控制面抖动与业务容量混在同一次负载里。
常规容量调整只需修改服务器上的私有发布配置并执行获授权的滚动命令,不要求源码改动或
新镜像:
```bash
easyai-ai-gateway-cluster-release capacity
```
## 执行
主验收用户、钱包和 API Key 可以由脚本在隔离验收组中幂等创建;管理员 Token 未提供时,
脚本使用本地私有环境中的 `AI_GATEWAY_ONLINE_ACCOUNT/PASSWORD` 登录获取短期 Manager Token。
两个 Gateway 地址默认从宁波、香港节点推导,三张真实小流量参考图默认生成 512×512 合成
PNG 并通过验收 Key 上传到 Gateway。生产快照默认在已部署的 API Pod 内只读导出,无需把
数据库连接串传到本机。所有自动生成的 Key、Token 和连接信息仅存在于进程或 `0600` 临时
文件中,不写入报告或日志。上述值仍可用 `AI_GATEWAY_ACCEPTANCE_*` 显式覆盖。脚本会以主
身份为模板幂等准备其余隔离身份;模型变量可省略,脚本会从当前启用候选中选择 Gemini 图片
编辑模型,以及 `omni_video.max_images >= 9` 的视频模型并优先 Seedance 2.0/fast。
验收组按 release SHA 隔离;历史 Run 的用户和分片不会被清理或复用到新 release,本次需要的
分片及其 API Key 会幂等迁移到当前 release 的专属组。
宁波 15 分钟预热内存门槛默认 65%,可用
`AI_GATEWAY_ACCEPTANCE_NODE_MEMORY_PRECONDITION_PERCENT` 在 50–79% 之间显式调整;报告必须记录
实际门槛,运行期 80%目标和 85%硬门禁不随之放宽。
为保证请求严格各有一半命中宁波和香港,可以把两个 Gateway URL 配成两个节点的
`https://<节点IP>`,并设置
`AI_GATEWAY_ACCEPTANCE_GATEWAY_TLS_SERVER_NAME=ai.51easyai.com`。压测器会继续校验正式
证书,并同时使用该域名作为 HTTP Host;这不是跳过 TLS 校验。
线上高负载不再由操作者本机单进程跨公网发送。脚本构建同一源码的静态 linux/amd64
压测器,校验 SHA-256 后以 `0600` 临时环境分别运行在宁波、香港宿主机的 K3s 之外;两个
进程同时只访问本站 TLS 入口,并按奇偶全局序号分担请求。这样避免本机上行带宽成为容量
瓶颈,同时仍覆盖正式 TLS/Nginx 入口。站点报告按请求数相加、耗时取较慢站点、p50/p95/p99
取两站较差值进行保守合并。宁波、香港的脱敏部分报告会分别保存在 Run 目录,远端进程和
私有环境在 Run 退出时按精确路径清理。压力采样直接从节点访问 Pod metrics,不再反复创建
`kubectl exec` 流,避免验收监控本身放大 K3s API Server 和 etcd 压力。
```bash
scripts/cluster/run-production-acceptance.sh \
--execute dist/releases/<完整SHA>.json \
--local-report dist/acceptance/local/<本地run-id>/acceptance-report.json
```
只有用户明确要求跳过本地验收时,才允许使用下面的审计豁免。报告会把 `localNative`
`amd64Artifact` 记录为 `skipped`,并保留 `local_acceptance_explicitly_waived` 记录;不得
伪造为通过。线上模拟、真实金丝雀、资源、一致性和发布 CAS 门禁仍全部执行:
```bash
scripts/cluster/run-production-acceptance.sh \
--execute dist/releases/<完整SHA>.json \
--skip-local-acceptance
```
脚本依次执行:
1. 校验工作区、manifest、线上 release SHA、digest、双站点镜像和副本数。
2. 准备专属验收用户组、身份分片、独立钱包与候选访问规则。
3. 部署 digest 固定且仅集群内可访问的协议模拟器。
4. 创建 Run、切换 `validation`、等待已有正式任务排空。
5. 每档滚动后先通过控制面稳定窗口,再按 P24、P28、P32 执行三轮负载和 Worker 强杀。
6. 执行两条真实小流量请求。
7. 校验任务、账务、回调、队列、连接池、RSS、节点内存、数据库同步、六向 WireGuard 和
公网健康。
8. 完成 Run 并输出 `acceptance-report/v1`,保持 `validation`,等待人工确认。
报告保存在 `dist/acceptance/<run-id>/`,包含每阶段吞吐、p50/p95/p99、队列和资源 CSV、
协议模拟器统计及最终摘要。报告不包含图片原文、Base64、Token、密码或连接串。
确认整体报告后单独执行人工开闸:
```bash
scripts/cluster/run-production-acceptance.sh \
--promote dist/releases/<完整SHA>.json \
--run-id <线上run-id>
```
`--promote` 会重新读取 Run、流量 revision、release SHA、镜像 digest、配置哈希及暂存容量
配置,任一 CAS 不一致即拒绝。成功切到 `live` 后同一命令继续运行有限期监控:前 2 小时
每 10 秒采样,之后每 60 秒采样至 24 小时,不恢复已经停用的周期性巡检。队列、RSS、
连接池、同步复制、租约、重复结算、WireGuard 或公网健康触发硬门禁时,监控通过 CAS
自动切回 `validation`,并恢复 Run 开始前保存的精确容量快照;不会自动回滚数据库。
## 通过和失败处理
通过要求包括:任务全部成功、Gemini 输出可解码且哈希/字节数正确、视频参考图校验全部
通过、无重复任务/上游提交/结算/回调、队列归零、Worker 心跳小于 30 秒、单实例分配不
超过当前档位、无 OOM/重启、Pod RSS 小于 1.5 GiB、节点内存低于 80%、PostgreSQL 连接
低于 75%、连接池不连续满载、双副本同步、六向链路和公网健康通过。
失败 Run 会记录原因、恢复上一稳定容量并保持 `validation`。成功 Run 也保持
`validation`,只有上述人工 `--promote` 才能开闸。排除失败问题后调用 Run 的
`retry` 接口重新执行。只有人工决定放弃验收时,才使用带相同 CAS 字段的 `abort` 接口
恢复 `live`;该操作不会把失败验收标记为通过。