撤出云平台六年后,我们做了一次“断网测试”
如同肌肉一样,灾难准备的能力也需要不断训练和演习。
把时间线拨到 2021 年 11 月 18 日星期四,Dropbox 服务一切如常。用户没有感觉到任何异样,就如同无数个岁月静好的日子。但真是这样吗?当然不是,那天下午五点,一群 Dropbox 员工在 Zoom 频道里吵作一团,因为大家突然接到命令,要求把圣何塞数据中心跟 Dropbox 网络直接断开。
这可是件大事,毕竟灾难恢复(DR)团队为此准备了一年有余,而影响到的可是 Dropbox 六年多积累下的工作结晶。
但面对自然灾害愈发普遍的世界,我们必须考虑数据中心受到此类影响的可能性。就是说我们不仅要在数据中心选址方面精心规划,同时也得建立起有助于降低风险的应对策略。
自 2015 年从 AWS 迁出之后,Dropbox 的基础设施就大量集中在圣何塞地区。虽然用户的元数据多年来一直在跨区域复制,但必须承认的是,我们的大多数服务都诞生在圣何塞、成熟在圣何塞。这是块宝地,但也靠近圣安地列斯断层,所以我们得保证突如其来的地震不会让 Dropbox 意外离线。
怎么向客户证明我们已经为灾害做好了准备?答案就是恢复时间目标(RTO,Recovery Time Objective),这项指标用于衡量我们要花多长时间才能从灾难性事件中恢复正常。多年以来,我们一直在通过工作流程缩短预期 RTO,希望能更从容地应对包括地震在内的可能灾难。
也正是依靠 2020 年和 2021 年的跨职能协作,灾难准备团队最终下决心把圣何塞数据中心完全断开,看看 Dropbox 是不是真的能够把 RTO 降低到目前的水平。下面,咱们就来回顾这段惊心动魄的故事。
一切要从架构设计说起
为了更好地理解如何缩短 RTO 周期,我们首先得明白 Dropbox 是怎么进行架构设计的。
Dropbox 拥有两大核心服务栈:其一用于块(文件)数据,其二用于元数据。不少关注 Dropbox 技术动向的朋友可能知道,我们的块存储解决方案名为 Magic Pocket,其设计就是通过多宿主主机提高可靠性。所谓多宿主服务,就是说这项服务在设计上能够依托于多个数据中心保持运行。
Magic Pocket 还是一套所谓双活系统。就是说除了多宿主设计之外,它还能同时且独立地为来自多处数据中心的块数据提供服务。Magic Pocket 设计包含内置复制与冗余机制,能够保证把区块故障给业务造成的影响控制在最低。这套架构还具备灾难弹性,所以用户能够明确看到哪处数据中心出了问题、但服务体验并不太受到影响。在部署了 Magic Pocket 之后,Dropbox 又启动了一项三段式计划,先是增强元数据堆栈弹性、最终在元数据层面同样建立双活架构。
具体来讲,计划的第一阶段就是实现主动 - 被动架构。也就是说,在完成必要的变更之后,我们就能把元数据从当前主动城域——即圣何塞数据中心(SJC)——转移至另一被动城域。这个过程就是所谓故障转移。
我们的首次故障转移已经在 2015 年成功完成,但这还只是达成最终目标的小小一步。之后,我们开始为元数据堆栈构建主动 - 主动的双活架构,希望以独立方式为来自多处数据中心的用户元数据提供服务。到了这一步,麻烦开始出现。
元数据让问题愈发复杂
我们的元数据堆栈建立在两个大型分片 MySQL 部署之上。其一用内部数据库 Edgestore 承载着通用元数据,其二则负责承载文件系统元数据。集群中的每个分片由六台物理设备组成:两个核心区域,各自对应一台主机 primary 和两台副本 replica。
而 MySQL 层中的两大权衡性设计,再加上 Edgestore 中的数据建模复杂性,迫使我们不得不重新考量整个灾难准备计划。
MySQL 中的第一个权衡就是如何处理复制操作。我们之前使用半同步复制以求取数据完整性和写入延迟之间的平衡性。但正是由于这一设计,导致各区域间的复制只能异步完成——意味着远程 replica 始终落后于 primary 主机。这种复制层面的滞后,导致我们很难处理主区域中的突发性故障。有鉴于此,我们对可能发生的故障做出预判,通过设计确保主区域在事件之下仍能保持一段时间的正常运行。冗余电源和网络系统都已部署到位,我们对实际效果也算是比较满意。
MySQ 中的第二个权衡则是一致性级别。我们的 MySQL 采用的是读取提交隔离模式,由此实现的强一致性使得开发人员能够轻松处理数据,但同时也限制了数据库的扩展能力。目前比较常见的扩展方式就是引入缓存以降低整体一致性,但同时增加读取吞吐量。在我们这套系统中,虽然 Dropbox 已经建立起缓存层,但它在设计上仍然与数据库保持强一致性。这个决定使得设计方案相当复杂,同时也限制了所能容忍的数据库缓存内容滞后度。
最后,因为 Edgestore 是一套面向多种用途的大型多租户图数据库,所以往往很难搞清其中的数据所有权。这种复杂的所有权模型,导致我们几乎没法简单将用户数据中的特定子集转移到其他区域。
这些权衡设计的存在,直接决定了我们后续构建双活系统的基本思路。开发者已经适应了前一种权衡所带来的高写入性能,也适应了后一种权衡实现的强一致性。总而言之,这些选择严重限制了我们在设计双活系统时的架构选项,也导致最终系统变得愈发复杂。到 2017 年,灾难准备工作已经停滞不前,但开发强大故障应对方案的压力却丝毫未减。为了保障能在灾难发生时获得良好的业务连续性,我们决定改变方向,朝着主动 - 被动故障模型迈出探索的脚步。
我们的灾难准备团队
在决定转向主动 - 被动方案后,我们开始为更频繁的故障转移设计必要工具。2019 年,我们完成了第一次正式的故障转移,之后每个季度都会再次尝试转移、并借此机会改进整个流程。2020 年是个重要的转折点——除了新冠疫情的爆发,我们 Dropbox 的灾难准备水平也自此真正上了一个新台阶。
2020 年 5 月,我们的故障转移工具发生严重故障并引发宕机,业务瘫痪达 47 分钟。负责驱动故障转移的脚本在执行当中出错,致使我们身陷半中断状态。这次失利也暴露出我们灾难准备策略中的几个重大问题:
驱动故障转移的系统本身缺乏故障弹性。
各服务团队使用自己的故障转移流程与工具,互不相通。
我们的故障转移频度不足,因此对方案的实践考查不够全面。
为了解决第一个问题,我们开始对现有故障转移工具和流程开展紧急审计。我们还做出必要变更,确保工具拥有良好的故障弹性;同时建立起新的清单,确保能够以更严格的方式执行故障转移演习。
对于第二和第三个问题,我们组建了专门的故障转移团队,也就是前文一直提到的灾难准备(DR)团队。有了这样一支专业队伍,我们就能把每季度一次的故障转移频度提升为每月一次。更频繁的故障转移不仅能帮助我们积累经验、提振信心,同时也让我们以前所未有的速度实现了灾难响应与灾难恢复。
明确的使命与新组建的七人小队,让我们有了设定更高目标的底气。到 2021 年底,Dropbox 必须把 RTO 控制在更短水平。
故障转移迎来实质性改进
2020 年 5 月那次宕机事故,凸显出了我们的一大重要问题——总是想用单一 Go 二进制文件完成城域之间的故障转移。虽然这种方法最初效果不错,但随着我们对于故障转移不断提出更高要求,整个思路也变得愈发难以为继。
因此,我们决定从头开始重写这款工具,提升它的模块化与可配置水平。我们从 Facebook 的 Maelstrom 论文中获得了灵感,其中详细介绍了一种巧妙的外向流量导引思路,足以承载起庞大的数据中心灾难恢复需求。虽然有了参照对象,但我们还是从最小可行产品做起,希望整套方案更适合 Dropbox 自己的系统。
我们借用了 Maelstrom 中的 Runbook 概念。Runbook 中包含一个或多个任务,每个任务负责执行特定操作。这些任务共同形成了一个有向无环图,使我们不仅能够描述故障转移演习中的每一个必要步骤,同时也能概括一切通用性的灾难恢复场景。以此为基础,我们可以使用易于解析和编辑的配置语言,整理出一份描述各项故障转移条件的专用 Runbook,这样后续的故障转移调整将会像编辑配置文件一样简单快捷。
与直接编辑 Go 二进制文件相比,新方法不仅更加轻巧、同时也提高了 Runbook 的重用能力,帮助灾难准备团队轻松完成一次又一次定期测试。下图所示,为 Runbook 流程和其中的任务。
转载请在文章开头和结尾显眼处标注:作者、出处和链接。不按规范转载侵权必究。
未经授权严禁转载,授权事宜请联系作者本人,侵权必究。
本文禁止转载,侵权必究。
授权事宜请至数英微信公众号(ID: digitaling) 后台授权,侵权必究。
评论
评论
推荐评论
暂无评论哦,快来评论一下吧!
全部评论(0条)