Skip to content

模型网关与灰度发布:路由、限流、金丝雀与 A/B 实验

本页速览 多个模型版本/多个模型并存时,网关负责路由、限流、灰度与 A/B。本文实战用 Nginx/Envoy + 应用层路由实现模型网关,并给出金丝雀发布与 A/B 实验的完整流程。

模型网关与灰度发布:路由、限流、金丝雀与 A/B 实验

一句话定义:模型网关是模型服务集群前的统一流量入口,负责把请求路由到正确的模型/版本,并在其上实施限流、熔断、灰度切流、缓存与审计——它是"模型版本变多之后"从直连模型服务升级出的必要一层。

为什么值得动手:模型一多、一更新,问题就来了:怎么让 5% 的流量先试试新模型而不影响全量?新版本效果不好怎么 10 秒内切回去?多个团队各挂一个模型服务,入口谁来统一管? 答案是网关。本文用 Nginx(权重路由 + 灰度)和 Envoy(高级路由)给出两种落地形态,再讲清金丝雀发布与 A/B 实验的完整流程。

一、模型网关的职责

职责说明
路由按路径/请求头把请求分到不同模型或版本
限流保护下游模型服务不被突发流量打垮(令牌桶/QPS 上限)
熔断下游连续失败时快速失败,不再继续压垮它
灰度/金丝雀按权重或用户把流量切到新版本
缓存对同输入请求复用结果(如人脸检索、模板化查询)
审计记录调用方、模型版本、耗时、结果,供 A/B 与对账
鉴权校验 API Key,控制谁能调哪个模型

先给结论:小型团队用 Nginx 起步(配置即代码、零维护),中大型团队用 Envoy(可编程、可观测、可灰度),需要"模型级"语义(版本、自动回滚、多框架)时用 KServe 这类专用推理平台。完整决策见框架与平台怎么选

二、方案对比

方案优势劣势适用
Nginx 简单路由零依赖、配置直观无内置熔断/重试,灰度仅权重2~3 个模型、快速上线
Envoy 高级路由头/权重/镜像流量路由、熔断重试内置、xDS 动态更新学习曲线陡,配置 DSL 复杂微服务化、多团队、K8s 原生
应用层网关(自研/KServe)懂模型语义:版本管理、自动扩缩、批量入口自研成本高;KServe 绑定 K8s模型为产品的团队

三、实战一:两个模型版本并存的路由(Nginx)

场景:CTR 模型从 v1 升级到 v2,先让 10% 流量走 v2。

nginx
# /etc/nginx/conf.d/model-gateway.conf
upstream ctr_v1 {
    server 10.0.1.10:8000;   # 旧版本模型服务(如 FastAPI/Triton)
}

upstream ctr_v2 {
    server 10.0.1.11:8000;   # 新版本模型服务
}

server {
    listen 8080;

    # 按请求头灰度:带 x-canary: v2 的请求强制走新版本(内部测试/特定用户组)
    location = /predict {
        if ($http_x_canary = "v2") {
            proxy_pass http://ctr_v2;
        }
        proxy_pass http://ctr_v1;
    }
}

权重灰度用 Nginx 的 split_clients,基于请求某个字段的哈希做稳定的百分比切分——同一用户总是落到同一版本,这对实验一致性很重要:

nginx
# 按用户 ID 哈希,10% 的 user_id 哈希值落在 v2
split_clients "${http_x_user_id}" $ctr_backend {
    10%    ctr_v2;
    *      ctr_v1;
}

location = /predict {
    proxy_pass http://$ctr_backend;
}

为什么按用户而不是按请求数轮询

灰度发布和 A/B 都要求同一用户看到的结果一致(不漂移)。按请求数轮询会让同一个用户前一条走 v1、后一条走 v2,业务侧会困惑"到底新模型什么效果"。所以灰度分流要么按用户哈希、要么按 cookie/请求头。

四、实战二:金丝雀发布流程(5% → 20% → 50% → 100%)

金丝雀(Canary)的目标是把新版本逐步放量,任何一步观测到恶化就停。标准流程:

  1. 先部署新版本但零流量:v2 服务就绪,走一遍冒烟测试(正确性 + 延迟);
  2. 5% 流量:跑 30~60 分钟,盯核心指标对比 v1/v2;
  3. 20% → 50%:每级观察 30 分钟以上,指标无恶化才继续;
  4. 100% 全量:全量后再观察一段时间,然后清理旧版本。

切流时的观察指标(口径必须 v1/v2 一致,否则没法比):

指标说明
P99 延迟新版本不能比旧的慢超过设定阈值(如 +20%)
错误率5xx/超时比例,恶化即停
业务效果如点击率、转化率——金丝雀的最终裁判
资源GPU 利用率、显存、CPU,确认新模型跑得动

权重切换在 Nginx 里改一行(10%20%),在 Envoy 里改路由配置(见下)。发布策略的完整方法论见发布策略:灰度与回滚

五、实战三:Envoy 实现按流量灰度 + A/B

Envoy 的优势是请求头/权重/镜像流量(shadow traffic)路由内建。以下配置把 10% 流量路由到 v2,并额外支持 x-ab: b 请求头强制走 v2(用于 A/B 实验对照组):

yaml
# envoy.yaml(片段)
static_resources:
  clusters:
    - name: ctr_v1
      connect_timeout: 0.25s
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: ctr_v1
        endpoints:
          - lb_endpoints:
              - endpoint: { address: { socket_address: { address: ctr-v1, port_value: 8000 } } }
    - name: ctr_v2
      connect_timeout: 0.25s
      type: STRICT_DNS
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: ctr_v2
        endpoints:
          - lb_endpoints:
              - endpoint: { address: { socket_address: { address: ctr-v2, port_value: 8000 } } }

http_filters:
  - name: envoy.filters.http.router
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

# 路由规则(实际在 VirtualHost 中)
route_config:
  virtual_hosts:
    - name: model_api
      domains: ["*"]
      routes:
        # A/B:携带 x-ab: b 的请求强制走 v2(对照组)
        - match:
            headers:
              - name: x-ab
                exact_match: "b"
            prefix: /predict
          route: { cluster: ctr_v2 }
        # 金丝雀:其余请求按权重 90/10 切分
        - match: { prefix: /predict }
          route:
            weighted_clusters:
              clusters:
                - name: ctr_v1
                  weight: 90
                - name: ctr_v2
                  weight: 10

Envoy 内置熔断与重试,一行配置:

yaml
route:
  cluster: ctr_v1
  retry_policy:
    retry_on: "5xx"
    num_retries: 2
  timeout: 2s

先给结论:生产环境的灰度推荐 Envoy 或 K8s 网关(如 Istio/Argo Rollouts)而非手写 Nginx if——配置可动态更新(xDS)、有熔断重试、有指标集成。Nginx 适合"5 分钟搞定"的临时灰度。

限流:网关的第二种保命技能

下游模型服务容量是固定的,网关的限流保护它们不被突发流量打垮。Nginx 令牌桶限流:

nginx
# 每个 IP 每秒最多 10 个请求,突发允许 20;超限返回 429
limit_req_zone $binary_remote_addr zone=model_api:10m rate=10r/s;

location = /predict {
    limit_req zone=model_api burst=20 nodelay;
    limit_req_status 429;
    proxy_pass http://$ctr_backend;
}

Envoy 的本地限流(envoy.filters.http.local_ratelimit)同理,且可以按 x-user-id 等请求头维度限流,配合下游能力设置"每模型每秒 N QPS"。限流参数怎么定,取决于压测与容量规划测出的单实例容量,再留 20% 余量。

熔断:保护自己,也保护下游

熔断的默认参数先给结论:连续失败阈值 5~10 次、熔断窗口 10~30 秒、熔断期间直接快速失败(返回 503)。Envoy 配置:

yaml
clusters:
  - name: ctr_v1
    circuit_breakers:
      thresholds:
        - priority: DEFAULT
          max_connections: 1024
          max_pending_requests: 100     # 排队上限
          max_requests: 2000
          max_retries: 3

熔断 vs 限流的区别一句话:限流挡"进来太多",熔断挡"出去打不动"——下游已经故障时,熔断让你不再傻等,而是快速失败让上游降级。配合 推荐系统在线推理 里的级联降级,整条链路才有韧性。

trace_id:灰度与 A/B 对账的地基

灰度切流后要回答"这条请求走了哪个模型版本",网关必须生成并透传 trace_id,把 model_version 写进响应头

nginx
# Nginx 响应头带上模型版本,业务侧事后可对账
add_header X-Model-Version "v1" always;
add_header X-Trace-Id $request_id always;

trace_id 贯穿网关→模型服务→日志→埋点,A/B 分析时按它 join 业务结果。这套链路追踪的实现细节见可观测性落地

六、A/B 实验设计:分流、埋点、显著性、时长

金丝雀是"效果防御"(新版本不能更差),A/B 是"效果求证"(新版本是否显著更好)。完整设计四步:

  1. 分流:实验组/对照组各 50%(或按流量预算),用用户 ID 哈希保证稳定(同用户同组),如第五节 x-ab 方案或网关内置分流;
  2. 埋点:网关在响应里打上 model_version(v1/v2)、trace_id,业务侧把模型版本与业务结果(点击/转化)join 起来。版本号必须随响应返回,否则事后对不上;
  3. 显著性检验:用双样本比例检验/卡方检验(转化类)或 t 检验(数值类),观察 p < 0.05 才下结论,并关注效应量(提升几个百分点);
  4. 时长:至少覆盖一个完整业务周期(如一周,含周末)。样本量提前用功效分析估算——常见错误是实验跑 2 天就下结论,样本不足得不出显著差异

A/B 的分流/埋点/结果回看,最终都要落到监控与可观测性的指标体系上,否则数据对不齐,实验结论不可信。

七、回滚机制:网关一键切回

金丝雀发现指标恶化,回滚动作只有一个:把权重从 10% 改回 0%,v2 服务可以留在原地排查

nginx
# 回滚:v2 权重归零
split_clients "${http_x_user_id}" $ctr_backend {
    0%     ctr_v2;
    *      ctr_v1;
}

要点:

  • 回滚不要删服务,只切流量。v2 留着抓日志、看错误,比重启环境快得多;
  • 网关侧做自动回滚门槛:新版本错误率 > 1% 或 P99 超阈值连续 N 分钟,自动切回(Envoy/Istio 或自研脚本都可实现);
  • 全量后发现回归(覆盖了灰度没覆盖的流量形态),同样切回 + 复盘。完整回滚策略见发布策略:灰度与回滚

常见坑与排查

现象排查
灰度口径不一致v1/v2 指标没法比统一指标定义与埋点字段,见第六节
缓存污染新旧版本结果被网关缓存混用缓存 key 带上模型版本号;灰度期间对新版本禁用缓存或单独缓存
分流不"粘性"同一用户反复横跳两个版本按用户 ID 哈希(split_clients),不要按请求轮询
权重改不生效Nginx 改了不重载nginx -s reload;Envoy 需 xDS 推送或重载配置
实验样本不足p 值不显著就宣布结论先算样本量、跑满周期,再下结论
网关超时短于模型耗时慢模型请求被网关 504网关 timeout 要大于模型 P99 延迟
灰度只测延迟不测效果延迟没问题但业务指标掉灰度与 A/B 都要埋业务指标,网关只给你流量,不给你结论

延伸阅读

参考资料