成为会带团队的技术人 - 前阿里本地生活研发总监 - 拉勾教育
上一讲我们学习了事故的应急和复盘,但 “预防胜于治疗”,所以今天我想从事故预防的角度和你聊一聊可用性治理的关键动作。
可能你听过这样一句俗语:只有千日做贼,没有千日防贼。但是在系统稳定性建设上,却无法通过一次优化完全杜绝事故发生的可能。因为业务发展会带动系统演进,它是一个动态变化的过程,并非一个常量,所以系统可用性的治理是持久战,需要你 “保持敬畏,坚持防范”。如果把事故比作火灾,那么技术 Leader 日常工作的核心就是围绕系统的风险隐患,建立 “防火墙”。
我们从研发的流程阶段来看,确定产品需求后,会通过架构设计、编码、测试、上线几阶段来交付系统。在这个过程中,上线环节是事故的高发阶段,因为随着变更的加入,原本系统稳定的运行状态会被打破;编码与测试阶段主要是实现功能,但不合理的实现是在架构设计阶段就被埋下的。当然,除了关注技术外,还要从团队、机制等管理手段出发,逐步建立你团队的稳定性军规。
所以今天我想从变更管控、架构设计、管理手段分享系统可用性的治理经验,希望能对你治理系统稳定性有所帮助。
变更会引起 90% 以上的故障
我们曾以年为单位统计了事故,得出了 90% 的数字,而且理论上说,公司越大、发布越多、这个数字就越大。因为互联网公司的研发模式基本都是 “小步快走、高速迭代”,一个业务一周十几次的发布变更很正常,而每一次变更会都会打破系统原本的“稳定运行态”,引入了新的变量,所以发布变更是研发最“高危” 的动作之一。
除了研发模式导致的高频变更外,随着业务发展,系统也会逐渐复杂,链路越来越长、完成一个功能相关联的系统服务越来越多。系统复杂度的提高,增加了变更时所带来的不确定性,为了应对这样的情况,这几年,我们在团队内部实施了严格的发布 SOP,简称为 “发布三板斧”:
在我看来,“发布三板斧” 的落地与推进,就是可用性治理的第一步(也是最关键的一步)。我们来看一下,发布三板斧的实施具体要注意哪些关键点。
1. 变更需要监控
在 01 讲中我提到过,完善的监控告警比人工反馈响应更快,也会减少故障的持续时间进而降低影响。而没有监控的变更就像盲人摸象,甚至会出现 “你负责的系统宕机了,都要用户电话告诉你” 的尴尬局面。
在推进监控落地的过程中,你要和团队成员讲明监控的重要性,还要确保监控的完善与有效,而针对某个业务场景,有效的监控要回答三个问题:
- 是否有问题发生?
- 哪里发生了问题?
- 发生了什么问题?
这三者是递进关系,对监控的覆盖程度与范围要求越来越细致。一般情况下,我们监控的都是 API 这一层面,但是单纯的技术指标并不能完整回答上面三个问题,往往要结合业务场景去设计,才能够更加精细化地感知异常。
比如之前饿了么有商家开放平台的系统,用以对接类似星巴克一类的 KA 品牌自己的餐饮系统,开放平台的核心价值就是对外暴露统一的协议,屏蔽各个 KA 品牌内部系统的数据逻辑。假设 A 品牌的系统今天出现了问题,导致 A 品牌餐厅都无法接单,那么从饿了么所有商户的接单曲线上看,可能没有直观的感知,毕竟一个品牌的订单量在平台上可能无法凸显,单一品牌接单量会淹没在海量的数据中。而如果有一个 “KA 商户接单曲线” 的监控,就能很容易看到异常了。
外卖接单曲线图
所以我们要结合业务配置有效的监控,而能否第一时间发现变更导致的异常、缩短异常带来的影响,就要看监控是否完善了。
如果说监控让我们更快地知道系统有没有问题,那灰度就是确保即使有问题也只在小范围产生影响,降低风险的作用范围。
2. 有效灰度必须有耐心
一些技术 Leader 认为 “灰度就是在生产环境进行小范围测试”,就算嘴上不这么说,心里也这么想。但这个认知是绝对错误的,灰度从来不是为了测试,也不等于 A/B Test。它本身是为了对抗 “未知的不确定性”。
我们之所以在编码完成后,会在测试环境进行测试验证,主要就是在找问题、找错误,而当我们走完一个完整的测试流程后就可以认为,已知的问题都已经解决了,又因为在测试环境,所以没有给线上真实的业务与用户造成影响。
而灰度就是假设 “还存在我们不知道的问题” 所以你才需要更加谨慎地进行灰度,确保即使问题真的在生产环境出现,造成的影响也是可控的。
在灰度的落地与推进过程中,要注意有效性,因为灰度这个动作很复杂、费时间,稍不注意就会 “形式化”。比如一个系统部署在 2 个机房,每个机房 4 个集群,正常的灰度顺序应该是单机房单集群中部分节点、单机房单集群中全部节点、单机房中全部集群,然后另外一个机房重复这个步骤。
要想实现灰度的有效性,关键点在于时间和流量。
- 时间:每个灰度阶段至少有 5 ~ 10 min 的观察,在监控、日志和各方反馈没有异常后再扩大灰度范围,确保一些运行时异常或量变积累质变的问题可以暴露出来。
- 流量:有时一些业务场景需要特定的触发条件,比如满足某些条件的用户或满足某些条件的订单,那么在灰度时就不能仅通过单位时间内有没有异常来判断,还要确保有足够的有效流量。
有效的灰度可以把问题影响锁定在一个小范围内,但是同样也降低了问题的 “明显性”,所以你要通过监控和日志更加仔细、谨慎地去寻找、观测异常并对比发现问题。并且因为动作烦琐,用时也长,还要和开发同学沟通好“这样做有什么意义?” 一类投入产出比的问题。
我建议你结合实际的系统情况与风险程度来确定灰度的程度,平衡好时间与效率,“好钢花在刀刃上”,因为以我的经验来看,这些投入的时间无论怎样都会大大少于你在事故复盘会上后悔的时间。
3. 回滚就是变更的 “后悔药”
在 01 讲中,我提到过故障恢复最好的手段是各种预案,而回滚则是预案中最普遍、也最有效的。回滚这件事儿,你并不陌生,我重点想强调 “何时回滚” 以及“如何确保能回滚”。
我记得很清楚,2019 年有一天我刚下班回家,风控的研发负责人就打来电话说:“订单系统前两天的一个变更导致风控有部分订单拦截失败。” 涉及风控就意味着资金会有损失,事儿不小,我立刻电话相关的同学,得知是前几天上线的一个功能导致的,因为与风控评估下来影响面不大,所以新功能没有下线,计划过两天修复这个 Bug。我顿时有一种三花聚顶的错觉,整个人都不好了。
- “已经产生了线上影响,并且可能有资损,怎么能过两天再修复?”
- “发现问题第一时间回滚就能解决的事儿,为什么不回滚?”
没人能回答我的问题,而我当时的决定是系统立刻回滚,并第一时间处理系统回滚带来的业务影响,承诺第二天尽快修复后完成新功能的重新上线,同时按照事故进行申报。
这件事儿也让我意识到,在研发对事故的敬畏之心不足时,回滚也会失灵。所以我建议你,除非影响面非常小并且可控,或者涉及重要的商业合同,否则一般情况下应该有立刻回滚止损、业务恢复的意识,不要有侥幸心理,你要成为这种意识的宣讲者与践行者。
那么如何确保变更是可以回滚的呢? 要知道,系统并不是天然可以无缝回滚的,想要系统具备回滚的能力,在设计与实现阶段需要付出额外的精力。可回滚的本质是系统的兼容性设计与实现,比如常见的 “只增不改”,一个 API 内要调整很多实现逻辑才能满足新业务的需求,此时不妨直接新增一个 API ,两个 API 保持参数一致,那么一旦新 API 有异常直接切换回旧的 API 即可。
所以,不论是灰度计划还是回滚策略都应该在架构设计阶段就去考虑,结合排期、风险程度、成本投入这些方面,要做好评估与平衡。
坚守 Design For Failure 的架构理念
“Design for failure and nothing will fail”,最早是 AWS 的一条最佳实践,即面向失败进行系统设计。你也可以理解为:考虑系统所有可能发生故障或不可用的情形,并假设这些可能都会发生,倒逼自己设计足够健壮的系统。
其实这个理念在分布式系统中很早就应用了,比如 “非关键路径都要可以降级”“核心系统一定要有熔断、限流、超时这些保护手段”“架构上要避免单点” 等,而今天我更多地想从正反两个角度来讲讲技术团队如何推行并落地这种理念。
- 正向:如何形成 Design For Failure 的系统设计习惯?
- 反向:如何确定系统真的可以 Failover?
1. 将经验教训沉淀下来
大部分技术 Leader 对于系统的参与都在架构设计这一环节,通过业务对焦、方案梳理,敲定系统架构、DB 和 API 的实现。这一过程中,你的重点不能只在功能的实现上,还要敏锐地去感知系统可能存在的风险隐患。
历史是最好的老师,我建议你总结并分析过去发生过的事故,并结合常规分布式系统的可用性风险,以此梳理出一个围绕事故隐患的风险点 Checklist,在需求迭代或者架构设计时,通过它高效地找到系统实现的薄弱环节。当然了,这个 Checklist 需要你结合系统演进不断地完善,以 DB 为例,最基本的你可以考虑下面这些风险点:
在不断梳理并实践这些风险点的过程中,我们又会形成一些问题的通用解决思路,和标准的设计原则。比如超出预期的主从延迟是分布式系统中很可能出现的情况,如果业务场景上主从延迟的容忍度很高,还不是关键路径,做好降级开关可能就足够了;如果是写完即读的场景,就要考虑是不是让读请求直接绑定主库,并且对主库是否造成较大的负载压力以及缓存是否能起到作用,或者改为消息推送的方式。
当然了,解决一个问题的方案很多,除了完善 Checklist,在团队普及这种设计理念之外,更关键的是将这些解决方案沉淀成设计原则,让研发人员可以在实际中落地。
2. 通过演练验证预案设计
因为 Design For Failure 的设计思想,我们日常在系统中做了很多预案的准备,同时也发现在真实事故发生时,很多设计并没有按照预期那样发挥作用。
在系统正常运行时,我们无法验证自己准备的灾备方案是否有用(比如,这些措施在故障发生时是否真的有效?处理流程与沟通协作是否通畅?),而一旦方案真的有问题,在真实故障发生时也为时已晚。
为此我们在 2016 年还参考了当时 Netflix 的 ChaosMonkey 设计并实现了自己的故障演练系统 Kennel,日常主动制造事故上下文来验证我们的设计与系统是否可靠。
通过这个例子,我想强调的是, 技术 Leader 要化被动为主动,有意识地推进故障演练,不论是以注入还是回放的方式制造可控的故障,以此验证应急处理的机制流程和预先设计的灾备方案是否有效,通过持续的日常演练来提高故障发现和恢复的能力,以便在真实事故发生时应对得更加从容。
当然了,我想提醒你,演练是一个逐步发展的过程,不需要一步到位。我们也是从最开始测试环境检验,然后在生产环境进行有预案的演练(即制造一个故障并启动预案看其是否生效),直到最近一两年才开始进行真正的随机故障演练,即运维或稳定性的专项同学在不提前通知的情况下注入不确定的故障,验证对应的团队和系统是否能及时感知、操作并恢复。
以上就是关于 Design For Failure 在团队中推行落地的一些关键点和建议,同时可用性的治理与预防还要结合管理手段,技术 Leader 通过适当的管理手段把理念落地、把执行到位、把结果做好。
把稳定性当作机制与文化去建设
系统稳定性结果好坏很大程度上取决于技术 Leader 的重视程度,如果一个团队的管理者都不能身体力行的去重视它,而仅仅只是喊喊口号,那就不要指望团队成员能认真地对待这件事。
所以要把稳定性当作一个机制和团队的文化去建设,不断加深大家对稳定性的认识以及和每个人切身利益的关联程度,进一步形成团队的氛围与文化。管理的方法需要结合团队与自身的情况去落地,这里我分享几个之前的做法,希望给你一些启发。
1. 新人 Landing 从稳定性学习开始
团队中新入职的同学往往有 1~2 周的适应期,除了熟悉基本情况外,新同学必须学习并通过发布变更 SOP 考试后,才能取得对应系统的发布权限。除此之外,还要学习这个部门最近半年发生的真实事故,并写一篇总结邮件给部门内所有人。
其实这是利用了心理学中的 “承诺一致性原则”,人们往往会重视自己公开承诺的事情,并形成对应的行为约束。新人的公开邮件不仅是通报自己的学习总结,某种程度上也是一种承诺 “这些错误和教训我认识到了,我不会犯类似的错误”,进一步加强对事故的敬畏之心。
在新人入职 3 个月进行述职转正时,作为评委之一,我一定会对他试用期内稳定性的结果进行评定,也会针对稳定性相关的内容进行问答。
总之,团队的新成员一定要在一开始就对稳定性有足够的认识与敬畏,技术 Leader 要严控这个环节,从人的维度避免风险的主动流入。
2. 每人不低于 35% 的稳定性 KPI
重要且生死攸关的事儿,一定要在 KPI 中体现出来,避免出现口号响亮但是落地无声的情况。一般来讲,我会要求技术 Leader 的稳定性 KPI 占比在 35% 到 40%,一线研发的同学可能是 50% 以上。
虽然占比上技术 Leader 因为有其他职责可能未必有一线研发同学高,但是其评判标准会更严格,比如达标默认是 B,唯有超出预期才可能到 B+ 或者 A-。
这部分稳定性 KPI 除了会影响最终绩效,也会直接影响年终奖、调薪、晋升等各方面,比如上一年度如果存在发布 SOP 红线违规导致事故,则晋升资格取消。总之,通过稳定性 KPI 的设计,将稳定性的结果与所有人的切身利益实实在在地绑定到一起。
3. 好的坏的都要在阳光之下晒一晒
想要落实一件事儿,往往要奖惩结合,而榜样的力量是无穷的,你可以通过榜样来告诉团队小伙伴,公司需要什么样的人才、不能容忍什么样的行为。
我们之前每个月会做一次红黑榜单,以不同的维度公示部门内各团队的稳定性结果,统计维度可以是:事故数、冒烟数、1-5-10 达成率、本月严重事故…… 把做得好与不好的都拿出来给大家看看,那些结果好的我们可以学习什么,那些结果不好的我们要规避什么,通过这样的形式让我们共同看见、共同学习。
当然,管理的手段千变万化,你要清楚的是,奖惩不是目的而是手段,要选择合适的手段提高团队成员的稳定性意识,并且最终取得好的结果,我建议你根据今天的内容,结合自己团队的具体情况去设计,“拿来主义” 也要有一个本地化的过程才能发挥好的作用。
小结
对于技术 Leader 而言,你不能用一次次的重大事故让团队成员慢慢理解系统稳定性的重要性。同样没发生火灾之前,大部分人都意识不到消防通道的重要性,唯有经历过才知道这些措施的珍贵。可用性的预防与治理需要投入大量的时间和精力,这一点上需要技术 Leader 做好投入产出的评估与平衡。
发布变更、架构设计的 “Design For Failure” 以及机制与文化的建立是三个重要的驱动方向,技术 Leader 可以围绕这三个方面不断延伸,用运营与治理的视角去看待可用性的预防,希望本节内容对你有所启发和帮助。
留个作业: 相信你在系统中肯定也有很多 design for failure 的设计,回顾一下它们发挥作用的高光时刻并梳理一下当时是如何思考这些设计的,同时也尝试优化这些设计看是否还有更好的方案。
最后,感谢你的阅读,如果这节课让你有收获,欢迎你将它分享给其他的朋友,我们下一讲见。