V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Frankcox
V2EX  ›  程序员

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

  •  
  •   Frankcox · 238 天前 · 2998 次点击
    这是一个创建于 238 天前的主题,其中的信息可能已经有所发展或是发生改变。
    比如我设置了新发布的服务的权重为 10%,之前旧服务的权重为 90%,现在我尝试访问一个前端服务,路由将其指向了新环境,但是浏览器同时请求 js,css,img 等资源,而这些资源则被路由指向了旧服务,导致了页面无法正确显示,这种情况有什么好的解决方式吗?比如粘性会话?
    26 条回复    2024-03-13 11:08:39 +08:00
    MelodYi
        1
    MelodYi  
       238 天前
    相比粗暴地取随机比例,可以考虑用取模之类的方式筛选一部分固定的用户选择新版本。类似 A 、B 测试的感觉。
    coderxy
        2
    coderxy  
       238 天前
    楼上+1
    Frankcox
        3
    Frankcox  
    OP
       238 天前
    @MelodYi 这种高级一些的确实能好些,不过应该需要开发侧进行适配吧?目前我们考虑做个基础版本的,尽量不用开发进行修改。
    Seulgi
        4
    Seulgi  
       238 天前   ❤️ 1
    你说的粘性会话是一个方案.1 楼说的也是一种方案,一般为了精准灰度用户,会根据用户数量做分组,然后组内用户进灰度。粘性会话其实就是在每次请求里加上标志位,header 也好,session 也好,cookie 也好,然后网关处理路由。
    Frankcox
        5
    Frankcox  
    OP
       238 天前
    @Seulgi 谢谢,这么看还是需要开发那里做对应的灰度适配支持,单纯靠 Ingress 处理不太行
    xderam
        6
    xderam  
       238 天前
    想问下,这种的情况不是应该返回新的静态资源版本吗?例如 http://domain/a.png?v=123 灰度只灰度了后端,前端最好也同步控制。而不是用粘性会话来解决。粘性会话感觉更合适的场景是后端“异常”的时候的方式。
    paopjian
        7
    paopjian  
       238 天前
    这个问题不应该是资源的缓存问题吗, 我们直接给 js css 加了随机 hash,每次发布都变化, 这样可以强制刷新
    Frankcox
        8
    Frankcox  
    OP
       238 天前
    @xderam 不好意思,我没太看懂,我理解这个问题反而后端服务会好一些,不用特殊处理吧?比如通常情况下单个 api 请求被分流到了 A 服务那就是 A 服务。相反,对于前端的服务,除了 HTML 还有剩余的一堆静态资源,这种多次请求会因为权重规则导致分裂。当然我指的现在的服务都是没做 version 处理适配灰度这种情况
    lasuar
        9
    lasuar  
       238 天前   ❤️ 2
    最佳的办法当然是权重+粘性会话。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
        10
    Frankcox  
    OP
       238 天前
    @lasuar 但我们这主要用的是 AWS 的 ALB 和 GCP 的 GCE ,还有点麻烦
    blackrabbit
        11
    blackrabbit  
       238 天前
    赞成 7 楼的理解,绝大部分灰度能力对访问发布到 S3 的文件( HTML 文件等)进行流量灰度,不会对 JS/CSS 等 CDN 的静态资源文件进行灰度,所以将静态资源 hash 即可
    xderam
        12
    xderam  
       238 天前
    @Frankcox 如果前提是没有做 version 的话,那就耐心的等待缓存到期或者全量发布完之后?我是感觉不应该用服务端的粘性会话去处理这个场景。如果 A 和 B 这种状态要长期存在,还是用一楼的方式比较好。或者四楼的那种“粘性会话”的方式也是不错的选择,这些都是业务侧可控的。而运维侧负载均衡提供的“粘性会话”是比较危险和不可控的,不太建议。以上不分前端后端的一个大原则。

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

    具体来说,灰度/金丝雀发布后,后端会存在 v1,v2 两组新旧版本的服务,来自同个用户的多个页面请求要么都由 v1 处理,要么都由 v2 处理,这样页面才会正常显示;比如正在上线端午活动页,结果有个用户的页面显示为:标题是“庆祝五一”( v1 服务处理),但图片显示的却是端午活动页( v2 服务处理),这是不允许出现的。
    xderam
        14
    xderam  
       238 天前   ❤️ 1
    @lasuar
    @Frankcox
    所以 我还是推荐版本控制或者 hash 的方式。不管是 ingress 还是 ALB CGE 甚至 nginx 让其干一件事即可,别把全村的希望都寄托在他们身上。不然最终绕了一大圈,提高了故障率和成本之后 ,还是要自己解决。别偷懒,迟早要还的。一次两次可能还凑合,但是第三次一定让你都还回来的。
    xingdaorong
        15
    xingdaorong  
       238 天前
    我们灰度是前端打包 2 次,一个是旧的,一个是新的,服务端通过用户信息判断用户是否是在灰度名单中,在就返回对应 html ,我们 html 中有 2 个 hashjs ,1 个 css ,服务端只需要动态改这 3 个 hash 返回前端就行。
    xderam
        16
    xderam  
       238 天前
    @lasuar 根据他给我的回复,问题好像就是前端。但是前端的静态资源 HTML 里使用了并没有区分版本,使用了同样的资源。就产生了他的那个问题。
    qishua
        17
    qishua  
       238 天前
    客户端上传 cdn 的静态资源里,包含一个 hash 或者 version 文件,先客户端静态资源 cdn 上传,然后服务端这边例如 gs1-gs10 ,更新 gs1 到新的版本,然后客户端访问 gs1 的时候会拿到最新的静态资源版本号,然后会更新客户端包体,更新过后,既可访问最新的 gs1 服务及对应的最新的客户端静态资源。
    Frankcox
        18
    Frankcox  
    OP
       238 天前
    @xderam #12 谢谢,我又看了下我们的一个前端服务,css 和 js 这两个确实已经做了 hash ,请求路径里有?v=1231231 这种情况。我没怎么写过前端,这里我还有个问题,如果我访问的基础 document 是旧服务 B ,剩余的这些 js,css 请求被分流到了最新的 A 服务,hash 版本对不上的话会发生什么?强制刷新缓存?如果这个强制刷新的请求又被分流到了新的 A 服务上呢?
    fregie
        19
    fregie  
       238 天前
    单纯靠 Ingress 也不是不行,在网关加 header 并根据 header 路由到不同服务
    但要看你的 api 网关支不支持
    bli22ard
        20
    bli22ard  
       238 天前
    有一种简单的不够优雅的做法,前端入口处做重定向,v1.xxx.com v2.xxx.com 按照灰度规则跳转不同的版本。
    还有一种方法,把所有 js css 图片合并到一个 web 服务里面,旧的和新的静态资源全部由这个服务提供,前提条件是对文件名设置了 hash 值,index.html 按照灰度规则后端下发对应版本就可以了
    dyllen
        21
    dyllen  
       238 天前
    借楼问下,灰度存在新旧两个版本的情况下,后端数据库新旧两个版本是如何处理?如果要回滚新数据怎么办?
    MelodYi
        22
    MelodYi  
       238 天前
    @Frankcox nginx-ingress 这类是可以的。
    原始流量里有 userid 之类的指标,取个模。
    你还能自行调整过滤的比例。第一天 10%,第二天 50%之类的。
    再复杂的也能走 lua 脚本之类的搞定的七七八八。
    F7TsdQL45E0jmoiG
        23
    F7TsdQL45E0jmoiG  
       238 天前
    好歹用源 ip 做 hash ,纯随机不靠谱
    gotosre
        24
    gotosre  
       238 天前
    如果带 hash/特征资源的先上传,最后上传 index 页面,是不是也可以解决
    然后 cdn 上保留至少 2 个版本的前端资源
    无论用户加载到哪个 index.html ,都能找到资源了呢
    不知道这个思路对不对
    whileFalse
        25
    whileFalse  
       238 天前 via Android
    设置粘性。
    另外静态文件应该使用对象存储+哈希文件名,也就是发版之后任何版本的静态文件应该都存在于静态资源路径里。
    xderam
        26
    xderam  
       237 天前
    @Frankcox 旧服务的静态资源先不要下掉,一般来说这种小文件放到 s3 或者 cdn 上也花不了几个钱。除非有洁癖 哈哈哈。前端的版本控制和 hash 可以搜索下,网上一般有介绍。大多数是不用自己写,前端打包的时候根据 git 的相关信息自动生成的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1009 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 21:15 · PVG 05:15 · LAX 13:15 · JFK 16:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.