微软 Azure DevOps 是一套应用程序生命周期服务,提供了从代码管理到持续集成、持续交付、测试、监控等一系列功能。然而,就在 5 月 24 日,这个服务在巴西南部区域发生了长达 10 小时的宕机,影响了数千名客户。事后调查发现,竟然是因为一个简单的拼写错误,导致了 17 个生产数据库被误删。

事件背景起源于,Azure DevOps 工程师有时需要对生产数据库的快照进行保存,以调查报告的问题或测试性能改进。为了确保这些快照数据库得到清理,会有一个专门的后台每天运行,系统会在设定的时间段后删除旧快照。

在 Sprint 222 期间,Azure DevOps 工程师升级了代码库,将已弃用的 Microsoft.Azure.Managment.* 包替换为受支持的 Azure.ResourceManager.* NuGet 包。此举连带了大量的 pull request 变更请求,以寻求将旧包中的 API 调用替换为新包中的 API 调用。而其中就隐藏了有关快照删除作业中的一个拼写错误,它将删除 Azure SQL 数据库的调用换成了删除托管数据库的 Azure SQL Server 的调用。

Eric 称,运行此代码的条件很少见,因此测试机制没有很好地覆盖。

这个拼写错误出现在一个用于清理数据库快照的后台作业中。原本,这个作业是为了帮助 Azure DevOps 工程师偶尔保存生产数据库的快照,以便调查问题或测试性能改进。但是,在最近的一次代码升级中,工程师用一个新的 NuGet 包替换了一个已经弃用的包,导致了一个巨大的变更请求。在这个请求中,有一行代码将删除 Azure SQL 数据库的调用换成了删除托管数据库的 Azure SQL Server 的调用。也就是说,本来只想删除一些旧的快照数据库,结果却把整个服务器都删掉了。

这个错误并没有被及时发现,因为它只在特定条件下才会触发,而 Azure DevOps 的测试并没有覆盖这些极端情况。当这个错误代码被部署到巴西南部区域的客户环境时,就引发了灾难性的后果。17 个生产数据库被删除后,整个区域的服务无法处理。

虽然数据没有丢失,但恢复过程却非常复杂和耗时。首先,由于客户无法自行恢复 Azure SQL Server,必须由 Azure SQL 团队参与恢复工作。其次,由于数据库有不同的备份配置,导致恢复数据时出现了不匹配的问题。最后,在数据库开始恢复上线之后,还出现了一系列网络服务器和负载均衡器的问题,导致服务无法正常访问。

Azure DevOps 工程师在数据库删除开始后 20 分钟内检测到中断,并开始着手修复。目前数据已经全部恢复,但却花费了长达十个小时。对此 Mattingly 则解释了几个原因:

  • 首先,客户无法自己恢复 Azure SQL Server,因此必须由 Azure SQL 团队来恢复 Azure SQL Server。“确定我们需要 Azure SQL 的值班工程师,让他们参与进来并恢复服务器,这个过程大约需要一个小时。”
  • 其次,数据库有不同的备份配置,一些被配置为 Zone 冗余备份,另一些则被配置为较新的 Geo-zone 冗余备份。协调这种不匹配情况给恢复过程增添了不少时间。
  • 最后,在数据库开始重新上线后,由于 Web 服务器出现了一系列复杂的问题,即使是数据位于这些数据库中的客户,也无法访问整个 scale-unit。

根据介绍,这些问题源于服务器预热任务,该任务通过测试调用遍历可用数据库列表。在恢复过程中的数据库出现了一个错误,导致预热测试 “执行指数级的 backoff retry,使得正常情况下只需不到 1 秒的预热平均耗时了 90 分钟。”

更复杂的是,这个恢复过程是交错进行的,一旦有一两台服务器开始重新接受客户的流量,它们就会过载并出现故障。最终,恢复服务需要工程师阻断所有流向巴西南部 scale-unit 的流量,直到一切都准备就绪后再重新加入负载平衡器和处理流量。

微软方面表示,已经实施各种修复和重新配置,以防止问题再次发生。

  • 已经修复了快照删除作业中的错误。
  • 为快照删除作业创建了一个新测试,它针对真实的 Azure 资源充分执行快照数据库删除方案。
  • 正在为关键资源添加 Azure 资源管理器锁,以防止意外删除。
  • 确保所有的 Azure SQL 数据库备份都配置为 Geo-zone-redundant。
  • 确保所有未来的快照数据库都在生产数据库的不同 Azure SQL Server 实例上创建。
  • 正在修复 Web 服务器预热任务中的逻辑,以便即使数据库处于 offline 状态也能成功启动。
  • 正在创建一个新的 cmdlet 来恢复已删除的数据库,以确保恢复使用与删除之前相同的设置(包括备份冗余)

微软已经对此次事件进行了深入分析,并采取了各种措施来防止类似问题再次发生。包括修复快照删除作业中的 bug,增加更多的测试覆盖范围,为关键资源添加锁定机制,确保所有数据库备份使用相同的设置等等。微软也向所有受此次中断影响的客户表示了歉意,并承诺会持续改进服务质量和可靠性。

这起事件给我们提供了一个教训:即使是一个看似微不足道的拼写错误,也可能造成严重的后果。因此,在编写、审查和部署代码时,我们要格外小心和仔细,并且要有充分的测试和备份机制来应对可能出现的问题。