Files
easyai-ai-gateway/docs/operations/production-acceptance.md
T
wangbo a4046c2b81 perf(worker): 解耦准入调度与远端执行
原因:线上 P24 同构验收确认两台香港 Worker 的全局容量为 48,但跨地域逐行准入事务每轮只能形成 8 个活跃租约,队列最老等待超过 15 分钟。\n\n影响:新增可配置的异步准入 dispatcher 角色;生产两地 API 负责准入和过期回收,Worker 仅执行 River job。当前主库同站点 API 可低延迟填满容量,主库切换后另一地 API 通过既有数据库锁安全接管;未配置环境变量时保持原 Worker 一体化行为。\n\n验证:Go 全量测试、go vet、gofmt、真实 PostgreSQL 1000 任务双进程回归、前端 lint/test/build、Kubernetes server-side dry-run、Compose、ShellCheck、cluster/manual release tests 全部通过。
2026-08-01 23:24:30 +08:00

14 KiB
Raw Blame History

生产同构验收模式与高媒体压力测试

目标与边界

生产同构验收使用当前生产 API、双站点 Worker、PostgreSQL、River、候选路由、限流、 账务、回调和文件物化链路。只有候选确定后的上游连接会被克隆并指向集群内的协议模拟器, 因此不会绕过真实 Gemini 与 Volces 客户端实现。

验收脚本是显式生产写操作,会暂停新正式任务、调整 Worker 容量并在恢复场景删除一个 Worker Pod。它不会自动发布新版本;只能针对已经发布、完整 SHA 和 digest 固定的 manifest 执行。默认先通过本地三节点同构验收;用户明确授权在线验收时, 可以记录审计豁免并跳过本地阶段,但不能把跳过记录成通过。

隔离与流量门禁

Gateway 流量模式保存在数据库中,支持 livevalidation

  • live:正常接收正式任务,带验收 Header 的请求被拒绝。
  • validation:普通新任务返回 503 validation_in_progress;已有正式任务继续排空。
  • 验收请求必须同时匹配当前 Run ID、随机 Run Token,以及 Run 中登记的专属 API Key/User 身份对;任意错配都被拒绝。
  • 普通生产请求体不能打开 simulation
  • X-EasyAI-Acceptance-Upstream: real 只对已授权验收身份有效,用于最后两条真实小流量请求。

验收请求使用以下 Header,Token 只存在于私有环境和进程内,不写入数据库、报告或日志:

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:

AI_GATEWAY_ASYNC_WORKER_INSTANCE_HARD_LIMIT
AI_GATEWAY_ASYNC_ADMISSION_MICROBATCH_SIZE
AI_GATEWAY_ASYNC_ADMISSION_DISPATCHER_ENABLED
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,其中一个位于香港控制面节点,另一个位于带 easyai.io/worker=true,easyai.io/database=false 标签的专用 Worker 节点;宁波混部节点不承载 Worker。后续副本上限继续按实时内存、CPU 和 PostgreSQL 预算计算,不按站点或代码写死:

AI_GATEWAY_ACCEPTANCE_BASE_REPLICAS_NINGBO=0
AI_GATEWAY_ACCEPTANCE_BASE_REPLICAS_HONGKONG=2
AI_GATEWAY_ACCEPTANCE_AUTOSCALING_MIN_REPLICAS_NINGBO=0
AI_GATEWAY_ACCEPTANCE_AUTOSCALING_MIN_REPLICAS_HONGKONG=1
AI_GATEWAY_ACCEPTANCE_AUTOSCALING_MAX_REPLICAS_NINGBO=0
AI_GATEWAY_ACCEPTANCE_AUTOSCALING_MAX_REPLICAS_HONGKONG=2

资源前置门禁和容量控制器只把已启用站点的 easyai.io/worker=true 节点计入弹性预算, 协议模拟器优先固定到 easyai.io/worker=true,easyai.io/database=false 的专用节点,避免它与 宁波主库和控制面争抢内存。每站点能否增加副本仍由实测 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_SECONDS30,突发扩出的空闲连接会及时回落, 避免四个 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_SECONDSAI_GATEWAY_ACCEPTANCE_ETCD_BACKEND_COMMIT_MAX_MSAI_GATEWAY_ACCEPTANCE_ETCD_WAL_FSYNC_MAX_MS 调整。最长等待 240 秒仍不稳定时直接阻断, 不会把发布滚动造成的控制面抖动与业务容量混在同一次负载里。

常规容量调整只需修改服务器上的私有发布配置并执行获授权的滚动命令,不要求源码改动或 新镜像:

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 的专属组。 已启用 Worker 节点的 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 压力。

scripts/cluster/run-production-acceptance.sh \
  --execute dist/releases/<完整SHA>.json \
  --local-report dist/acceptance/local/<本地run-id>/acceptance-report.json

只有用户明确要求跳过本地验收时,才允许使用下面的审计豁免。报告会把 localNativeamd64Artifact 记录为 skipped,并保留 local_acceptance_explicitly_waived 记录;不得 伪造为通过。线上模拟、真实金丝雀、资源、一致性和发布 CAS 门禁仍全部执行:

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、密码或连接串。

确认整体报告后单独执行人工开闸:

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;该操作不会把失败验收标记为通过。