灰度发布/金丝雀发布时如何解决请求分裂的问题?

58 天前
 Frankcox
比如我设置了新发布的服务的权重为 10%,之前旧服务的权重为 90%,现在我尝试访问一个前端服务,路由将其指向了新环境,但是浏览器同时请求 js,css,img 等资源,而这些资源则被路由指向了旧服务,导致了页面无法正确显示,这种情况有什么好的解决方式吗?比如粘性会话?
2757 次点击
所在节点    程序员
26 条回复
MelodYi
58 天前
相比粗暴地取随机比例,可以考虑用取模之类的方式筛选一部分固定的用户选择新版本。类似 A 、B 测试的感觉。
coderxy
58 天前
楼上+1
Frankcox
58 天前
@MelodYi 这种高级一些的确实能好些,不过应该需要开发侧进行适配吧?目前我们考虑做个基础版本的,尽量不用开发进行修改。
Seulgi
58 天前
你说的粘性会话是一个方案.1 楼说的也是一种方案,一般为了精准灰度用户,会根据用户数量做分组,然后组内用户进灰度。粘性会话其实就是在每次请求里加上标志位,header 也好,session 也好,cookie 也好,然后网关处理路由。
Frankcox
58 天前
@Seulgi 谢谢,这么看还是需要开发那里做对应的灰度适配支持,单纯靠 Ingress 处理不太行
xderam
58 天前
想问下,这种的情况不是应该返回新的静态资源版本吗?例如 http://domain/a.png?v=123 灰度只灰度了后端,前端最好也同步控制。而不是用粘性会话来解决。粘性会话感觉更合适的场景是后端“异常”的时候的方式。
paopjian
58 天前
这个问题不应该是资源的缓存问题吗, 我们直接给 js css 加了随机 hash,每次发布都变化, 这样可以强制刷新
Frankcox
58 天前
@xderam 不好意思,我没太看懂,我理解这个问题反而后端服务会好一些,不用特殊处理吧?比如通常情况下单个 api 请求被分流到了 A 服务那就是 A 服务。相反,对于前端的服务,除了 HTML 还有剩余的一堆静态资源,这种多次请求会因为权重规则导致分裂。当然我指的现在的服务都是没做 version 处理适配灰度这种情况
lasuar
58 天前
最佳的办法当然是权重+粘性会话。k8s 中的 nginx ingress 支持这些功能,但是否支持组合使用还不清楚,理论推测应该是可以的。nginx ingress 通过下面这些注解来实现对应功能:
- nginx.ingress.kubernetes.io/affinity:true 。启用会话粘性,通过 cookie
- nginx.ingress.kubernetes.io/session-cookie-name:?。设置具体的 cookie 字段
- nginx.ingress.kubernetes.io/affinity-mode:粘性模式,balanced or persistent
- nginx.ingress.kubernetes.io/canary:true 。启用金丝雀发布
- nginx.ingress.kubernetes.io/canary-weight:number 。按权重
- nginx.ingress.kubernetes.io/affinity-canary-behavior:配置启用金丝雀发布时的粘性模式,sticky or legacy

其他功能还支持正则匹配 header ,这些都是在 k8s 组件层面上支持的功能,不需要编码。

参考: https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md
Frankcox
58 天前
@lasuar 但我们这主要用的是 AWS 的 ALB 和 GCP 的 GCE ,还有点麻烦
blackrabbit
58 天前
赞成 7 楼的理解,绝大部分灰度能力对访问发布到 S3 的文件( HTML 文件等)进行流量灰度,不会对 JS/CSS 等 CDN 的静态资源文件进行灰度,所以将静态资源 hash 即可
xderam
58 天前
@Frankcox 如果前提是没有做 version 的话,那就耐心的等待缓存到期或者全量发布完之后?我是感觉不应该用服务端的粘性会话去处理这个场景。如果 A 和 B 这种状态要长期存在,还是用一楼的方式比较好。或者四楼的那种“粘性会话”的方式也是不错的选择,这些都是业务侧可控的。而运维侧负载均衡提供的“粘性会话”是比较危险和不可控的,不太建议。以上不分前端后端的一个大原则。

不说后端 就是前端的话 。请求到新的 HTML 最好里面已经告诉浏览器去加载新的静态资源( version 为新的)。这就又回到 version 控制( hash )的问题上了。反正就是自主可控,虽然很繁琐,但打包发布工具这时候就有作用了。如果坚持不用 version 或者 hash 的方式话。上了 CDN 你又要找其它的“粘性会话”方案了。并非一劳永逸。
lasuar
58 天前
@paopjian #7 hash 变更是为了让前端刷新静态资源,但后端还有个问题就是如何让同个用户发出的多个资源请求都路由到同一组服务。

具体来说,灰度/金丝雀发布后,后端会存在 v1,v2 两组新旧版本的服务,来自同个用户的多个页面请求要么都由 v1 处理,要么都由 v2 处理,这样页面才会正常显示;比如正在上线端午活动页,结果有个用户的页面显示为:标题是“庆祝五一”( v1 服务处理),但图片显示的却是端午活动页( v2 服务处理),这是不允许出现的。
xderam
58 天前
@lasuar
@Frankcox
所以 我还是推荐版本控制或者 hash 的方式。不管是 ingress 还是 ALB CGE 甚至 nginx 让其干一件事即可,别把全村的希望都寄托在他们身上。不然最终绕了一大圈,提高了故障率和成本之后 ,还是要自己解决。别偷懒,迟早要还的。一次两次可能还凑合,但是第三次一定让你都还回来的。
xingdaorong
58 天前
我们灰度是前端打包 2 次,一个是旧的,一个是新的,服务端通过用户信息判断用户是否是在灰度名单中,在就返回对应 html ,我们 html 中有 2 个 hashjs ,1 个 css ,服务端只需要动态改这 3 个 hash 返回前端就行。
xderam
58 天前
@lasuar 根据他给我的回复,问题好像就是前端。但是前端的静态资源 HTML 里使用了并没有区分版本,使用了同样的资源。就产生了他的那个问题。
qishua
58 天前
客户端上传 cdn 的静态资源里,包含一个 hash 或者 version 文件,先客户端静态资源 cdn 上传,然后服务端这边例如 gs1-gs10 ,更新 gs1 到新的版本,然后客户端访问 gs1 的时候会拿到最新的静态资源版本号,然后会更新客户端包体,更新过后,既可访问最新的 gs1 服务及对应的最新的客户端静态资源。
Frankcox
58 天前
@xderam #12 谢谢,我又看了下我们的一个前端服务,css 和 js 这两个确实已经做了 hash ,请求路径里有?v=1231231 这种情况。我没怎么写过前端,这里我还有个问题,如果我访问的基础 document 是旧服务 B ,剩余的这些 js,css 请求被分流到了最新的 A 服务,hash 版本对不上的话会发生什么?强制刷新缓存?如果这个强制刷新的请求又被分流到了新的 A 服务上呢?
fregie
58 天前
单纯靠 Ingress 也不是不行,在网关加 header 并根据 header 路由到不同服务
但要看你的 api 网关支不支持
bli22ard
58 天前
有一种简单的不够优雅的做法,前端入口处做重定向,v1.xxx.com v2.xxx.com 按照灰度规则跳转不同的版本。
还有一种方法,把所有 js css 图片合并到一个 web 服务里面,旧的和新的静态资源全部由这个服务提供,前提条件是对文件名设置了 hash 值,index.html 按照灰度规则后端下发对应版本就可以了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1022738

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX