高级实践(1) 基于名字服务的灰度发布
概念说明
- 定义 一般是取出一个或者多个服务器停止服务,执行更新,并重新将其投入使用。周而复始,直到集群中所有的实例都更新成新版本。
摘掉子节点,判断子节点是否还有后台任务在运行,然后子节点没有任务在执行,则执行更新发布。发布完成之后,对子节点进行检测,若节点正常,则把节点重新添加到负载当中。
前提
- 滚动更新的实现最好是基于应用的无状态,这个无需多做解释了。
- 滚动更新的兼容性,更新前后一定是存在新旧代码同时存在的情况,对业务、对应用一定能承受的新旧代码的兼容性。比如我的应用有10个节点,先更新4个节点,更新完把节点加上去,会存在同时新旧代码存在的情况。
- 由上及下,我们对开发、测试、运营、运维的要求更高了。尤其是测试环节,比如原来的生产环境是停机发布,上线灰度之后,就相当于未经生产验收直接上线生产,所有对代码的完善性,预生产环境的验收和测试要求更高。当然运维环节也是如此,上线滚动对运维侧的稳定性和要求也更高了。
实现步骤
1.根据名字摘取节点
2.判断连接状态,和任务留存情况,或者等待超时时间10分钟
3.再判断一次负载子节点是否真实的摘取,如果摘除了,则执行发布
4.执行发布完成之后验证url,如果验证正确,把节点添加回去
上图
灰度发布图
负载均衡截图
微信消息截图
背景
通过腾讯云提供的负载均衡实现,结合腾讯云提供CLI和Rexdeploy来实现基于名字服务的无缝自动发布。
安装与配置
1. 安装和配置腾讯云CLI环境
具体安装方法请参照: https://cloud.tencent.com/document/product/440/16909 注意不是tccli,而是qcloudcli
2. 初始化灰度发布相关数据
参考: https://book.osichina.net/an-zhuang/shengji.html
灰度配置
配置文件路径: config/config.yml
is_weight_allow: 1 #是否允许负载均衡下所有节点都是摘取状态,1不允许(意味着至少保证1个节点在使用) 0允许
is_weixin: 1 #是否开启微信消息 1开启 0不开启
is_qq: 1 #是否开启QQ消息 1开启 0不开启
weixin_config: '{"url": "http://微信消息API,"header": "","params": "","require":"","requirecode":"200"}' #微信消息调用接口
qq_config: '{"url": "http://QQ消息API(1)","header": "","params": "","require":"","requirecode":"200"}' #QQ消息调用接口
finish_qq_config: '{"url": "http://QQ消息API(2)","header": "","params": "","require":"","requirecode":"200"}' #QQ消息调用接口
max_sleep_time: 10 #灰度摘掉节点之后的最大等待时间
checkurl_max_count: 5 #灰度校验url最大次数
checkurl_interval_time: 10 #灰度校验url校验间隔
checkurl_init_time: 10 #灰度校验url之前的初始化等待时间
deploy_max_count: 60 #灰度应用发布校验最大次数
deploy_interval_time: 60 #灰度应用发布校验间隔
table_load_key: "load_key_sorts" #灰度发布的顺序定义数据表
max_weight_count: 8 #负载均衡修改节点的最大重试次数
如无特需,可以不修改以上参数。
QQ消息API部署参考: https://github.com/sjdy521/Mojo-Webqq
微信企业号消息API可参考: https://gitee.com/mhmrcui/WeixinAPI-PHP
或者调用自定义的消息接口,自定义消息格式如上
步骤解析
如上截图,灰度发布大体经过7个步骤。
执行灰度发布 rex deploy --k='server'
第1步 根据灰度顺序表依次发布对应系统
如上server定义的是 先发布server1,然后在发布server2
第2步 摘取节点
根据server2所关联的负载均衡,然后将server2机器所在权重改为0,如果server2绑定了2个负载均衡,同时会修改2个。
同时如果第1次修改权重失败,会触发重试机制,重试次数见配置项max_weight_count
。 如果重试了max_weight_count次数之后还未成功,就会终止灰度发布并发送消息。
第3步 超时等待时间
摘掉节点之后,等待max_sleep_time
秒。自动判断任务留存情况,参考问题章节
第4步 校验发布包和原包差异
这个步骤会校验要发布的包和原来使用的包存在哪些差异,同时也会校验配置的差异。会显示差异的数量,并不会打印出差异的详情。如(server2) 发布包原包差异: [server2程序:0]====>[server2配置:0] 0代表没有差异,程序包和配置包都为0,表示程序和配置没有任何的变化。如果为5,表示有5个文件或者目录存在差异,有可能是新增、删除、修改等
第5步 执行发布
上传程序和配置包,修改软链接,重启等。
第6步 校验url
根据server2的url的校验配置,校验改server2的接口是否正常。如果正常则继续,如果不正常就会终止灰度发布并发送消息。
第7步 添加节点
发布并校验成功,则会把节点加回去。整个灰度的发布就结束了
问题处理
我们在滚动部署过程当中,由于业务的方面的原因。当摘掉一个节点的时候,我们不能马上就对该节点进行发布的操作。因为你在摘掉节点之后有可能还有异步的任务在调用执行,所以我们只能在节点任务执行完毕之后才能对该节点进行操作。原则上,该节点有没有任务还在运行,应该是从应用本身去判断。但是业务层并没有提供对应的接口和功能,来让我们判断该应用上的任务是否都已经正常结束了。那么这样的话,我们就只能从运维的角度简单粗暴的去判读任务的留存情况。
其实杜绝这类问题的发生,我们一般是 从入口和应用方面去考虑。
入口方面: 阻止新的连接和请求过来
应用方面: 通过各种手段判断应用没有后台的留存任务还在执行
我们基本上从以下4个方面考虑
- 端口连接
- 访问日志
- 应用日志
- 超时时间
我们以后端tomcat为例
端口连接
参数:访问端口: 如8080 ; 检测端口时间间隔: 60s 结果: 单位时间内没有ESTABLISHED的连接 命令:netstat -n |awk '$4 ~ /:8080/' | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
说明: 一般情况下,有任务调用就一定会产生tcp连接,通过tcp的连接情况,可以非常粗犷的判断是不是还有任务还在执行调用,但是这种方法是非常不准确的。因为如果大部分的tcp连接是短连接,有些任务通过tcp的调用,从建立连接到释放这中间是非常的短暂的。有可能你在查询的瞬间时刻是否没有tcp连接,但是下一毫秒又有新的连接进来。当然,我们在摘掉节点后,原则上要把节点访问的端口给阻挡,比如通过防火墙或者安全组把访问应用的对外端口给拒绝。访问日志
参数:访问日志: 如/data/log/server1/access_log..2018-04-16.txt ; 检测时间间隔: 60s 前提条件:没有定期自调用 结果: 单位时间内,访问日志的md5值不在变化 命令:md5sum /data/log/server1/access_log..2018-04-16.txt
说明: 一般情况下,有任务调用就可以开启访问日志,通过访问日志的情况,可以非常粗犷的判断是不是还有任务还在执行调用,但是这种方法也不是非常精准的。当然,我们在摘掉节点后,原则上要把节点访问的端口给阻挡,这样按道理是不会有新的请求过来。应用日志
原理和命令等同上 说明: 通过判断应用日志来判断应用是否还有留存任务在运行,这样的方式对应用有一定的要求,有任务在运行一定会有日志,没有任务或者没有定期执行的任务在日志中记录。超时时间
说明: 基于以上,我们需要一个超时时间,如果后台留存任务一直在运行,我们总不能无止境的等待下去,所以过了超时时间之后,不管是否还有没有留存任务,我们都判定任务已经结束。