SQL回滚和自动数据库部署的陷阱

转载:https://octopus.com/blog/data...
SQL回滚和自动数据库部署的陷阱_第1张图片

尽管可以执行SQL回滚以还原数据库更改,但问题是,应该吗?回滚数据库更改并不像回滚代码更改那么简单。数据库是应用程序的命脉。回滚失败可能会导致数据损坏或删除。本文介绍了导致数据损坏或删除的陷阱,以及为什么前滚是一种更好的方法。

TL; DR

前滚是更好的选择。在特定情况下,可以回滚数据库更改,但是这种情况很少见。设计数据库回滚过程所花费的精力应该集中在使部署尽可能快速和安全上。快速安全的数据库部署使您可以前滚。

本文是我们编写的有关自动数据库部署的系列文章的一部分。

回滚方案

回滚的需求通常将分为以下三类之一:

  • 部署期间。
  • 部署后的验证期间。
  • 部署和验证后。

部署和验证是有意分开的,因为用户可能会在验证期间使用该应用程序。这取决于部署策略(中断,金丝雀或蓝/绿色部署)以及您的应用程序支持什么。例如,Octopus Deploy具有维护模式功能,该功能可以防止其他非管理员部署代码,即使他们仍然可以访问它。维护模式等功能支持部署后的验证。

让我们看一下典型应用程序的部署过程。该应用程序具有一个数据库,一个用React编写的前端,一个RESTful API后端和一个后台服务。部署过程可能类似于:

  1. 批准部署到生产中。
  2. 部署数据库更改。
  3. 部署后台服务。
  4. 对于每个Web服务器:

    1. 将服务器从负载均衡器中取出。
    2. 部署RESTful API。
    3. 部署前端。
    4. 运行健全性检查测试。
    5. 将服务器重新添加到负载均衡器中。
  5. 通过运行全套测试来验证发行版。
  6. 通知发布的适当人员。

对于某些部署,数据库更改很简单。对存储过程的新索引或效率调整。回滚这些更改几乎没有影响。回滚复杂的更改,添加列,移动列,添加表或拆分表将产生重大影响。并且不要忘记迁移脚本。这些回滚要复杂得多。

为什么回滚部署?

我听到的三个常见答案是:

  1. 部署出了点问题。
  2. 我们运行了一系列测试,但其中一个或多个测试失败。
  3. 用户发现了一个显示停止错误。

好的,很酷,但是_出了点问题_有点含糊。部署失败的发生可能有多种原因,例如:

  • 由于网络问题而失败。可能是网络管理员重新启动了交换机,并终止了与部署目标的连接。
  • DBA会应用角色成员身份更改,但是新角色没有数据读取器权限。
  • 部署目标已关闭。
  • 当天早些时候,用于部署的服务帐户的密码已更改,但是部署仍在使用旧密码。
  • 更新的应用程序需要.NET Core 3.0,但仅安装了.NET Core 2.2。
  • 部署过程中的配置错误。

我认为这些都不足以证明回滚数据库更改是合理的。通常,重试可以解决这些问题。在这种情况下,我们向八达通部署中的部署中添加了指导性故障功能,因此您可以选择如何处理部署故障。

第二个原因,回滚是因为一个或多个测试失败。是否所有测试都必须通过?就像部署失败一样,测试可能由于多种原因而失败,例如:

  • 集成测试使应用程序调出到外部系统。外部系统要等到明天才能更新,并且会返回意外的结果。结果对于外部系统的当前版本是正确的,但是对于明天要发布的版本是不正确的。
  • 有人不小心更改了一些测试客户的姓名。测试原本是“测试用户”,但是现在的数据是“测试用户#1”
  • 外部系统与您的应用程序同时更新。测试将失败,直到该外部系统从现在起20分钟启动并运行为止。

同样,我认为这些故障中的任何一个都不能证明回滚数据库更改是合理的。通常只有在星体对齐时才能进行集成测试。

部署的变更量也可能影响回滚。当进行了几次更改时,回滚的愿望要高得多,而不是几十次更改。当发现显示停止错误时,回滚的愿望成倍增加。缺点是它们通常由用户找到。那变成了22。为了找到显示停止错误,用户必须使用该应用程序。但是,一旦用户开始使用该应用程序,回滚将变得更加困难。

备份和还原的陷阱

您可能会想自己:“为什么不在部署过程开始之前进行数据库备份?这将解决回滚这些复杂更改的问题。” 备份的使用寿命有限。在以下情况下,备份将变得无用:

  1. 用户开始使用该应用程序的新版本。
  2. 下一次计划的备份发生。

这些事件可能会在一分钟或几个小时后发生。回滚将意味着更改数据。或更糟糕的是,删除数据。

通过备份还原进行回滚并不是在真空中进行的。应用程序共享数据非常普遍。

在Octopus Deploy工作之前,我研究了贷款发起系统,其主要目的是收集数据以发送到决策引擎。决策引擎确定是否应该拒绝,自动批准贷款,或者需要贷款员来收集更多数据。贷款员输入更多数据后,他们可以将其提交以供后续决策。贷款发起系统和决策引擎是具有独立数据库的独立应用程序。决策引擎对第一个决策的处理与对第二个决策的处理不同。它使用贷款发起系统发送的唯一标识符来跟踪其所做的决策。

第一次在测试环境中通过备份还原贷款来源系统时,我们开始从决策引擎获得意外结果。问题是决策引擎的数据库没有从备份中还原。我们看到了两个问题:

问题1:

  1. 用户提交贷款。
  2. 决策引擎自动批准贷款。
  3. 从备份还原。
  4. 用户再次提交贷款。
  5. 决策引擎将其视为第二个请求,并拒绝贷款,认为这是欺诈性贷款。

问题2:

  1. 用户为客户A提交贷款。
  2. 决策引擎自动批准贷款。
  3. 从备份还原。
  4. 还原操作删除了整个贷款记录以及其他数十个贷款记录。
  5. 用户重新创建并提交了贷款,但是它获得了一个不同的唯一ID以发送到决策引擎。
  6. 决策引擎已经具有客户B的唯一ID,并且它基于客户A和B的信息进行决策。

如您所见,备份不应用于回滚。它们应仅用于灾难恢复。我看到的典型备份计划是每周一次完整备份,每天一次局部备份,一天中的时间点备份。这些备份是在数据库服务器或数据中心丢失的情况下完成的。

陷阱回滚脚本

我已经看到一些公司制定了规则:_对于每个数据库更改,必须编写相应的SQL回滚脚本_。但是,回滚脚本有其自身的陷阱。

编写SQL回滚脚本需要花费时间。如果脚本从不运行,那么这将浪费时间。如果您有几十个成功的部署,则编写回滚脚本的动机将会降低。随着时间的流逝,人们开始公开质疑为什么首先需要创建它。最终,脚本成为回滚的最低要求。它们被编写为选中一个复选框。

更重要的是,必须对脚本进行测试。这导致了进一步的问题,例如:

  • 谁来测试他们?
  • 他们什么时候测试?
  • 它是自动测试还是手动测试?

如果答案是肯定的,那么_Bob会在产品发布之前的早晨进行测试_,那么测试很可能仅在一半时间内进行。如果这不是一项阻塞性任务,并且不是自动化的,那么它将无法完成。

回滚脚本的寿命有限,就像数据库备份一样。例如,当回滚不中断时,删除新索引或还原经过调整的存储过程,则使用寿命很长。例如,当更改中断时,将删除新的列或表,则使用寿命将受到限制。

例如,如果将列添加到表中,则回滚脚本将删除该列。简单。但是,如果该列已投入生产两天,并且数百名用户已将数据保存到该列,回滚脚本是否仍应删除该列?在大多数应用程序中,删除数据非常重要。

在何时需要回滚脚本的情况下,想出一个决策树是一种诱惑。最后,这会使情况变得更糟。一个场景又一个场景的场景将被添加到树中,并且将变成一个笨拙的混乱。

向前发展:一种不同的思维方式

部署后,一旦用户开始使用该应用程序,您就可以跨过Rubicon了。有人可能会争辩说,一旦验证开始,Rubicon就会越过。

通常,成功回滚部署的工作量远远超过了将修补程序推送到生产所需的工作量。

在针对贷款发起系统自动进行数据库部署之后,部署花费了不到10分钟的时间。数据库部署只有3-4分钟。我们有四个环境,当我们知道问题出在哪里时,我们可以检入更改,进行验证,然后在30分钟内将其推送到所有四个环境。如果我们想跳过两个较低的环境,则不到20分钟。

在部署后危机期间提出回滚主题时,团队将必须:

  • 分析以前的部署发生了什么变化。通常,它涉及打开差异工具并逐行进行更改。
  • 创建要测试的区域列表,以确保应用程序在回滚后不会崩溃。
  • 确定要更改的数据。
  • 确定要删除的数据。
  • 确定是需要回滚数据库还是仅回滚代码。
  • 创建生产的备份并将其还原到测试服务器上。
  • 回滚先前的部署并开始测试。

在部署后危机期间,这种分析并不是在真空中进行的。用户要求知道发生了什么。上级人士要求提供状态报告。必须联系DBA以获得生产数据库的副本以进行测试。期望在不到两个小时的时间内完成这些步骤是不现实的。我已经看到团队花了整天的时间来完成所有这些步骤。

理想的解决方案:前滚并分阶段向后兼容的更改

数据库部署通常是部署中风险最高的部分。可以降低这种风险吗?

我回想起我所做的所有生产部署,包括数据库。当变化是在数小时或数天前进行时,压力水平就不存在了。这使我们有时间来验证工作时间内的更改。该代码仍在部署窗口中进行了部署。部署代码通常很快。在部署窗口中进行验证的速度要快得多,因为大多数验证已完成。

只有当数据库更改向后兼容时,这才有可能,而且这需要大量的纪律。有关应如何进行向后兼容的数据库更改的详细信息,请参阅我关于Blue / Green Database Deployments的文章。这个例子有点极端,但是我认为花在它上面的时间是值得的。

这种方法还意味着部署数据库更改的过程需要与部署代码更改的过程分开。它还需要更多的计划,因为您必须处理两个生产部署。即使有了这些,我还是认为回滚应该是最后的选择。数据库不必回滚,但是代码可以。这可能会删除功能并令用户沮丧。

这样做并不总是可行的。我的个人规则是:

  • 尽可能使数据库更改向后兼容。
  • 如果可能,请在代码部署之前的生产小时甚至几天内进行阶段数据库更改。
  • 除非发生灾难性事件,否则请向前滚动。

结论

要努力成功地回滚部署远远超过它会采取修复推到生产的努力。与回滚相比,回滚出现问题的可能性要大得多。对于数据库回滚尤其如此。一天只有几个小时,花时间花在部署上更好的时间要比花时间担心回滚和所有可能的场景要好。

祝您部署愉快!

如果您喜欢这篇文章,并且我们有完整的自动化数据库部署系列供您阅读。

你可能感兴趣的:(运维回滚)