转载自   CSDN(CSDNnews)

作者丨tomkrouper && shlomi-noach

翻译Diwei

MySQL对于GitHub的重要性不言而喻,本文作者从MySQL的备份、自动测试能否成功从备份恢复数据、模拟各种 master 可能挂掉的情况、自动测试 failover 是否正常、自动测试 schema 迁移等几个方面说明了为何会相信MySQL自动化。以下为译文。


对于GitHub来说,MySQL的基础架构是非常重要的组件。MySQL给GitHub.com、GitHub的API、身份验证等提供服务。每个git请求都或多或少会接触到MySQL。我们的任务是保持数据的可用性和完整性。即使MySQL集群服务出现意外了,也需要能够执行一些任务,比如繁重的清理工作、临时更新、在线模式迁移、集群拓扑重构、池化和负载平衡等等。我们有基础设施来自动化这些操作。在本文将分享一些例子,说明如何通过持续测试来建立我们对基础设施的信任。


备  份


对数据进行备份是非常重要的。如果还没有进行备份,那么这就是一个潜在的问题。Percona Xtrabackup是用来为MySQL数据库提供完整备份的工具。如果有一些已经确定需要保存的数据,也有一个专门备份数据的服务器。


除了完整的二进制备份之外,每天还运行几次逻辑备份。这些备份使工程师能够获得最新的数据。有时,他们希望从表中获得一组完整的数据,这样他们就可以在跟生产数据量一样的表上测试索引的更改是否有效,或者从某个时间点查看数据。Hubot允许恢复一张备份的表,当表已经导入好以后,它就会ping给我们。



数据被加载到非生产数据库,该数据库可供那些提出恢复数据要求的工程师们访问。


最后一种进行数据备份的方法是使用延时复制。与其说是一种备份,倒不如说是对数据的一种保障。对于每个生产集群,有一个延迟4小时复制的主机。假如某个查询没有运行,我们会在chatops(即一种会话驱动型开发的做法)上运行mysql panic。这将导致所有的延迟复制立即停止复制,然后“呼叫”数据库管理员。

这样就可以使用延迟复制来验证是否存在问题,然后将二进制日志快速转发到发生错误之前的位置。然后,我们可以将那个点之前的数据恢复到主服务器。


虽然说备份这个功能设计的很棒,但是如果一些未知或未捕获的错误导致备份没有成功,它们就会变得毫无价值。使用脚本恢复备份的好处就是它允许我们通过cron(是一个linux下的定时执行工具,可以在无需人工干预的情况下运行作业)自动验证备份文件是否有效。我们为每个集群都设置了一台专用主机,这台主机就是用来恢复最新的备份数据。这样可以确保备份正常运行,并且我们能够从备份中检索数据。


根据数据集大小会选择每天进行几次恢复。恢复后的服务器会按照预期加入到复制流中,并能够赶上复制。这种做法不仅仅是在测试备份文件是否可恢复,而且还可以测试需要识别的时间点是否准确。如果恢复过程中出现问题,我们会收到通知。


还追踪恢复的时间,所以我们很清楚在紧急情况下建立新的副本或恢复需要多长时间。


以下是自动恢复过程中Hubot编写的一些输出信息。



使用备份是为了给现有的MySQL服务器集添加一个新的副本。我们将构建一个新的服务器,一旦被告知它已经准备好,我们就可以开始恢复该特定集群的最新备份。有一个脚本可以运行所有的恢复命令,否则我们将不得不手工操作备份。我们的自动恢复系统实际上使用了相同的脚本。这大大简化了系统的构建过程,并允许我们使用聊天命令行的模式来启动和运行主机。下面显示的是在运行聊天命令行模式的数据恢复方法:


备份失败


使用Orchestrator (使你能够在工作环境中自动创建、监视和部署资源)为使用者执行自动化故障切换。期望Orchestrator可以正确检测master是否出现故障,然后可以指定副本进行升级,在所指定的副本下修复拓扑以后再升级。希望VIPs能够改变,池可以改变,客户端可以重新连接,puppet可以运行必要的组件等等。故障切换是一个复杂的任务,涉及到基础架构的许多方面。


为了建立对故障切换的信任,建立了一个类生产的测试集群,然后让它不断的崩溃以观察故障切换功能。


类生产环境与生产环境在很多方面的设置是完全相同的:硬件类型,操作系统,MySQL版本,网络环境,VIP,puppet配置,haproxy设置等。唯一不同之处在于测试集群不发送/接收生产流量。


在测试集群上模拟写入负载,同时避免复制延迟。写入负载不会超荷,但是有一些查询,这些查询是有意在相同的数据集中写入的。这在正常的时期作用并不明显,但事实证明是有用的,我们将会简要描述。


测试集群有三个数据中心的代表服务器。希望故障转移可以在同一数据中心内的服务器能够在对方时效时自动接替彼此的工作。希望能够在这样的约束下尽可能多地抢救出尽可能多的复制品,这些复制品能够尽可能的适用。orchestrator对拓扑结构没有先前的假设,它只能对出故障时的状态做出反应。


然而,我们有兴趣为故障转移创建复杂而多变的场景。我们的故障转移测试脚本为故障转移准备了理由:


  • 它识别现有的master;

  • 它重构了拓扑结构,使所有三个数据中心的代表成为主控。不同的DC具有不同的网络延迟,并且预期会在不同的时间对主机的崩溃做出反应;

  • 它选择一个解决崩溃方法。 我们选择杀掉master(kill -9)或网络划分它:iptables -j REJECT(nice-ish)或iptables -j DROP(无响应)。


脚本继续通过选择的方法使master崩溃,并等待orchestrator可靠地检测到崩溃并执行故障转移。虽然我们期望检测和升级在30秒内完成,但脚本并不会如你所愿,它在查找故障切换结果之前浪费掉一段指定的时间。比如:


  • 检查一个新的(不同的)主人是否到位;

  • 集群中有大量的副本;

  • master是可改变的;

  • 对master的写入在副本上可见;

  • 更新内部服务发现条目(新主人的身份如预期;旧主人已删除);

  • 其他内部检查。


这些测试确认故障转移是成功的,不仅是MySQL-wisee,而且是在我们更大的基础架构范围内。人们已经假设了一个VIP;具体的服务已经开始;信息到达了应该去的地方。


该脚本进一步恢复了失败的服务器:


  • 从备份中恢复它,从而隐式地测试我们的备份/恢复过程

  • 验证服务器配置如预期的那样(服务器不再相信它是主服务器)

  • 将其返回到复制集群,期望找到在主服务器上写入的数据


考虑以下预定的可视化故障转移测试:从一个运行良好的集群,看到问题在一些副本,诊断主(7136)死了,选择一个服务器来促进(a79d),重构拓扑低于服务器,为促进它(故障转移成功),恢复死去的主人,将它们转化为集群。

测试失败看起来像什么呢


测试脚本使用了一种stop-the-world的方法。故障转移组件中不管哪个环节出现问题,在管理员解决问题之前,整个过程都是失败的,而且后面的自动化测试也是无法执行的。当然我们会收到提醒,然后检查状态和日志。


测试脚本在以下环节中可能会失败:出现了意外的检测;故障转移期间;在备份/恢复问题上;太多服务器出现宕机;在故障转移后的意外配置上等等。


我们需要确保orchestrator正确地连接到服务器。这就是之前说的写入负载会起作用的地方:如果设置不正确,复制很容易被破坏。我们会得到DUPLICATE KEY或其他错误,以表示出了问题。


这一点尤其重要,所以我们对orchestrator进行了改进,引入了新的行为,并允许我们在一个安全的环境中测试这些更改。

混沌测试即将到来


上面演示的测试过程能够捕获(并已捕获)基础架构的许多问题。但这就够了吗?


在生产环境中总会有各种各样的问题。关于这些特定的测试方法,它不适用于我们的生产集群。它们不共享相同的流量和流量操作,也没有相同的服务器集。失败的类型可能会有所不同。


正在为生产集群设计混沌测试。 混沌测试将会在我们的生产中,但是按照预期的时间表和充分控制的方式来破坏碎片。混沌测试引入了对恢复机制的更高级别的信任,并影响(因此测试)更大的基础架构和应用程序。


这是一项微妙的工作:尽管我们承认需要进行混沌测试,但我们也希望避免对服务造成不必要的影响。不同的测试在风险级别和影响方面会有所不同,我们将努力确保服务的可用性。

Schema迁移


我们使用gh-ost从而将生产的schema进行迁移。gh-ost非常稳定,但是现在正在增加或者计划增加一些新的主要的功能,因此现在也处于开发当中。


gh-ost通过将数据复制到ghost表中,将二进制日志所拦截的正在进行的更改应用到ghost表上,即使原始表被写入到这个表中,也可以将表移到表上。然后,它将ghost表替换为原来的表。在迁移完成时,GitHub将继续使用由gh-ost生成并填充的表。


这一次,几乎所有的GitHub MySQL数据都是由gh-ost重新创建的,而且大部分都是重复的。我们必须高度信任gh-ost,让它一次又一次地篡改我们的数据,即使是在开发过程中。下面是我们如何获得信任的方法。


gh-ost提供了一个测试生产能力。它支持在副本上运行一个迁移,就像它在主服务器上运行的方式一样:gh-ost将连接到副本,并将其视为主服务器。它将以与实际主迁移相同的方式解析它的二进制日志。但是,它将复制行并将binlog事件应用到副本中,并避免将写入写入到主服务器上。


我们在生产中运行了专门的专用副本。这些副本不服务于生产流量。每个这样的副本都检索当前的生产表列表,并以随机的顺序迭代它们。一个接一个地选择一个表,并在该表上执行一个复制迁移。迁移实际上并没有修改表结构,而是运行一个简单的引擎=InnoDB。即使在生产中使用表时,测试也会运行迁移,从而复制实际的生产数据,并在二进制日志中应用真正的生产流量。


这些迁移可以被审核。下面是我们如何从聊天中检查运行测试的状态:



当一个测试迁移完成了对表数据的复制时,它停止复制并执行剪切操作,替换原来的表,用ghost表替换原来的表,然后交换回来。我们对实际替换数据不感兴趣。取而代之的是原始表和ghost表,两者都应该是相同的。我们通过检查这两个表的整个表数据来验证这一点。


一个测试可以完成:


  • 成功:一切都很顺利,校验和也一样。我们希望看到这一点。

  • 失败:执行的问题。由于迁移过程被杀死、复制问题等等,这种情况有时会发生,而且通常与ghost本身无关。

  • 校验和失败:表数据不一致。对于经过测试的分支,这需要修复。对于正在进行的主分支测试,这将意味着立即停止生产迁移。我们没有得到后者。


测试结果被审计,发送到机器人聊天室,作为事件发送给我们的度量系统。下面的图中的每一条垂直线代表一个成功的迁移测试:


连续运行这些测试。如果出现故障,我们会收到警报通知。当然,我们也可以去机器人聊天室看看发生了什么。

新版本测试


我们一直在改善gh-ost,我们的开发流程都是基于git的分支,最后再通过pull请求进行合并。


提交的 gh-ost pull请求通过持续集成(CI)进行基本的编译和单元测试。从技术上讲,该公司在技术上有资格合并,但更有趣的是,它有资格通过天堂进行部署。作为我们基础架构中的敏感组件,我们需要在合并成主之前,将gh-ost分支部署到密集的测试中。



有些PRs是小的,不影响数据本身。对状态消息、交互命令等的更改对ghost应用程序的影响较小,其他一些则对迁移逻辑和操作造成了显著的变化。我们将严格地测试这些数据,在我们的生产表舰队中运行,直到满足这些更改不会造成数据损坏威胁。

结  论


通过测试,我们就更想新机器的运作额。通过在生产中做这些自动化测试,我们可以不断的得到一切都如预期的那样运行的确认信息。随着基础架构的继续开发,我们也会通过调整测试从而满足最新的调整。


在没有测试的情况下,生产总是令人惊讶的。我们对生产环境的测试越多,我们对应用程序的期望和基础设施的能力就会得到更多的投入。