1 背景与解决思路
做灰度发布,主要有两个大的方向
- 在代码中做。一套线上环境,代码中做开关,对于不同的用户走不同的逻辑
- 在接入层做。多套(隔离的)线上环境,接入层针对不同用户转发到不同的环境中
来分别看下这两种方案的优缺点
方案 | 优点 | 缺点 |
---|---|---|
在代码中做 | 灵活,粒度细;一套代码(环境)运维成本低 | 灰度逻辑侵入代码 |
在接入层做 | 无需(少)侵入代码;风险小 | 多套线上环境,运维成本高 |
脱离具体的应用场景谈解决方案都是耍流氓。具体到我们的业务场景:
- 对线上的质量要求高,目前无单测,QA黑盒测试,无自动化
- 变更周期长(可接受),单次迭代功能点多
- 流量低峰期在22点后,整体回归时间长,且存在漏测情况
可以看到我们要做灰度发布的主要诉求是保证线上的质量,尽量降低因迭代带来的服务问题。而非要针对于不同的用户做AB Test。考虑到如果灰度的方案涉及到修改代码,则可能引入其他不确定的风险,在此,我们采用第二种,也就是在接入层做分流的策略。
2 具体实现方案
2.1 接入层 -> Web层
接入层采用nginx,可以基于IP或cookie的方式进行分流,由于我们是商业ERP系统,有用户登录的逻辑,自然选择基于cookie的策略。
就基于cookie的分流策略而言,又有两种实现方案
- nginx维护Cookie名单文件,每来一个请求看Cookie是否在名单中,做不同的转发
- nginx不维护Cookie名单文件,根据Cookie的特征进行转发
第一种方案的缺点显而易见,每次请求判断是否是灰度用户时间复杂度为O(N);且变更名单,需要操作接入层服务。我们采用第二种方案,具体的实现策略
- 业务中维护白名单文件(存放在数据库中)
- 在登陆时,如果用户在名单中则给用set特定标识的Cookie;退出或Session过期后Cookie失效
- nginx匹配特定Cookie,做转发
这样,调整灰度的范围,只需要操作数据库即可,无需重启服务。
2.2 Web层 -> 核心层模块
目前两层模块之间的解耦方式是通过Zookeeper,对这部分的灰度发布的实现,是通过caller, callee约定一个固定的节点名称(可以把具体的版本号写进去)来实现。
3 运维上的升级
3.1 维护灰度的机器
我们采用${index}.${platform}.${module}来管理模块和机器的对应关系,多了一个灰度的机器,在${platform}中增加一个stage平台名,代表实验环境。
实验环境也属于线上环境,需要增加对应的监控。
全流量的机器不包含实验环境的机器,独立部署,转全后全流量的流量也不会落到stage机器上(nginx的upstream.conf文件不用动)
3.2 建立灰度编译打包、部署任务
全流量的编译打包,和灰度的打出来的包,是一样的,版本号也一样。
只在灰度部署的任务中,自动更新注册节点名称。
由于要固定caller, callee的ZK节点名称,所以,强制在灰度部署任务中,增加一个stage的标记逻辑。
3.3 nginx的转发逻辑
增加对于带cookie的请求转发
location / {
# ...
if ($http_cookie ~* "SPECIALID.*|$") {
proxy_pass http://stage-cluster;
}
proxy_pass http://default-cluster;
}