混沌工程实践指南——如何实施一次"完美"的冷启动
导读:随着微服务架构的流行,服务本身的可用性降低会大大影响整个系统的稳定性。本文作者一方面阐述了我们需要注意哪些问题来保证单个系统在启动/停止过程中不会影响整个系统的可用性,另一方面介绍了如何对系统进行错误注入以模拟故障。本文十分适合系统架构师阅读。
中亭,阿里巴巴高可用架构团队高级技术专家,多年稳定性产品研发、架构演进、常态&大促保障经验,故障演练平台MonkeyKing创始人,云产品AHAS技术负责人,混沌工程布道师。
一、概述
在传统的定义中,冷启动是电脑(或系统)的一种启动方式。就是切断电脑的电源,重新启动,一旦冷启动,内存的东西全部丢失,重新检测硬件,再依启动操作系统。
在IT领域中,冷启动覆盖启动服务的动作到有健康流量流入的全部过程,经历的阶段可能有代码的初始化、数据加载、连接建立、错误恢复、系统自检等等。根据业务特点、系统规模、组织结构、技术能力等因素的影响,实现方式和结果可能会有所不同(比如:有状态设计、无状态设计、局部热加载等)。但是不管是什么类型的产品或团队、采用哪种技术栈,发布变更都是最高频发生和最容易出问题的生产环节。
实现一个高容错的启动方案,应该是每一个IT团队的基础要求。本文将会结合一些典型的技术架构,围绕一些通用的故障场景,给出一些设计和验证方法。
二、一些常见故障
在软件工程中,单体应用的标准定义是用户界面和数据访问代码被组合到一个程序的单层软件应用程序。通常情况下应用程序对应的代码由多个模块或项目所组成,项目会被打包成为一个大包(如WAR包)进行管理和发布。在业务发展的早期,此种架构可以复用较多的系统代码,提高研发效率。不过也存在着一些明显的隐患:
代码初始时间过长,导致健康检查失败
代码包含的项目越多,WAR包越大,系统冷启动的时间就越长。随着业务的发展,系统的启动时间就会失控。如果健康检查的重试间隔、阈值没有及时作出调整,就很可能出现误判的情况,出现大面积启动失败情况。在不恰当的发布场景下,可能会带来容量问题。
配置初始异常,导致应用不健康启动
应用启动获取配置的方式包括:环境变量、进程参数、本地文件、数据库、配置中心等。随着配置数量的膨胀,加载经常出现的问题可能有配置超时、配置乱序、配置过大、配置不完整的问题。其中配置超时和过大可能会导致系统物理启动失败或负载飙高。配置乱序或不完整,可能会带来系统非预期的初始化,产生逻辑问题。
不严谨的容错处理,导致超预期容灾
当远程配置异常时,大部分系统设计都会走入容错逻辑,如:加载本地容灾数据确保应用启动,或重试获取远程数据直至启动成功或失败。两种方式都是较常用的策略,正常状态下都可以正常工作。不过介于两种方案之间仍存在一些灰色地带。思考一下,当远程数据访问出现连接中断、返回非预期的状态码(比如:500、404)或无法解析的报文时,系统是否会走入本地容灾路径?是否会因为判断本地数据过期并重新加载数据,导致永远无法正常启动?
不幂等的容错逻辑,导致异常任务丢失
单体应用的设计理念中,应用程序负责不仅是对一个特定的任务,还要覆盖每一步需要完成特定功能。因此系统启动设计的时候,经常性会把一些没有正确处理的任务进行加载和处理。如果加载和处理过程被打断,且加载和处理过程不具备幂等性,那么一个直接结果是很多异常任务永远不会被处理。
三、规避原则
2014年,Martin Fowler与James Lewis共同提出了微服务的概念,定义了微服务是由以单一应用程序构成的小服务,自己拥有自己的行程与轻量化处理,服务依业务功能设计,以全自动的方式部署,与其他服务使用HTTP API通讯。同时服务会使用最小的规模的集中管理 (例如Docker) 能力,服务可以用不同的编程语言与数据库等元件实作。微服务赋予了研发团队协同开发能力,改变了复杂软件系统的交互方式。单体应用可能出现的故障,微服务都会遇到。同时因为分布式调用的引入,还会带来一些新的问题。下面整理了一些常见故障的背景和规避原则:
杜绝启动强依赖,降低服务启动时间
随着服务拆分,应用启动依赖的外部服务也会增多,从系统设计的角度,要尽可能的减小因为某个依赖的服务异常而导致系统无法启动的情况。比较理想的情况应该是应用可以正常启动,但是提供的服务能力是有部分缺失的(即:启动弱依赖、运行态强依赖)。启动强依赖的另外一个痛点是新建完整站点时,会出现循环依赖或必须按照特定顺序建站的问题。
应用程序不可避免会依赖其他三方服务的Client,Client中可能会引入诸如缓存前置、业务容错的逻辑。此时需要关注超时阈值、错误重试、同步处理等逻辑,多数冷启动故障都是因为这些点没有处理好。还要重点确保引入的代码块中不存在诸如"System.exit(status)"这样的代码。不然大面积触发此种异常时,重启已经无法恢复系统的正常运行。
先完成服务自检,再注册服务
当冷启动过程中,如果服务还没有完全初始化成功,就有请求进来了,这时候的处理结果就是失败。
针对服务上线问题,服务框架的一个做法是在ProvierBean在初始化阶段都不注册到注册中心,等Spring容器把所有的Bean初始化成功后,再统一注册服务。
延伸一下,如果应用还有很系统性或业务性的操作,比如:加载数据、预热缓存、开关初始等。建议提供一个幂等调用的HTTP接口,可以被启动脚本调用做检查,判断是否继续后续Web服务器的启动。
先摘除服务地址,再停止服务进程
当重启一个服务的时候,需要经过关闭服务和启动服务两个阶段。启动服务阶段如上一个章节所述。关闭服务阶段会导致上游系统调用产生大量失败,并带来一些业务波动。一种失败原因是请求还在服务端处理没有写回给客户端,另一种是上游客户仍在请求开始重启的服务端。
服务框架对于优雅下线的做法是通过JDK的ShutdownHook来实现的,会经历从注册中心注销服务、禁止接受新请求、停止创建外部调用等阶段。通过延迟一定时间和上游客户重试的方式减小业务的影响。
配置监控报警,最快发现配置不一致情况
每一个大型分布式微服务系统都应该有一个配置中心,配置内容可能覆盖了业务开源和容灾规则,其重要性无需多言。即便如此,微服务应用的视角中,对于配置中心必须是弱依赖、可缓存、可容灾的。当系统的部署环境和节点规模变大之后,在一些极端场景很容易出现配置不一致的问题(比如:新老版本、局部容灾等),进而引发严重的业务或可用性问题。因此于配置不一致的监控和覆盖就显得尤为重要。可以通过巡检系统或日志监控的方式去校验每一次变更的动作,提高配置不一致发现的时效。
四、三步实施一次"完美"的冷启动
百分百完美的方案是不存在的,因为不同的架构和阶段采用的方案都是有取舍的。不过做到相对"完美"还是十分有可能的。可以结合混沌工程[1]的思想,通过模拟一些失控的情况来验证当下实施方案是否与预期一致,提前发现可能存在的风险。
应用高可用服务(AHAS)是一款能够帮你快速提升高可用能力的SaaS化云服务,提供提供架构感知、故障演练和限流降级的三大能力。目前公测期间,戳一下免费开通[2]。下面围绕"杜绝启动强依赖"这条设计规则,通过一个实际的例子来介绍如何通过应用高可用服务(AHAS)来辅助实施一次"完美"的冷启动。
"录制"系统架构
开通AHAS的服务并安装AHAS探针(整个按照过程无侵入、白屏化操作),架构感知模块能够自动感知您的系统架构,以可视化的方式直观呈现应用对基础架构的依赖关系,以及组件间的依赖关系。
完成AHAS的安装后,实施一次正常的发布行为,系统的依赖、网络调用就会按照时间的维度完整的录制下来。如下图:
点击发布的服务节点(本例中是visits这个服务),可以看到详细的调用关系。其中与discovery-service(注册中心)这个服务有依赖关系,并且通信的端口是8761。
"模拟"异常场景
从"杜绝启动强依赖"这条原则出发,当discovery-service出现异常时,应用是要具备弱依赖启动能力的。因此我们模拟一个调用discovery-service超时的情况。我们配置的方案是visits主机范围,所有远程访问8761端口的请求都延时5000ms。(一些通用的服务都有自己的固定端口,可以通过模拟指定端口的网络延迟来模拟服务或网络故障,一些常见TCP服务端口列表可以参考:List of Well-Known TCP Port Numbers)[3]
"观察"启动表现
在故障模拟命令下达之后,就需要业务观察系统的表现是否符合预期。观察的点可以包括:系统监控、业务监控、是否触发报警等,可以通过云监控、业务实时监控服务 ARMS等云服务配合观察。
五、小结
混沌工程是在分布式系统上进行实验的学科, 混沌工程实践的其核心思想是通过实验和探索,来逐步增强对系统的信心。AHAS本身也提供了丰富的故障能力。
如果你对提升稳定性有一定诉求,可以结合文章中介绍设计理念来做一些试验。免费试用
六、参考资料
百度百科:冷启动 [4]
AWS Lambda [5]
https://medium.com/@nathan.malishev/lambda-cold-starts-language-comparison-️-a4f4b5f16a62
图片引用:Monolithic Application Architecture [6]
图片引用:Microservice Architecture [7]
维基百科:微服务 [8]
Dubbo延迟发布 [9]
HSF优雅上线(内部) [10]
Dubbo优雅下线 [11]
文中链接:
[1] https://zhuanlan.zhihu.com/p/52505917
[2] https://common-buy.aliyun.com/?source_type=zhihucoldstart&spm=5176.cnahas.0.0.41734bb7qvKk6B&commodityCode=ahas_001#/open
[3] https://www.webopedia.com/quick_ref/portnumbers.asp
[4] http://link.zhihu.com/?target=https%3A//baike.baidu.com/item/%25E5%2586%25B7%25E5%2590%25AF%25E5%258A%25A8
[5] http://link.zhihu.com/?target=https%3A//amazonaws-china.com/cn/lambda/features/
[6] http://link.zhihu.com/?target=https%3A//www.thesunflowerlab.com/blog/choose-microservices-monolithic-application-architecture/monolithic-application-architecture-min/
[7] http://link.zhihu.com/?target=https%3A//microservices.io/patterns/microservices.html
[8] http://link.zhihu.com/?target=https%3A//zh.wikipedia.org/wiki/%25E5%25BE%25AE%25E6%259C%258D%25E5%258B%2599
[9] http://link.zhihu.com/?target=https%3A//dubbo.incubator.apache.org/en-us/docs/user/demos/delay-publish.html
[10] http://link.zhihu.com/?target=http%3A//gitlab.alibaba-inc.com/middleware/hsf2-0/wikis/hsf_online_offline_gracefully
[11] http://link.zhihu.com/?target=https%3A//dubbo.incubator.apache.org/en-us/docs/user/demos/graceful-shutdown.html
本文转自知乎专栏应用高可用。
参考阅读:
技术原创及架构实践文章,欢迎通过公众号菜单「联系我们」进行投稿。转载请注明来自高可用架构「ArchNotes」微信公众号及包含以下二维码。
高可用架构
改变互联网的构建方式
长按二维码 关注「高可用架构」公众号