灰度的 10% 流量怎么分?按权重、按请求头还是按 cookie?同一个用户会不会一会儿旧版本一会儿新版本?这篇用 nginx 1.31.6 和 Envoy 1.39.3 把几种分流方式量出来。
做法 | S(稳定) | C(新版本) | C 的比例 ------------------------------------+-------+--------+------ (a) nginx split_clients $request_id | 8962 | 1038 | 10.4% (b) nginx upstream weight=9:1 | 9000 | 1000 | 10.0% (c) nginx split_clients 固定 key | 10000 | 0 | 0.0% Envoy weighted_clusters 90:10 | 9018 | 982 | 9.8% (b) 是确定性的加权轮询,正好 9000 / 1000;(a) 和 Envoy 是按随机数分,有抖动,10000 个请求时标准差约 30;(c) 固定 key 每个请求都落到同一个桶,10% 的权重没有意义,这就是 split_clients 必须选一个随请求(或随用户)变化的 key 的原因
split_clients 随机;upstream weight 是确定性的加权轮询;Envoy 是随机选择簇;key 必须随请求或用户变化。
请求 | nginx:去 C 的个数(共 20) | Envoy:去 C 的个数(共 20) ----------------------+---------------------+-------------------- 无头无 cookie | 0 | 0 X-Canary: 1 | 20 | 20 X-Canary: 0 | 0 | 0 Cookie: canary=1 | 20 | 20 Cookie: a=b; canary=1 | 20 | 20 Cookie: canary=10 | 0 | 0 Cookie: mycanary=1 | 0 | 0 相近的 cookie(canary=10、mycanary=1)都没有被误命中:nginx 用 $cookie_canary 按 cookie 名取值再比较,Envoy 对整个 cookie 头写了一个要求 canary=1 前后是分隔符的正则;两者都要把匹配写精确,否则 canary=10 之类的值会被当成 canary=1
两个网关的结果一致,匹配要写精确。
做法 | 只看到 S 的用户 | 只看到 C 的用户 | 两个版本都看到过的用户 ---------------------------------+-----------+-----------+------------ nginx split_clients $http_x_user | 179 | 21 | 0 nginx upstream weight=9:1 | 24 | 0 | 176 Envoy weighted_clusters 90:10 | 22 | 0 | 178 按用户(或会话)哈希分流,同一个用户始终落在同一个版本,体验一致,也便于排查;每个请求独立随机的分流,10% 的流量会让约 1 − 0.9^20 = 88% 的用户在 20 个请求内碰到过新版本
按用户哈希分流让同一用户始终落在同一个版本;每个请求独立分流,约 88% 的用户在 20 个请求内碰到过新版本。