SQL数据库并不是DevOps的障碍

19-01-08 banq
              

来自acm的文章(jdon文章点击标题看原文),自动化和一点纪律可以实现更好的测试,更短的发布周期和更低的业务风险。(Thomas A. Limoncelli)

一位朋友最近对我说,“我们不能做DevOps,因为我们使用SQL数据库。” 我差点从椅子上掉下来。这种说法在许多层面都是错误的。

“但你不了解我们的情况!” 他拒绝了。“DevOps意味着我们将更频繁地部署我们软件的新版本!我们现在几乎无法处理部署,而且我们每年只会执行几次!”

我向他询问了他目前的部署过程。

“我们每隔几个月就会获得一个新软件,”他解释道。“将其投入生产需要大量工作。因为我们使用SQL,部署看起来像这样:首先,我们踢出所有用户并关闭应用程序。接下来,DBA(数据库管理员)修改数据库模式。他们的工作完成后,安装并启用了新的软件版本。这个过程需要花费很多时间,所以我们倾向于在周末这样做...我讨厌。如果失败了,我们必须恢复到备份磁带并恢复一切从头开始,重新开始。“

他总结道,“只是安排这样的事件需要数星期的谈判。我们通常会失去谈判,这就是为什么我们最终在周末这样做。每隔几个月这样做是痛苦的,这是压力最大的一个来源。如果我们不得不每周发布这个版本,我们大多数人都会放弃。我们没有周末!哎呀,我听说有些公司每天都会多次发布软件。如果我们这样做,我们的应用程序将永远是升级!“

首先,我要澄清一些误解。然后让我们谈谈一些使这些部署变得更容易的技术。

首先,DevOps不是一种技术,它是一种方法论。DevOps最简洁的定义是它将敏捷/精益方法从源代码一直应用到生产。这样做是为了“更快地提供价值”,这是一种说法,可以减少功能从构思到制作所需的时间。更频繁的发布意味着新写入的功能等待投入生产的空闲时间更短。

DevOps不要求或禁止任何特定的数据库技术或任何技术。你说你能或不能“做DevOps”,因为你使用特定技术就像是说你不能将敏捷应用于使用特定语言的项目。SQL可能是一个常见的“月X借口”,但这是一个微弱的借口。

DevOps和SQL数据库在某些人的思想中无法联系在一起。在2000年代和2010年初,发明和普及DevOps的公司经常是大型网站,巧合的是,它们也普及了NoSQL(密钥/价值存储)数据库。然而,将这两者联系起来会使与因果关系的相关性混淆。这些公司也在推广免费向员工提供美食午餐。我们都同意这不是DevOps的先决条件。

其次,我不确定是否有人可以“  DevOps”。您可以使用DevOps技术,方法等。也就是说,人们经常使用这句话,以为我已经失去了那场战斗。

我的朋友和我进一步讨论了他的情况,很快他就意识到DevOps并非不可能; 这只是一个艰难的过渡。然而,一旦过渡完成,生活将变得更加容易。

我的朋友还有一个问题。“看,”他坦白道,“这些部署都是冒险的。每次我们做一次都冒险公司的数据,说实话,我的工作。我只是不想这样做。每隔几个月做一次压力就足够了更频繁地做这些事吗?不,先生,这是不负责任的。“

正如我在前一篇专栏文章(“小批量原则”, 14(2)页[ https://queue.acm.org/detail.cfm?id=2945077 ])中所讨论的那样,当某些事情存在风险时,就会产生自然倾向寻求少做。与直觉相反,这实际上增加了风险。下次你做危险的事情时,你会更加失去实践,周围环境的累积变化会变得越来越大,使得未知副作用的失败几乎得到保证。

相反,DevOps采取激进的立场,即风险更高的事情应该更频繁地完成

较高的频率暴露了各种角落的轻微(和主要)问题,因为“这种情况每年只发生一次”。它迫使我们自动化流程,自动化流程测试,并使流程顺利进行,从而降低风险。它为参与者提供了更多的练习。实践是完美的。它不是逃避我们所害怕的事情,而是勇敢地承担风险并克服它。像任何经历过术后恢复的人一样,你重复练习直到它不再痛苦。

部署总是有一些固定成本。原则上,您应该始终将部署的固定成本降低到零。在不降低固定成本的情况下提高部署频率对业务是不利的,也是不负责任的。

本文的其余部分介绍了在使用SQL的环境中实现快速发布的两种实践。实现它们需要开发人员,质量保证和操作摆脱困境和协作,这在某些组织中是闻所未闻的,但却是DevOps的本质。结果将是一个更平稳,更少痛苦,并且当然不那么紧张的开展业务的方式。

 

技术1:全自动化架构的更新

在旧方法中,任何架构更改都需要关闭整个应用程序,而专家团队(或一个非常过度工作的DBA)会手动修改架构。如果您要进行全自动部署,则需要完全自动化架构更新。

为此,应用程序应该管理架构。架构的每个版本都应该编号。应用程序从模式版本1开始。该值存储在数据库中(想象一个单行表,其中一个字段存储值“1”)。当应用程序启动时,它应该知道它与架构版本1兼容,如果它在数据库中找不到该版本,它就会拒绝运行。

但是,要自动化架构更新,软件的下一个版本知道它需要架构的版本2,并且知道将版本1架构升级到版本2的SQL命令。在启动时,它看到版本为1,运行适当的模式升级命令,将存储在数据库中的版本号更新为2,然后继续运行该应用程序。

执行此操作的软件通常具有SQL架构更新命令表。数组索引n中的命令将模式从版本n-1升级到n。因此,无论找到哪个版本,软件都可以将数据库带到所需的模式版本。实际上,如果找到未初始化的数据库(例如,在测试环境中),它可能会循环遍历几十个架构更改,直到它到达最新版本。并非每个软件版本都需要更改架构; 因此,单独的版本号用于架构和软件。

有开源和商业系统来实现这个过程。其中一些产品比其他产品更复杂,支持各种语言,数据库系统,错误处理复杂性,以及它们是否也支持回滚。网络搜索“sql change automation”会找到很多。我最熟悉开源项目Mayflower for .NET代码(https://github.com/bretcope/Mayflower.NET)和Goose for Go(https://bitbucket.org/liamstask/goose)。

用于将数据库锁定几分钟甚至几小时的模式修改。这会导致应用程序超时并失败。由于无锁架构更新和在线重建索引功能,现代SQL数据库减少或消除了此类问题。这些功能可以在所有最新的SQL产品中找到,包括开源产品,如MariaDB,MySQL和PostgreSQL。请查看文档,了解在不中断的情况下可以和不可以执行的操作的详细信息。

一旦您的软件使用这些技术,采用CI(持续集成)变得非常容易。您的自动化测试环境可以包括在旧架构中构建数据库,升级它并运行新软件版本的测试。您的架构升级过程可能会在投入生产之前进行数百次测试。这应该为流程带来新的信心,降低架构升级的风险,并使DBA在升级中的个人参与脱钩。他们将欢度他们的周末。

我最喜欢的部分是你的架构现在被视为代码。已经消除了控制台上的手动工作,并且您已经能够在开发人员沙箱,测试环境,UAT(用户验收测试)环境和生产中完成整个过程。您可以多次运行该过程,修复并微调它。现在它是代码,您可以应用最好的代码管理和软件工程技术。

 

技术2:多个模式的编码

如何在分布式计算环境中升级数据库模式?

想象一下典型的基于Web的应用程序,它是在Web负载均衡器后面运行的同一软件的许多实例(副本)。每个实例都接收其HTTP流量的份额。实例访问同一数据库服务器。

当软件与数据库模式紧密耦合时,无法执行需要更改数据库模式的软件升级。如果您首先更改架构,那么实例将全部死亡或至少会被更改混淆; 你可以尽可能快地升级实例,但是你已经因为中断而丢失了游戏。

啊哈!为什么不先升级实例!遗憾的是,当您逐个升级实例软件时,新升级的实例无法启动,因为它们检测到错误的架构。在模式更改为与软件匹配之前,您将最终停机。

显而易见的解决方案是:违反物理定律,并在所有实例上升级软件的同时更改数据库架构。如果你能做到这一点,一切都会好的。

可悲的是,ACM有一项反对违反物理定律的政策,就像大多数雇主一样。这就是为什么传统方法是关闭整个应用程序,升级所有内容,然后将其重新联机。这是我们能做的最好的事情,直到IEEE的朋友们知道如何暂停时间。

无论你是通过违反物理学还是通过安排停机来制止世界,你都引入了一个更大的问题:你做了很多个别的改变,但是在系统再次运行之前你不知道是否有任何改变。你也不知道哪些累积的变化会导致事情破裂。

这种“大爆炸”的变化是有风险的。一次制作和验证更改的风险较小。如果您同时进行多项更改,并且存在问题,则必须启动二进制搜索以确定导致问题的更改。如果您一次进行一次更改,并且出现故障,则搜索变得简单。退出一个更改比更多更容易。

Heck,即使是谷歌,凭借其高度复杂的测试技术和方法,也了解暂存环境和生产环境之间的细微差别可能导致部署失败。他们将软件发布为“金丝雀”:升级一个实例,等待它是否正常启动,然后随着时间的推移缓慢升级其余实例。这不是一种测试方法,这是针对不完整测试的保险政策 - 而不是他们的测试人员并不优秀,但没有人是完美的。

金丝雀技术现在是行业最佳实践,甚至嵌入在Kubernetes系统中。(金丝雀这个词源自“煤矿中的金丝雀”。第一个要升级的事件就像一个警告标志那样存在问题,正如煤矿工人过去带来的鸟类,通常是金丝雀,它们比人类对毒气更敏感。如果金丝雀死了,那就是撤离的迹象。)

由于这些问题是由软件紧密耦合到特定模式引起的,因此解决方案是放松耦合。这些可以通过编写同时适用于多个模式的软件来解耦。这是分离发布和激活。

第一阶段是编写不对表中的字段进行假设的代码。在SQL术语中,这意味着SELECT语句应该指定所需的确切字段,而不是使用SELECT *。如果您使用SELECT *,请不要假设字段按特定顺序排列。LAST_NAME可能是今天的第三个字段,但可能不是明天。

有了这个规则,从架构中删除字段很容易。部署了不使用该字段的新版本,一切正常。在所有实例运行更新版本后,可以更改架构。实际上,由于残余字段被忽略,您可以稍后拖延并删除它,可能要等到下一个(否则无关的)模式更改。

添加新字段很简单,只需在使用它的第一个软件版本之前在模式中创建它。我们使用技术1(应用程序管理自己的架构)并部署修改架构但不使用该字段的版本。使用正确的事务锁定,使用新软件重新启动的第一个实例将干净地更新架构。如果出现问题,金丝雀就会死亡。您可以修复软件并尝试新的金丝雀。还原架构更改是可选的。

由于模式和软件是分离的,开发人员可以在闲暇时开始使用新领域。虽然在过去升级需要找到一个与多个团队兼容的维护窗口,但现在这个过程是分离的,并且所有各方都可以以协调的方式工作,但不能保持同步。

更复杂的变化需要更多的计划。分割字段,删除某些字段,添加其他字段等等时,真正的乐趣就开始了。

首先,必须编写软件以使用旧模式和新模式,最重要的是还必须处理过渡阶段。假设您要从一个字段中存储一个人的完整名称,到将其分成第一个,中间名,姓氏,标题等的单个字段进行迁移。软件必须检测哪些字段存在并采取适当的行动。当数据库处于转换状态并且存在两组字段时,它也必须正常工作。一旦存在两组字段,就可以运行批处理作业,该作业分割名称并存储各个部分,使旧字段为空。代码必须处理某些行未转换而其他行已转换的情况。

进行此转换的过程有许多阶段,包括创建新字段,更新软件,迁移数据和删除旧字段。这被称为云系统管理实践中的McHenry技术(我与Strata R. Chalup和Christina J. Hogan共同撰写):Michael T. Nygard 设计和部署生产就绪软件

 

现场架构变革的五个阶段

1.正在运行的代码读取和写入旧模式,仅从表或视图中选择所需的字段。这是原始状态。

2.展开:通过添加任何新字段但不删除任何旧字段来修改模式。没有进行代码更改。如果需要回滚。

3.修改代码以使用新的模式字段并推送到生产中。如果需要回滚,它只会恢复到阶段2.此时,可以在系统运行时进行任何数据转换。

4.合同:删除引用旧的,现在未使用的字段的代码并将其推送到生产环境中。如果需要回滚,它只会恢复到第3阶段。

5.旧的,现在将未使用的字段从架构中删除。万一此时需要回滚,数据库将简单地恢复到第4阶段。

 

该技术非常复杂,可以处理实时分布式系统上最复杂的模式更改。此外,每个突变都可以单独回滚。

对于特殊情况,可以减少阶段数。如果只添加字段,则跳过阶段5,因为没有要删除的内容。该过程简化为本文前面所述的内容。阶段4和5可以组合或重叠。或者,可以将来自一个模式改变的阶段5合并到下一个模式改变的阶段2中。

使用这些技术,您可以在不停机的情况下完成最复杂的架构更改。

 

总结

使用SQL数据库并不是创建DevOps的障碍。自动化模式管理和一些开发人员规范可以实现更加强大和可重复的测试,缩短发布周期并降低业务风险。

自动化版本解放了我们。它将令人担忧,压力大的手动升级过程转变为无事故发生的常规事件。它可以降低商业风险,但更重要的是,它可以创造一个更加可持

如果您可以放心地部署新版本,则可以更频繁地执行此操作。之前未发布数周或数月的新功能现在可以更快地吸引用户。错误修复得更快。安全孔越早关闭。它使公司能够为客户提供更好的价值。

              

1