一个简单的灰度发布思路,求指正 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
请不要在回答技术问题时复制粘贴 AI 生成的内容
CRH

一个简单的灰度发布思路,求指正

  •  
  •   CRH Jun 2, 2020 5115 views
    This topic created in 2165 days ago, the information mentioned may be changed or developed.

    背景

    • 小公司,技术力量一般
    • to B 服务,可用性要求高
    • 服务仅提供 APP 端
    • 可以强制客户升级 APP 版本

    目标

    • 旧版本的 APP 连接至新版本的后端,可能会导致不可预知的 bug,没有人手做这方面的兼容性测试
    • APP 和后端同步升级,可能会导致未测出的 bug 大面积出现
    • 以前都是半夜上线,测试小哥连夜验证,如有问题再连夜回滚版本,累
    • 不想修改太多代码

    实现

    环境

    • 分生产( prod )、预发布( pre )环境
    • 两个环境的 API 入口分别为 prod.xxx.compre.xxx.com
    • prod 和 pre 分别部署在两组不同的应用服务器上(组 1 & 组 2 ),连接至同一组数据库服务器

    新增接口

    接口 A

    • APP 每次启动(登录)时,访问 prod 环境的接口 A
    • 接口 A 会根据用户 ID 返回应安装的 APP 版本
    • 如接口 A 返回的版本与本地版本不符,会强制客户更新 APP

    接口 B

    • APP 每次启动时,访问 prod 环境的接口 B
    • 接口 B 根据 APP 版本号返回应该连接的环境
    • 比如生产环境是 v1.0,新发布了 v1.1,那么 v1.0 访问接口 B 会返回 server:prod.xxx.com ,v1.1 会返回 server:pre.xxx.com

    上线过程

    • 假如生产环境在运行 v1.0 版 APP,新发布了 v1.1 版 APP
    • 新版本上线时,将 v1.1 版的后端代码发布至 pre 组(组 2 )应用服务器,此时 pre 组没有流量
    • 配置 prod 环境的接口 A,给部分友好用户返回 version:v1.1,大多数客户仍然返回 version:v1.0
    • 友好用户会被强制更新到 v1.1 版 APP,这部分流量由 pre 组服务器承担
    • 友好用户经过一段时间的试用,如果发现问题:
      - 配置接口 A 全部返回 version:v1.0
      - 这些用户就会被强制更新回 v1.0
    • 如果试用没有问题:
      - 逐日依据客户重要程度,分批配置接口 A 返回的数据,返回 version:v1.1,并相应增加第 2 组服务器资源
      - 在接口 A 对全部客户均返回新版本号后,等待数日(保证全部客户已更新),配置接口 B,对所有版本均返回 server:prod.xxx.com
      - 修改负载均衡设置,将 prod.xxx.com 指向第 2 组服务器
    • 将第 1 组服务器(原 prod 组)下线

    补充说明

    • 其实也不是什么新思路,只是比较容易实现
    • 使用推送代替接口 A,可以更好地控制客户使用的版本,避免版本分裂
    • 新版本只能在数据库上新增字段,不能删除或修改已有字段
    • 其实安全起见,pre 环境应该使用独立数据库 & 双写至 prod 数据库的,不过感觉比较复杂,打算先不做

    问题

    • 有现成的轮子么?
    • 这个做法有啥隐患?

    请指教,谢谢!

    25 replies    2020-06-03 21:50:20 +08:00
    Aruforce
        1
    Aruforce  
       Jun 2, 2020
    目标这个应该改成存在的问题吧...

    想做到数据安全升级的话,就不能略过,需要完成老 APP 在新 API 下的兼容测试;
    略过之后如果存在兼容 bug 那肯定存在数据错误需要修复;

    你这种操作是啥?拿一部分用户献祭?脏数据修复搞死你。。。
    而且不同代码使用同一套数据环境。。。确认表结构是兼容的?
    lovedebug
        2
    lovedebug  
       Jun 2, 2020
    我们一般都会用特性开关,打开部分用户测试。或者用旁路引流部分到 duplicator 环境测试
    scukmh
        3
    scukmh  
       Jun 2, 2020 via iPhone
    @Aruforce 楼主有提到只允许新增字段,不允许删除字段呀。
    firefox12
        4
    firefox12  
       Jun 2, 2020
    既然可以强制前端升级,那么 协议一般都是这么搞。

    客户端第一个命令 都是 告诉服务器 我的版本号 客户 id 。
    服务器给的结果都是 1. 可以继续 你去连这个服务器 pre
    2. 可以继续 你去练这个服务器 pro

    服务器 可以根据这个客户 id 做灰度,简单的 比如 id <10000 的 是你们测试的 id, 那么你们就可以用自己的 id
    在 pre 服务上测,大部分人都继续用 pro 的。
    等测试好了 把 pro 升级,把服务器的的 灰度关掉, 服务器的结果就变成
    1. 如果不是新版本 就必须升级。
    2. 已经是新版本 请走 pro.
    pmispig
        5
    pmispig  
       Jun 2, 2020
    你这个我可以给你提供一个更简单的思路。
    在 app 所有的 get/post 等请求里面,带一个 version=1.0 这样的头部,然后用 openresty 根据头部来做路由就行了。
    想让部分用户升级的话,就是另一个问题了,这个很简单。
    CRH
        6
    CRH  
    OP
       Jun 2, 2020
    @Aruforce 对……应该是存在的问题。
    曾经出现过测试阶段没有测出的 bug,在实际使用中出现了,就要紧急回滚版本 + 被很多客户骂。
    少部分“友好”客户,就是和我们关系比较好的客户,上线初期可能只有一两家。
    我们甚至可以派客服远程一直看着他们的屏幕,有问题就及时协助处理。
    脏数据修复确实是个问题
    CRH
        7
    CRH  
    OP
       Jun 2, 2020
    @pmispig 谢谢,是说放在 request header 里面对吧
    pmispig
        8
    pmispig  
       Jun 2, 2020
    @CRH 是的
    aut0man
        9
    aut0man  
       Jun 2, 2020
    @pmispig 抱歉哈…提问下(为我的菜而先行道歉,代码能力弱)你这个 version=1.0 的头部能解决什么问题呢?这样大家可以直接访问到 1.0 版本的 prod,而不是 pre ?…
    @CRH 楼主想解决的是,不想每次更新后彻底测试彻底上线,想整个类似热更新一样的东西?大家更新了,就是新 feature,不更新 旧版的也能用 是这样吗?
    whileFalse
        10
    whileFalse  
       Jun 2, 2020
    用得着这么复杂?
    启动的时候请求后端:
    get api.xxx.com/boot?client_version=1.0.1&channel=alpha&user_id=12345678
    返回:
    {
    "latest_version": "1.0.2",
    "force_upgrade": false,
    "api_base": "https://prod.api.com/develop"
    }
    whileFalse
        11
    whileFalse  
       Jun 2, 2020
    后续请求都用 api_base 拼 url 就行了。
    pmispig
        12
    pmispig  
       Jun 2, 2020
    @aut0man 解决的问题是简化你这个切来切去,把简单问题复杂化了
    CRH
        13
    CRH  
    OP
       Jun 2, 2020
    @aut0man 我们都会在测试环境做比较彻底的测试的
    出发点是:
    - bug 总有漏网之鱼,如果是严重 bug 会影响所有客户
    - 先让小部分和我们关系比较好的客户用新版本,使用一段时间没有问题了,再让所有客户更新到新版本
    guyskk0x0
        14
    guyskk0x0  
       Jun 2, 2020 via Android
    服务端兼容两个版本 App 不就好了,越简单越安全。

    假设当前是 app v1 + server v1,server 升级到 v2 时完全可以兼容一下 v1,新功能一律加开关,出问题立即切回 v1 实现。

    然后 app 可以灰度更新到 v2,有问题就卸载安装回 v1 。

    下一次 server 升级 v3 之前,再要求所有 app 都升级到 v2 。
    shuangya
        15
    shuangya  
       Jun 2, 2020 via Android
    首先,虽然说有强制升级 App 的能力,但这个给用户的体验太差了,没有必要还是不要使用最好。
    楼主的方式改造是可行的,但是需要考虑一些问题,一是强制升级体验会很差,二是如果不强制升级,就有维护多个版本的成本。
    说说我们的大概做法:如果接口更改是向下兼容的,只是加了一些字段什么的,一般不会改 URL 。因为迭代频繁的时候,每次都改 URL 成本太高。这种情况下,一般是正式一批服务器,灰度一批服务器,根据特定条件(比如 ID 范围、按所属企业啥的),统一代理把它转发到正式 /灰度服务器。如果出现问题,只需要把转发重新改回线上就可以快速回滚。
    如果是不向下兼容的,那一般会发布为一个新的接口,可以加上版本标志什么的。这个时候灰度其实就不需要后端做什么了,只需要客户端分批升级就行。
    shuangya
        16
    shuangya  
       Jun 2, 2020 via Android
    举个例子,为什么说强制更新尽量少用。
    你们的客户公司,老板正在出差,网络状况不太好。这个时候,他需要审核一点资料啥的。然后他点开了 App,很不巧,他被灰度到了,如果不更新就不能使用。然而他的网络情况让他下了好几遍新版本也没有下载成功。
    很显然,他不一定要新版才能完成的工作,却因为强制升级而不能完成了。
    对于 to B 来说,需要更多考虑用户体验,而不需要额外的营销手段留住客户。所以需要尽可能考虑 B 端客户的各种情况,能让客户正常使用是最重要的。
    比如弱网,异地 /跨国漫游……这类 to C 产品不用太关心的因素。
    Foxkeh
        17
    Foxkeh  
       Jun 2, 2020 via iPhone
    不敢搞灰发,因为
    化肥会挥发。黑化肥发灰,灰化肥发黑。黑化肥发灰会挥发;灰化肥挥发会发黑。黑化肥挥发发灰会花飞;灰化肥挥发发黑会飞花。黑灰化肥会挥发发灰黑讳为花飞;灰黑化肥会挥发发黑灰为讳飞花。黑灰化肥灰会挥发发灰黑讳为黑灰花会飞;灰黑化肥会会挥发发黑灰为讳飞花化为灰。黑化黑灰化肥灰会挥发发灰黑讳为黑灰花会回飞;灰化灰黑化肥会会挥发发黑灰为讳飞花回化为灰。
    okboom
        18
    okboom  
       Jun 2, 2020
    @Foxkeh 大兄弟,笑死我了
    chihiro2014
        19
    chihiro2014  
       Jun 2, 2020
    先分 10%的流量给新版本,剩下 90%给旧版本,稳定了再上线
    ica10888
        20
    ica10888  
       Jun 3, 2020
    这个叫金丝雀发布吧...
    ica10888
        21
    ica10888  
       Jun 3, 2020
    我的,金丝雀发布是灰度发布的别名,感觉金丝雀这个比喻很合适...
    aut0man
        22
    aut0man  
       Jun 3, 2020
    @guyskk0x0 我读懂你这个了 浏览下来觉得你这个是实现比较方便且靠谱的:D thanx 学到了
    Ianchen
        23
    Ianchen  
       Jun 3, 2020
    Istio 了解下,或者实在不行就按用户来走流量分发啊,就像内测用户与公测用户进的服务器不同,但是这个实际上是后端类似网关一类的服务去分配的。至于 App 是否升级这与灰度无关吧
    CRH
        24
    CRH  
    OP
       Jun 3, 2020
    @shuangya 谢谢哈,前面对我们的产品描述得不够仔细,我的锅

    我们的产品是类似于超市里自助结账的设备,给服务员 /顾客操作的,也就是说设置在固定的地点,有固定的营业时间
    强制更新可以放在半夜没有人用的时候,正常情况下可以做到无感
    shuangya
        25
    shuangya  
       Jun 3, 2020
    @CRH 如果是这样的话,强制更新也是可行的,但是还是要考虑回滚、更新失败的降级措施(至少保证旧版本可以用)
    总的来说,在服务端进行灰度比较可控一些,如果是推送到端上再发现问题,回滚起来很麻烦(比方说你们推送了一个新版本,白天开始用之后才发现后端的新代码没考虑到一些 case,这个时候靠强制推送来回滚比较麻烦)
    About     Help     Advertise     Blog     API     FAQ     Solana     5742 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 134ms UTC 08:26 PVG 16:26 LAX 01:26 JFK 04:26
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86