近期的 CrowdStrike outage 事件如同一记响亮警钟,强烈提醒我们持续审查并保持高标准的变更发布流程。这并非批评 CrowdStrike 的中断事件及其流程,而是一个契机,可依据我审查处理数百万TPS 复杂系统中多起事件的经验,重新审视最佳实践并查看用于分析中断事件的蓝图。
一个显然因缺少空值检查的休眠错误导致了 CrowdStrike 的中断。多个社交媒体帖子指责了可能进行此次变更的开发人员。但在深入探讨其他细节前,我在审查影响客户的事件时秉持一个原则:不关注“谁”,而关注“为什么”。
我们需明白,开发人员或操作员并非有错的一方,指责游戏对系统状态无意义的改进。若问题根本原因为人为错误,那说明我们的系统缺乏检查或故障安全机制,应首先关注操作员错误为何可能发生。
我们应不断努力构建系统、流程和工具,让操作员难以(最好不可能)犯对生产系统产生广泛影响的错误。温馨提示:永远不要让流程复杂,保持简单,同时增加做错事的难度(如删除数据存储表)。这里有一个通用蓝图,可帮助在错误到达生产环境前预防错误,或在错误仍到达生产环境时最小化影响范围。分析中断事件及后续如何改进系统时,同样适用此流程。
一般来说,可将最佳实践和中断后分析分为三类:完全预防 -> 最小化影响范围 -> 快速检测和快速恢复。
一、完全预防:筑牢变更发布的第一道防线
首先思考一个问题:我们能做些什么在错误/问题到达生产系统之前就将其捕获呢?答案包括在本地环境进行简单测试、设定高要求的代码审查标准、实现单元测试和集成测试的高覆盖率、部署管道测试自动化以及预生产警报。
-
支持功能完备的沙箱(开发人员)环境:
开发人员需要一个功能完备且独立的环境,以便用真实数据进行快速实验和测试,同时又不影响真实用户。但这种环境通常不稳定,因为许多其他开发人员也在其中测试他们的变更。一种处理方式是让值班人员或支持团队在不处理高优先级生产问题时处理这些不稳定问题。这是个需不断努力解决的问题,但却是早期捕获错误/问题的必要步骤。
-
对代码审查过程设定高要求:
高要求的代码审查能确保大部分错误在进入生产系统前被捕获并修复。我曾看到敏锐的审查人员在代码审查时捕获到虽不在此次代码变更中修改却会影响服务部分的问题。理想情况下不应如此,但现实有所不同。另一种常见方法是在代码提交前需要两次批准。但需注意,审查人员过多可能会减慢流程、影响敏捷性,所以要做好平衡。如果代码审查耗时几周,很可能审查人员在解决比代码变更本意更大的问题。代码审查的目标不是产生完美代码,而是提高代码的整体可读性、可理解性和可维护性。要记住,客户重视新变化/功能的交付,交付是成功关键。
-
实现高单元测试和集成测试覆盖率:
我见过因单元测试或集成测试覆盖率不足导致错误进入生产环境的多个事件。尽管很多开发人员私下认为编写单元/集成测试无聊,但这是能防止生产系统出现更广泛中断的重要环节。可要求新变更只有在有足够单元测试和集成测试覆盖率时才批准代码审查。
-
全面的预生产环境测试:
与可能存在稳定性问题的开发人员环境不同,预生产环境是生产系统的最接近复制品。此环境中的变更经过了代码审查、完成了单元测试/集成测试/手动测试,准备进入生产系统。部署管道中的工具如 SonarQube 可帮助自动化部分检查。采用由外而内的测试方法能模拟外部客户使用应用程序的情况,即设置外部进程定期发送请求并检查结果准确性。预生产环境的缺点是不会经历与生产系统类似的流量,无法揭示规模相关问题。所以很多团队定期进行负载测试,模拟流量以验证服务指标是否健康。运行这种设置可能成本高,但可通过按需设置在不使用时拆除来解决。这通常是客户看到新代码变更影响前的最后一步。在此环境中,要高度优先支持可用性或延迟相关警报,并阻止部署进展直到调查完成。理想的系统会在任何指标报警时阻止向生产环境推进。
上述步骤能确保在部署到生产环境前捕获错误,但也可能因各种边缘情况失败,CrowdStrike事件就是例子。我们接着讨论若错误从预生产环境溜出该如何处理。
二、最小化影响范围:将错误的影响降至最低
如果错误溜进了生产环境,我们该如何确保其影响范围尽可能小呢?
* 注意:上一部分涵盖了避免产生生产影响的可能性。
-
在单机环境中测试变更:
在将代码部署到生产环境前,先推送到单机环境。单机环境通常由一台或几台服务器组成,处理一小部分(通常为1%-5%)的实际生产流量。重要的一点是,即使单机环境处理生产流量,其指标也应与生产环境的指标分开。这样我们就能在单机环境指标出现差异时得到警报,并立即回滚变更。理想的回滚应是自动的,警报触发即启动。我们还应确保对生产所做的变更尽可能可逆。但如在 CrowdStrike 中断事件中看到的,并非所有变更都是可逆的。此时,单机环境或后文所述的逐步推出是更好的选择,以控制影响范围。影子模式测试是在推出不可逆转变更前验证代码变更的另一种方法。在影子模式下,收集并分析新代码的指标或日志,而不改变现有行为。指标或日志完全验证后,再替换现有行为。
-
对单机环境运行由外而内的测试过程:
如前所述,由外而内的测试方法要求构建一个外部进程,持续模拟客户对系统的流量,并验证系统的响应。这很关键,因为仅测量服务器端指标可能无法揭示最终客户可能遇到的问题,如网络延迟等。测量并警报这些指标有助于在客户联系支持团队前检测到问题。(毕竟,如果客户告知我们系统中的问题而我们却不知道,那就是一种“罪过”。)我们应主动发出警告,并在客户联系支持团队前积极修复问题。
-
逐步将变更推出到生产环境:
单机环境测试后,代码变更应分阶段推出,只影响总体流量的一小部分。有多种方法,可使用可用性区域(AZ)感知部署等策略,即假设服务在多个 AZ 中运行,一次部署一个 AZ,这是保证高可用性的最佳实践之一。这需要按 AZ 分类的指标,以便通过警报捕获问题并触发自动回滚,如前一部分所述。如果变更无法回滚,最好确定一种方法,将变更仅应用于直接合作的或事先确定的少数客户,以便更好地进行事件响应。这应是罕见情况,即与少数客户合作。但若推出信心不足的变更,这仍比触发影响所有客户的广泛中断要好。
如前一部分所述,若能在生产中进行影子测试,那是在影响客户前检测回归的好方法。另一种类似A/B 测试的方法是,系统以 1%、5%、10%、25%、50%和 100%的客户增量启用新变更的效果,可更好地控制有问题变更的影响。如果服务处理数百万TPS 的流量,考虑蜂窝架构是明智的。我们无法深入探讨蜂窝架构的细节。简单来说,与其在一个区域中运行一个生产环境,不如运行多个环境,处理来自不同预先分配客户到不同蜂窝的流量。还可使用系统的其他键(如客户帐户 ID 等)预先分配流量。除非系统处理数十万 TPS 以上的流量,否则不建议实现蜂窝。蜂窝会带来不同类型的复杂性,额外的运营开销可能并非对所有系统都值得。使用蜂窝进行逐步推出的一个缺点是会增加总体部署时间,因为以前由一个部署服务的特定区域被分解为多个蜂窝部署,且通常对每个蜂窝的部署是顺序的。
三、快速检测和快速恢复:在危机中迅速扭转局势
当意外错误被部署到生产环境后,我们期望能尽早检测到问题,并迅速采取行动让系统恢复稳定状态。
构建细粒度指标和警报:对整体服务健康指标(如可用性、延迟等)进行警报是个良好的开端,但随着系统不断发展,定期评估是否需要其他能精准捕捉客户体验的细粒度指标至关重要。例如,如果少数大客户和其他客户主导系统整体流量,而小客户仅占总体流量很小一部分,那么 P99、P99.9 可用性指标可能无法捕捉小客户体验,也可能不在这些指标中体现。
在此情况下,需专注构建自定义指标,如每个客户级别的可用性(PCA)指标,它能衡量所有客户的体验,无论客户产生大量流量还是只占总体流量一小部分。需要一种方法捕捉每个客户的体验,然后将这些数据在所有客户中聚合,以确定有多少客户在可接受故障范围内得到成功服务。这并非不跟踪总体 P99 及更高可用性或延迟指标,而是将 PCA 指标与服务级别指标结合使用,以更好地了解客户体验。
我曾遇到一种情况,总体指标(如 p99、p99.9)是可以接受的,但一小部分使用不太广泛功能的客户却一直存在可用性问题。此时,总体可用性指标完全在所有者定义的服务级别协议(SLA)范围内。另一种考虑捕捉这些问题的方法是主动为该功能添加单独的指标维度,以涵盖他们的体验。我们讨论过针对单机环境的由外而内测试,这些外部测试过程也应针对生产环境运行,以捕捉最终客户的体验。我还记得有一次,服务指标看起来都很好,但团队却能捕获到一个仅在客户端可见的问题,这是由于网络相关问题,仅看服务指标无法发现。如前所述,我们绝不想处于客户提醒我们问题而我们却不知且未努力修复的情况。
改进根本原因分析过程:除指标外,另一个需分析的领域是,一旦系统警报触发,我们希望开发人员能快速找出问题根本原因,通常从分析日志和有用指标开始。如果团队最近经历过一次中断,那么深入思考未来若发生类似问题能否进一步减少找出根本原因所需时间是很有价值的。这可以是改进日志记录或推动呈现更好的指标,让值班人员能确定错误位置。这能确保未来中断的响应能从过去的经验教训中受益。团队级别的知识库运行手册或“急救包”可用于分享更广泛的经验教训,帮助直接团队和组织中的其他团队。
自动回滚和回滚速度:我们的部署管道应确保在警报触发时自动进行回滚。还需注意,整个区域应能在几个小时内(即 3 - 6 小时)将部署的变更回滚到上一个版本。回滚变更所需时间越长,对客户的影响就越大。对于回滚不可能的情况(应很少见),应与高级领导一起评估自定义的操作员响应,以便在生产中遇到问题时遵循。这应是一次性情况,而非常态。
中断后系统状态恢复的改进:回滚并不总是能恢复系统状态。在这种情况下,询问开发人员是否需要更好的方法或流程来将系统恢复到先前的安全状态是有用的,同时思考可以采取哪些行动来提高系统状态恢复的速度。
结束语
总之,我们务必确保有一种持续评估、学习以及改进的文化。系统存在一个“操作安全区域”,会受到管理压力、有限人力以及安全举措等力量的影响。一旦这个区域靠近失败边界,就会出现中断情况,此时我们需要采取行动,使其回归中心位置。借助完全预防、最小化影响范围以及快速检测和恢复等手段,我们能够不断优化变更发布流程,提高系统的稳定性与可靠性。