应用不停机发布的思考与初识 | IDCF
来源:技术奇妙物语作者:陈俊应用发布,简单来说就是将已开发完成的系统功能部署到生产环境,并可正常对用户提供服务。传统的应用发布步骤一般采用“三步曲”:
第一步:停止应用
第二步:更新应用
第三步:启动应用
传统行业的业务一般都集中的日间,也就是说凌晨基本没有业务,或者非重要业务。
就算凌晨无法使用这些功能,觉得也没关系,第二天再来就是了,客户又不会跑了。
“全天”代表着任何时间
“优质”代表着客户体验
“线上”代表着线上自助
Part 1
应用不停机发布,从字面上很好理解,就是应用发布过程中服务不中断。
但仔细回想一下,原先应用发布过程中,反正服务会中断,那就在应用发布完成后,通过网络屏蔽外部流量的方式,先进行核心或常用功能的内部验证,在确保没有问题后,再解除网络屏蔽,并对外提供服务。
那现在呢?应用发布后直接对外?不需要进行内部或小流量验证?想必每个人的答案都有所不同。对此,可以对应用不停机发布能力,简单定义三个成熟度。
成熟度 | 成熟度能力 |
成熟度一级 | 应用发布过程服务不中断 |
成熟度二级 | 应用发布过程服务不中断,单应用功能可先进行内部/小流量验证 |
成熟度三级 | 应用发布过程服务不中断,多应用(联动)功能可先进行内部/小流量验证 |
注:不同的成熟度对技术能力的要求会有所不同,建议通过逐步演进的方式来提升成熟度,并在演进过程中对不同的技术能力进行验证,从而不断完善。
有人会说蓝绿发布,或有人会说滚动发布,灰度发布就可以实现以上能力。但其实,这些答案并不准确,或者说并不全面,它们虽有交集,但无法涵盖。
既然提到发布模式,那我们先来简单比较一下几种发布模式的优缺点。
蓝绿发布 | 滚动发布 | 灰度发布 | |
资源成本 | 生产等比例资源投入,成本较高 | 基本无需额外资源投入,成本较低 | 基本无需额外资源投入,成本较低 |
回滚时长 | 两套环境直接切换,时长较短 | 根据滚动速率决定,时长较长 | 灰度部分实例回滚,时长适中 |
技术难点 | 资源完全隔离,技术难点较低 | 需制定滚动策略,技术难点适中 | 引入流量路由能力,技术难点较高 |
影响范围 | 影响所有用户,影响面较大 | 影响所有用户,影响面较大 | 影响少量用户,影响面较小 |
注:大部分的技术文章里都会提到,可通过以上任何一种发布模式,做到用户无感知的应用发布,但在实际情况下,并不完全足够,还需要通过一些辅助手段组合在一起才能实现。
结合以上发布模式的优缺点,如果你的组织在基础架构技术能力上已经非常成熟,那么灰度发布一定是最佳之选,但如果还处于初级阶段,那可能并不是,而蓝绿发布的资源投入较高,也不是一种非常好的选择。
剩下的只有滚动发布,但滚动发布的发布策略会比较复杂,特别在容器环境下,同一应用多个服务实例的滚动策略还需要单独来实现。
所以,前期可以选择一种折中的方式,即:单独搭建一套预发验证环境,应用架构需对齐生产环境,但容量方面可以最小化,一般一个应用至少2个服务实例。
这样,我们就可以在对生产环境做应用发布前,事先对生产预验证环境进行应用发布,发布后仅对内部用户可见,验证通过后,再对生产环境采用全量应用发布。
Part 2
应用不停机发布是一项综合性能力,当明确好一种发布模式后,就需要逐步识别会涉及到哪些技术组件,以及明确技术组件在整个解决方案中所担任的职责边界,从而使它们能够相互协同工作。
如下列举了一些主要的技术组件:
技术组件 | 职责边界 |
应用管理平台 | 主要负责控制整个应用发布流程,以及集成并调度不同的技术组件,协同完成应用不停机发布 |
负载均衡(4层) | 主要负责服务请求的流量接入,可根据所识别的流量特征,负载分发到不同的负载均衡(7层) |
负载均衡(7层) | 主要负责服务请求的流量路由,可根据所识别的流量特征,路由分发到同一应用的不同实例 |
容器/虚拟机平台 | 主要负责容器/虚拟机资源管理,包括容器/虚拟机的创建、更新、销毁等 |
域名解析系统 | 主要负责域名地址管理,包括域名的注册、更新、解析等,以及提供用户访问应用或应用间访问等寻址能力 |
注册中心 | 主要负责服务资源管理,包括服务的注册、更新、注销等,以及提供服务请求方发现服务提供方的能力 |
在明确技术组件后,还需要对技术组件进行合理的架构规划,以适配不同的网络架构要求。
本次主要将对负载均衡进行特别说明,一方面它是负责处理流量的核心技术组件,另一方面网络架构的不同,对它的部署架构影响可能也是最大的。
在传统的网络架构环境中,出于对网络安全或其他考虑因素,通常会划分出多个不同的网络区域,网络区域间的访问需要通过开设防火墙访问策略才可以进行互通。
但这种方式必然会对应用服务间直接进行点对点访问的方式造成影响,主要原因是虚拟机或容器环境中,应用的IP地址可能会发生变化,导致无法提前明确防火墙访问策略。
所以,一般都会考虑使用负载均衡(代理模式)来解决这个问题。
建议前期优先选择方案三,虽然链路较长会小幅影响性能,但此部署方案相对较为成熟,一方面可以避免流量负载不均的问题,另一方面对于应用的改造成本也会相对较低。
注:除网络区域隔离会遇到这种情况外,在某些网络架构中,不同的容器集群间也同样无法访问,所以也同样适用。
另外,除必要情况下,也应尽量减少跨网络区域或跨容器集群间的访问,例如:优先容器集群内路由访问,跨网络区域频繁交互的应用服务建议迁移至同一网络区域等。
负载均衡通常可以分为4层模式和7层模式,其中4层更关注流量负载,而7层更关注流量路由。
一般建议将负载均衡(4层)和负载均衡(7层)进行分层部署,以充分发挥它们的强项。
建议前期优先选择方案二,虽然无法实现多容器集群间的全局流量调度,但对于当前可观测性和排障能力还不够健全的组织,通过物理隔离降低运维难度也是一种不错的选择。
综上架构决策,最终的全局流量链路大致如下,默认情况下,数据中心间流量隔离,即:单数据中心流量收敛,但可根据实际情况进行选择性放行。
Part 3
应用不停机发布的基础是服务路由,在这里,我们可以把应用服务分为两种角色,服务请求方或服务提供方,而服务请求方通过不同的寻址方式,最终访问到服务提供方的形式,可以称之为服务路由。
服务路由可以分为南北向和东西向。
- 南北向:服务请求方—>负载均衡(4层)—>负载均衡(7层)—>服务提供方
- 东西向:服务请求方—>服务提供方
Part 4
应用不停机发布的核心是应用如何优雅停止,光有服务路由可能还不够,它虽然已经解决了大部分问题,但离成功还差最后一步。
前面我们提到过应用发布要做到用户无感知,那如果应用发布过程中出现瞬断或短时间中断,而用户又正好在使用,那算不算用户就感知到了呢?
不过,如果你觉得线上服务中断5分钟也可以忍受,那我只能呵呵了。但我相信大部分具备互联网服务模式的头部公司,别说发布一次服务停止5分钟,可能就连5秒钟也无法忍受。
那我们先来分析一下为什么会出现瞬断或短时间中断:
- 服务请求处理还没完成,应用就被强行停止(例如:运维必备大招之kill -9 进程),导致处理中的请求无法正常返回。
- 服务提供方虽然已停止,但未通知服务请求方,服务请求方仍然继续访问已停止的服务提供方,导致出现异常。
- 容器:销毁服务实例,创建新版本服务实例。
- 虚拟机:更新服务实例,更新后解除屏蔽。
- 在服务提供方上实现Shutdown hook,即:等待服务请求处理完成后,再进行应用停止。
- 在服务提供方停止前,需通知所有服务请求方,并在完成通知后再进行应用停止。
实现效果 | 改造成本 |
---|---|
方式一:最多中断5秒 | 对容器/虚拟机平台进行少量改造 |
方式二:不中断(不通知) | 对容器/虚拟机平台进行改造 |
方式三:不中断(通知) | 对容器/虚拟机平台进行改造;对注册中心/统一注册中心进行改造 |
方式一:最多中断5秒
step1:依赖spring-boot-starter-actuator组件,暴露/shutdown端点。
management:
endpoint:
shutdown:
enabled: true
endpoints:
web:
exposure:
include: shutdown
step2:调整Eureka配置参数,该调整会分别增加Eureka客户端和服务端性能压力。
配置项 | 配置描述 | 缺省值 | 建议值 |
---|---|---|---|
eureka.client.registry-fetch-interval-seconds | Eureka客户端获取服务注册信息频率 | 30 | 4 |
ribbon.ServerListRefreshInterval | Eureka客户端ribbon刷新时间 | 30 | 1 |
eureka.server.useReadOnlyResponseCache | Eureka服务端是否使用只读缓存 | true | false |
step3:容器销毁服务实例或虚拟机更新服务实例前,请求如下地址进行应用服务实例停止。
curl -X http://应用服务地址/actuator/shutdown
方式二:不中断(不通知)
step1:依赖spring-boot-starter-actuator组件,暴露/shutdown和/service- registry端点。
management:
endpoint:
shutdown:
enabled: true
endpoints:
web:
exposure:
include: shutdown,service-registry
step2:调整Eureka配置参数,该调整会分别增加Eureka客户端和服务端性能压力。
配置项 | 配置描述 | 缺省值 | 建议值 |
---|---|---|---|
eureka.client.registry-fetch-interval-seconds | Eureka客户端获取服务注册信息频率 | 30 | 4 |
ribbon.ServerListRefreshInterval | Eureka客户端ribbon刷新时间 | 30 | 1 |
eureka.server.useReadOnlyResponseCache | Eureka服务端是否使用只读缓存 | true | false |
step3:容器销毁服务实例或虚拟机更新服务实例前,请求如下地址进行应用服务实例下线。
curl -X http://应用服务地址/actuator/service-registry?status=DOWN
注:此时,服务提供方仍然可以提供服务,但再次获取服务实例的服务请求方不会再获取该服务实例。
step4:服务请求方最多等待5秒后会更新服务列表,所以,在完成以上操作后,可以休眠5秒钟,再请求如下地址停止应用服务实例。
curl -X http://应用服务地址/actuator/shutdown
注:eureka.client.registry-fetch-interval-seconds+ribbon.ServerListRefreshInterval=5秒。
方式三:不中断(通知)
step1:依赖spring-boot-starter-actuator组件,暴露/shutdown和/service-registry端点。
management:
endpoint:
shutdown:
enabled: true
endpoints:
web:
exposure:
include: shutdown,service-registry
step2:调整Eureka配置参数,该调整会增加Eureka服务端性能压力。
配置项 | 配置描述 | 缺省值 | 建议值 |
---|---|---|---|
eureka.server.useReadOnlyResponseCache | Eureka服务端是否使用只读缓存 | true | false |
step3:容器销毁服务实例或虚拟机更新服务实例前,请求如下地址进行应用服务实例下线。
curl -X http://应用服务地址/actuator/service-registry?status=DOWN
注:此时,服务提供方仍然可以提供服务,但再次获取服务实例的服务请求方不会再获取该服务实例。
step4:在注册中心上记录当前正订阅该服务实例的服务请求方列表,并根据列表通知它们立即重新获取最新的服务实例,通知完成后再请求如下地址停止应用服务实例。
curl -X http://应用服务地址/actuator/shutdown
以上三种方式,可以结合价值收益和改造成本进行权衡,接受瞬断的可以选择方式一,而对技术有极致追求的可以选择方式三,如果两个都不是,那就选择方式二吧。
注:每个应用服务的Eureka配置参数并不一定能够完全统一,这样可能就会造成大量配置管理成本的增加,但如果可以统一,那方式二还是不错的选择。
Part 5
在具备以上条件能力后,应用发布不停机的基本框架已成型,但这样应用发布就能实现不停机了?
那有这么简单,我们在开发上还需要遵循一些规范,但符合这些规范的话,可能会增加我们的一些开发成本。
因此,并不是说每次应用发布都强行需要实现不停机发布,而是应该进行合理的取舍。不过,一个好的系统设计必然能做到既能遵循开发规范,也不会增加太多开发成本。
如下列举了一些常用的开发规范,实际情况可按需调整,其目的是为了不管是接口更新还是数据库更新等,都应尽量做到向上兼容。
涉及方面 | 开发规范及原则 |
接口 | 1.允许新增字段,必填字段需要设置缺省值 2.允许原有字段扩展长度或新增字典值 3.不允许修改原有字段的语义及格式 4.不允许删除原有字段 5.无法兼容时,应新增接口; 6.接口下线前,需确保无调用方。 |
数据库 | 1.允许新增字段,必填字段需要设置缺省值 2.允许原有字段扩展长度或新增字典值 3.不允许修改原有字段的语义及格式 4.不允许删除原有字段; 5.无法兼容时,应新增表; 6.新老表并存期间,数据统计需聚合处理; 7.老表下线前,需进行数据迁移。 |
消息 | 1.优先考虑新老格式兼容; 2.无法兼容时,生产者和消费者可共同约定新的主题; 3.若生产者无法约定新的主题,消费者可增加消息分发层进行主题重命名。 |
缓存 | 1.优先考虑新老格式兼容; 2.无法兼容时,则需要对不同的业务逻辑进行额外特殊处理。 |
写在最后
感谢你可以很耐心的读到这里,整篇文章主要围绕应用不停机发布进行了思考,从为什么要做,能带来什么价值,一直到应该怎么做,要关心哪些方面,进行了简要说明,主要目的是为了能让大家,对应用不停机发布有一个大概的框架认识。
实现应用不停机发布的手段,也并非仅有文章中说的那些方式,涉及到的技术组件可能也不止这些,但其解决思路基本都差不多,具体的技术实现方式,可以结合自身的架构环境再进行适配和调整。
玩乐高,学敏捷,【规模化敏捷联合作战沙盘之「乌托邦计划」】,12月25-26日登陆深圳,将“多团队敏捷协同”基因内化在研发流程中,为规模化提升研发效能保驾护航!!🏰⛴
企业组队和个人均可报名参加,一起挑战极客乌托邦