相传魏文王和名医扁鹊之间曾经发生过这样一段对话:
魏文王:“你们兄弟三人,谁是医术是最好的呢? ”
扁鹊:“大哥最好,二哥差些,我是三人中最差的一个。”
魏文王:“那为什么你的名气最大?”
扁鹊:“大哥治病,是治病于病情发作之前,病人尚未发病即已根除病因,使得他的医术没有得到认可,没什么名气;二哥治病,是治病于病情初起时,二哥药到病除,大家认为二哥善治小病,名气只在本乡里;而我是治病于病情严重之时,大家看到我或在经脉上穿刺放血,或在患处敷以毒药以毒攻毒,或动大手术直指病灶,使重病人病情得到缓解或治愈,所以以为我的医术高明,名气因此响遍全国。”
在上面这个小故事中,根据医生治病发生在病情发展的不同时期可以分为三个阶段:
- 病发前,施以小术,代价非常小,却很容易根除病因。
- 病发初期,患者小恙,对症下药,也能很快治愈。
- 病情严重,患者身体损伤极大,要施以非常手段才有可能救治病患,代价很大,也很容易留下后遗症。
技术人员治理系统犹如医生治病救人,为了保证系统能够稳定地对外提供服务,有比较高的可用性,需要关注系统发展的整个生命周期,见微知著,一方面能够识别软件开发中潜在的问题,尽早解决;另一方面当系统出现小问题,不要忽视,及时处理;最后当系统发生较大问题时,也能够使用雷霆手段快速恢复,保证系统可用。
为了提高系统的可用性,这三个阶段我们都需要关注,本文会通过分析影响系统可用性的因素,尝试找出在这三个阶段我们需要采取的必要措施。
什么是系统可用性?
首先我们来看看维基百科上对可用性的一些定义:
可用性就是一个系统处在可工作状态的时间的比例
系统可用性是衡量一个系统正确地对外提供服务(可工作)的能力。我们通常采用SLA(Service Level Agreement)来衡量系统可用性,也就是我们经常听到的的几个9,其对应的系统不可用时间可以参照下表:
3个9(99.9%)表示一个月最多不超过43.8分的不可用时间,对于每月例行停机维护的系统基本很难达到;5个9(99.999%)要求一年内系统不可用的时长不超过5分钟,听起来就觉得不可思议。
哪些因素会影响系统的可用性
除了人为原因导致的故障,基础设施的定期维护,硬件设备损坏,自然灾害等等都会导致系统不可用,因此100%的系统可用性是基本不可能达到的;要提高系统可用性,我们得先分析影响可用性的问题发生的原因和影响,下面根据我的经验列举一些会对系统可用性产生较大影响的因素:
人员误操作
经典如rm -rf . /的操作在日常的开发过程中并不少见,比如:
- 在生产环境执行了IDE导出的数据恢复脚本,不慎选中了DROP TABLE,结果删除了整表的数据
- 在生产环境中清理冗余的Docker镜像文件时,不慎删除了所有镜像
- 本该在测试环境执行的脚本,错误的跑在了生产环境的数据库上
- ……
数据、文件存储等无疑是大多数企业的核心资产,涉及到数据的故障往往都是非常大的故障,不仅影响范围非常大,如果前期没有足够的灾备准备,是很难在短时间内恢复的,甚至可能无法恢复,造成巨大损失。
雪崩效应
在分布式系统架构下,服务之间需要配合来完成复杂的业务流程,某个服务提供者的不稳定在请求量变大的情况下,会逐步演化成整个系统的雪崩效应。
雪崩效应通常会经历如下过程:
- 服务提供者不可用(宕机或性能较差)
- 服务调用者请求量增加(业务量增加、失败重试、缓存穿透等)
- 服务调用者系统资源耗尽,服务调用者不可用
雪崩会造成整个系统的瘫痪,以我们的系统曾经经历了一次雪崩为例,一个核心服务在没有代码变化的情况下,增加了用户量(可以理解为对用户分批开放),在数据基数增大和请求量增大时,服务的接口性能无法满足需求,陡增的请求量对数据库了造成非常大的压力(CPU占满),接口全部超时,导致长时间占用API Gateway的连接资源,API Gateway无法处理其他请求,进而导致了整个系统的瘫痪,如下图:
本质上来讲雪崩的发生是因为服务提供者无法满足当前业务的高并发需求,同时也没有很好的应对措施保证系统其他服务的正常运转。
未经完整测试的版本发布
以上两类故障的发生通常会引发非常严重的问题,但频率相对较低,而定期的版本发布常常因为没有经过完整的测试导致线上故障发生,严重的情况下也会对可用性产生很大的影响。
随着系统服务年限的增长,业务的增长让系统变的越来越复杂,依赖人工的黑盒测试基本上很难覆盖所有业务场景(我们曾经有一个开发了3年多的系统,在没有自动化回归测试的情况下,每次发布后需要4个测试同学花费2个小时进行只读的关键场景回归测试,成本非常高昂),这导致定期的版本发布经常会带来一些意外的故障,每次发布后都需要有人值班来解决这些故障。
系统间集成测试成本更高,一方面集成测试需要跨多部门进行沟通协调,另一方面集成的一方通常无法知道另一方的实现细节,测试用例很难保证全面。对于关键系统的集成,如果修改后没有充足的回归测试,上线后很可能导致影响主流程的故障,影响用户的使用。
基础设施故障及定期升级维护
不管是自建基础设施,还是使用第三方云服务,基础设施的故障和定期升级维护是不可避免的,也是影响系统可用性的关键因素。
影响可用性的基础设施相关因素包括:
- 硬件故障
- 网络故障(包括网络带宽不足导致的通信问题等)
- 系统升级(操作系统、数据库、网络设备、中间件等)
- 日常维护(备份、迁移等)
如何提高系统可用性
影响系统可用性的因素很多,以上列举了一些非常典型的场景,这足以让我们对影响可用性的因素有一个非常直观的理解。为了从可实施的角度讨论如何提高系统可用性,这里不考虑基础设施硬件故障等不可控因素。
从上面的因素中我们不难发现,有些问题我们可以通过提高工程化能力和优化工作流程解决,但如何将这些工程化能力和流程落地也是一个非常复杂的问题,因此我下面会通过技术和团队两个视角来看如何才能提高系统的可用性。
从技术视角,要不断强化工程能力
根据可用性的定义,要提高系统的可用性,就是要缩短系统不可用的时长,保持系统的健康状态;那么回顾下文章开头的小故事,我们可以从三个阶段来针对性的采取一些措施:
- 病发前:
- 完善的代码质量管理体系和自动化测试体系,能够保证产品质量,通过代码检查、安全扫描和测试自动化,避免未经测试的代码部署到生产环境
- 完善的权限管理体系,能够保证生产环境权限不滥用,避免过多的人为操作对生产环境产生影响
- 其他自动化的开发、运维工具体系,在提高工作效率的同时,注重安全性,通过自动化的脚本检查、运维流程自动化等方式避免不必要的错误对生产环境造成的危害
- 病发初期
- 完善的监控体系,能够尽早识别系统的潜在问题,系统运营人员可以快速甄别即将发生的故障,不要等到用户反馈才知道系统出了问题
- 完善的持续集成/持续部署体系,能够保证尽量快的反馈,尽量短的发布时长,在功能开发和故障修复后快速地部署代码到生产环境
- 病情严重
- 完善的发布验证、回滚、限流、熔断、降级策略,能够尽量缩小故障的影响范围,保证即便有部分服务不稳定,也不至于导致整个系统不可用
- 完善的灾备恢复体系和演练,能够保证系统在发生重大紧急事故时可以快速恢复,尽量缩短不可用时长
从团队视角,要有一支重视技术的团队
在软件系统的开发运维过程中,我们有很多手段可以发现问题,如线上故障、监控报警、回顾会议等等,但从根本上解决问题往往非常困难,大多数情况下是头痛医头,脚痛医脚,到最后结果就是技术债台高耸,线上故障频发;即便找到了解决问题的办法,在实施的过程中还会遇到很多问题。
探究其原因,可能比较复杂 ,但从团队视角来看,通常存在团队对待技术并没有那么严谨,对待生产环境没有那么敬畏,对待自己的代码没有那么严苛。
要提升系统的可用性,必须要有一支重视技术的团队,这个团队应该具备以下特征:
- 自上而下崇尚技术,尊重技术
- 有专家级成员,有能力实施上面提到的各种工程能力
- 不急功近利,不会为了短期的业务目标而在技术上妥协
- 团队成员遵守团队纪律,不做违反纪律,破坏规则的事情
结语
追求系统的高可用就像一个人追求身体健康一样,整个软件开发团队自始至终都要秉持爱护软件系统的心态,在软件开发的全流程中,时刻保持警惕,通过提高团队在三个阶段中的工程化能力来及时发现和解决系统中存在的问题。
这不单纯是个技术问题,善治系统的团队首先要在团队内建立尊重技术、工程的文化氛围,建立团队行为规范,严明纪律,有所为有所不为;在此基础上,不断在团队开发过程中针对问题寻找解决问题的最佳实践,做且做好正确的事,相信高可用是必然的结果。
文/ThoughtWorks 麻广广
更多精彩洞见,请关注微信公众号:ThoughtWorks洞见