做web的灰度发布是我一直关注的问题。传统的web灰度发布一般有几种方式:基于IP解析或者负载均衡器粘性会话的分发,或者通过Nginx做基于IP或者cookie的分发。其中,只有用Nginx做基于cookie的分发才能做到业务可控。

相比于其他方式,业务通过cookie控制灰度策略具有显著优势。首先是稳定:可以让登录用户在不同浏览器或切换IP后仍访问同一灰度环境。其次是可控:比如腾讯根据QQ用户的尾号做灰度发布,以便按指定百分比向用户推送新版本;Chrome和Firefox浏览器都允许用户自行选择使用稳定版或者Beta版更新通道(虽然是客户端并非web);或者简单地让公司内部账号能够访问新版本。

以前很多公司使用Nginx做负载均衡器,通过配置或者集成Lua脚本做灰度发布是很自然的。在云环境下,越来越多的公司选择使用云原生的负载均衡器。如果再使用Nginx作灰度就引入了额外的复杂度、成本和故障点。

用ALB代替Nginx

AWS ALB于近日推出了高级路由策略,可以藉由http header分发请求。只要为用户添加一条cookie,比如env=new,再添加一条转发规则,即可实现基于cookie的灰度分发。

如果你的HTTP网站本来就使用了AWS ALB,那么你现在可以简单地配置好流量分发规则,并为来访用户自动添加识别身份的cookie。例如有两个环境,并希望分配30%流量到新环境中:

  1. 当用户登录时,按照其数字uid的尾号添加cookie。这里假设用户uid的尾号均匀分布。
  2. 尾号为0,添加一条cookie“tail0=0”;尾号为9,添加两条“tail0=0;tail9=9”;尾号为8,添加三条“tail0=0;tail9=9;tail8=8”;以此类推。
  3. 添加转发规则,条件为“Http header Cookie is*tail3=3*”,如图。

uid尾号为1、2、3的登录用户都具有tail3=3这个cookie,因此达到目的。当我们想修改灰度比例时,只要修改ALB转发规则即可。该过程只需业务在登录时添加一条cookie,代码侵入很少。而转发规则远比维护Nginx配置或Lua代码容易。ALB完全可以代替Nginx。

发挥想象力

ALB可以做基于路径的分发,这意味着同一个域名下可以由不同service提供服务,甚至实现http层的拦截器。通过添加一个独立于业务的“灰度发布服务”,熟悉开发的小伙伴可以在运维层面玩出花儿来。

零代码侵入

上一个例子里我们依赖业务代码在登录时设置的cookie,这是对业务代码的侵入。为了完全消除侵入,需要将设置cookie的动作卸载到“灰度发布服务”上。我们使用cookie“env”进行分发控制。未设置“env”的请求将被转发至“灰度发布服务”。如图:

灰度发布服务执行以下操作:

  1. 为用户随机分配一个env值,或者根据业务API请求一个env。
  2. 将用户原始请求附带env cookie后再次发送,此请求会命中业务服务并执行用户需要的操作。
  3. 将业务服务的返回值,加上set cookie头返回给用户,为用户添加上env cookie。

这样就可以在对业务和用户均透明的情况下为用户分配一个灰度环境。

用户主动选择环境

可以将某个子路径导向“灰度发布服务”。如图:

"blog.just4test.net/.canary-deployment" 通过页面展示当前可用的环境名称并允许用户主动切换。

另外,当一个ALB承载了相关联的多个业务域名时,也可以通过“灰度发布服务”成组切换多个域名的环境类型。

主动切换环境时也切换cookie上下文

对于常见用法来说,灰度的两个环境应该是cookie等价的。以我司举例:业务服务是无状态的,staging和production环境又使用相同的缓存和数据库,因此session通用,在staging和production间进行灰度切换后不影响用户的登录状态等信息。

但对于某些特殊用法——比如将dev和qa环境也纳入灰度主动切换的范围——不同环境的session id并不通用(存放在不同的缓存服务中),因此切换后会丢失用户登录状态。此时通过“灰度发布服务”将当前环境的cookie保存在local storage中,并从local storage中还原新环境的cookie。值得注意的是,cookie有多种特性字段,读出session时只能读取其中的name和value,部分cookie只能从服务端读出,部分cookie只能在https环境下读出。这个操作可能造成部分cookie丢失导致页面工作不正常、cookie可见性变更导致安全隐患等,因此不适合在生产级服务中提供。

通常来讲,在灰度中纳入不等价的多个环境是很奇怪的,不符合灰度的初衷,并不推荐这么做——相比于把生产域名切换到dev环境,直接访问dev域名更加合适。

一个有理有据的目的是打包web套壳app。我司的app大部分是web套壳,app里面要配置服务url,dev、qa、staging、production四个环境就要打包四次,而QA也要安装四次,令人烦躁不说还经常分不清手机上的哪个应用到底是哪个版本。引入主动环境切换,可以一个包从dev用到生产;可以向用户提供“稳定版”和“测试版”的选择权,并允许公司内部用户切换到dev和qa环境。这种选择权对于后端完全无侵入;对于app端虽然有非常小的侵入但能解决一个痛点,因此易于推广。