侧边栏壁纸
博主头像
木小风的博客博主等级

行动起来,活在当下

  • 累计撰写 20 篇文章
  • 累计创建 19 个标签
  • 累计收到 9 条评论

目 录CONTENT

文章目录

构建可回滚的应用及上线checklist实践

木小风
2021-11-20 / 0 评论 / 0 点赞 / 2536 阅读 / 5240 字

构建可回滚的应用及上线checklist实践

一、背景

在互联网分布式应用中,如果上线的新版本有bug又不能回滚止损,带来的后果将是灾难性的。因此做到上线可回滚以及上线前的checklist是保证服务稳定性的基本要求。

在简单的场景里直接回滚到上一版个版本即可,但是如果涉及多个上下游和组件、考虑多版本兼容,就需要有好好设计下如何构建可回滚的代码,充分验证后还需要仔细检查上线checklist,最大程度保证线上服务的稳定性。

二、构建向前兼容的代码

回滚指的是程序或数据处理错误,将程序或数据恢复到上一次正确状态的行为。在回滚之后,程序依然能够正常处理,称为可回滚。

不可回滚原因大多是旧程序不能处理新数据,同时新数据又不能丢弃,代码回滚导致旧业务逻辑出错。

保证向前兼容的手段:

1、数据库变更

新加字段:设置默认值,默认值要保证新旧代码逻辑的语义一致性。比如用户表添加了用户状态,默认值要设置为默认有效。

删除字段:新版本全量发布后,最后迭代2-3个版本后,再删除无用字段,同时要做好数据库备份。

新加唯一约束:首先要确保原有的数据值没有重复的,再添加唯一索引,可使用以下SQL验证:

select field,count(1) from table group by field having count(1) > 1

复杂数据库字段变更:

  • 通常做法是:双写读旧 -> 新字段离线验证 -> 旧字段全量拷贝至新字段 -> 双读旧为主diff -> 双读新为主diff -> 写新读新 -> 移除旧字段和逻辑;
  • 出问题之后,只回滚程序,不回滚数据。

2、对外提供服务API变动设计

(1)对外提供RPC服务

入参新加字段:设置为可选的,如果没设置值,要有老业务逻辑兼容代码

返回值字段:设置为可选的,如果是新版本肯定有值字段,需要在字段描述文档里做好备注

如果入参和返回值结果差异较大,建议新建一个RPC方法,逐渐把业务方调用迁移到新方法

(2)对外提供HTTP服务

HTTP接口和RPC接口的不同是没有强制的约束,数据交换大多采用Json形式,虽然灵活性强,但是约束力低给管理带来很大的成本。因此HTTP接口文档必须给出类似RPC一样的规范,比如Swagger等工具,比如给指定字段名、类型、是否必填、不填写的默认值等。

3、多版本发布

对于客户端API服务或者基础服务来说,用户升级会有很大的延迟,对于服务提供方来说则需要尽量保证多版本同时可用。最好在最开始设计接口的时候就添加version字段,保证以后的扩展。

4、静态资源发布

因为静态资源一般放在CDN上缓存时间设置的比较长,比如1个月。这样假设发布的版本有问题,需要清理CDN缓存,并且也需要清理浏览器缓存,而且因为存在版本覆盖的问题,即使覆盖了也不一定保证是操作正确了。

现在的工具默认都会把md5设置到js、css文件名中,可有效的避免以上问题。

image-20211120215947571

三、上线checklist

尽管准备工作做的再好,难免也会出现疏漏,因此指定上线前checklist,组内同学交叉评审,保证上线前的最后一步,是很有必要的。

发布策略一般是从下游逐步向上部署。而回滚方案相反,一般是从上游往下回滚。

通用的checklist需要考虑以下几方面:

1、依赖

(1)存储(数据库、分布式缓存)

是否有数据库变更需要代码上线前先提交,并且验证执行成功。

(2)动态配置

首先需要确认代码中的动态配置有无默认值,没有默认值需要先在动态配置系统上添加相关的配置。

(3)下游

确认下游jar包版本是否正确,线上最好使用release包。

确认下游的业务方是否已经上线。

(4)鉴权

美团内部的服务调用都有鉴权,检查鉴权是否已经添加并且已生效

2、上游

(1)Nginx层

确认路由策略,在发布过程中会不会造成用户不断刷新页面命中不同代码逻辑,给用户带来用户体验上的问题和困扰。

(2)上游业务方

确保和上游业务方沟通好,等当前服务全量发布后且观察、验证没问题再通知上游发布。

3、业务内部

通过代码review,QA测试等方式保证上线对现有业务逻辑没有影响。

​ 如有必要可建设业务的checklist规范

  • 代码review:重点查看代码规范、业务逻辑的正确性、对相关功能的影响

  • 测试用例:测试相关代码的正常逻辑、边界条件、其他功能不受影响,如果写好测试用例请参考其他专业的文档

  • 自动化测试:有条件的团队建议添加自动化测试,来保证核心业务流程的正确性

四、可回滚发布

1、回滚方案

需考虑以下几方面:

  • 是否有动态开关、流量控制可以一键恢复到旧版本逻辑
  • 回滚代码会不会导致上游调用失败
  • 回滚代码:根据docker镜像、Git commitId、Git tag回滚

2、可回滚验证

方案设计要考虑可回滚性;通常要在test或预上线环境验证(演练)可回滚性。同时QA同学要把可回滚作为质量验收的一部分。

3、平滑升级

(1)线上验收

通过staging环境或线上灰度链路, 进行线上验收。

(2)蓝绿发布

线上部署两个集群,每个集群都能抗住所有的流量,发布的时候一个集群接受用户请求,等当前集群全部发布成功后,再把流量全部迁移到新版本集群。

优点:可保证新版本特性同一时间对所有用户生效;保证系统高可用,一个集群出问题,可快速切换到备用集群;

缺点:日常使用的机器只有一半,造成浪费;

蓝绿发布的高配方案:部署两套集群,通过Nginx层动态切换集群。

蓝绿发布的低配方案:用动态开关控制新旧逻辑,等新版本全量上线后,再切换到新代码逻辑。

image-20211120220311570

(3)灰度发布(金丝雀发布)

采用金丝雀部署,可以在生产环境的基础设施中小范围的部署新的应用代码。一旦应用新发布,只有少数用户被路由到它。最大限度的降低影响。检测新版本没问题,再逐步扩大发布范围直至全量。

实现灰度发布,需要部署系统设置分批发布,同时必须有监控、报警等相关的系统的配合。确定没问题后再扩量。

还有一点需要注意的就是灰度路由策略,一般服务中用的多是的随机策略,这样可能导致用户在发布过程中不断刷新页面,可能会来回命中新老版本逻辑,影响用户体验。避免方式有以下几种:

  • 采用用户ID做路由因子,问题是新增、删除机器时,算法匹配的机器会变,就需要引入一致性Hash算法,复杂性较高
  • RPC路由策略一般不能执行路由规则,就需要程序自己判断。常用做法是采用动态配置设置百分比值,算法如下:
userId % 100 < $dynamicTrafficPercent ? oldVersion : newVersion
(4)AB测试

AB测试虽然也有流量控制的功能,但一般是用于验证不同版本之间的性能、用户体验、效果数据等的手段。是上线后由运营控制的。

AB测试和灰度发布最大的不同是AB测试是通过桶的方式分配流量。

4、监控报警

常用的发布相关的监控报警包括:

  • 新、旧版本的流量及百分比
  • 对外提供的接口的性能指标、下游的性能指标(TP50、TP90、TP99、TP999)
  • 异常指标及报警
  • 业务指标

5、发布完成周知

  • PM: 周知PM关注业务指标、客诉等
  • 上游:如果上游依赖此发布,周知上游开始发布
  • 下游:周知下周注意流量增量及耗时等情况

五、上线步骤及规范

1、不在高峰期上线,如必须上线,要发邮件申请,同时降低并发度

2、上线前周知

3、保证能回滚

4、发布过程采用分组发布(强制)

5、上线过程中观察系统指标、业务指标、异常指标等;如有异常立即禁用已发布机器

6、上线后周知PM、上下游等

7、有异常第一时间回滚

六、总结

从本文可以看出,保证线上稳定性是一个复杂且系统的工程,需要从技术规范、流程规范、周知、检查、工具支持等各个方面来保证。

作者简介:木小丰,美团Java高级工程师,公共号只发原创精品文章,专注软件研发过程中的实践、思考。欢迎关注公共号:Java研发

img

历史文章推荐
Maven依赖冲突问题排查经验
升级Java17问题记录
使用Groovy构建DSL
Gradle最佳实践

0

评论区