实现零停机时间部署


40

我正在尝试实现零停机时间部署,因此在理论上,我可以在下班时间减少部署,而在“更慢”的时间内部署更多。

我当前的设置,有些简化:

  • Web服务器A(.NET App)
  • Web服务器B(.NET App)
  • 数据库服务器(SQL Server)

我当前的部署过程:

  1. “停止” Web服务器A和B上的站点
  2. 升级数据库架构以获取正在部署的应用程序的版本
  3. 更新Web服务器A
  4. 更新Web服务器B
  5. 将所有内容恢复在线

当前问题

每月导致少量的停机时间-大约30分钟。我在下班时间这样做,所以这不是一个大问题-但这是我想摆脱的事情。

另外-没有办法真正退缩。我通常不制作回滚DB脚本-仅升级脚本。

利用负载均衡器

我希望能够一次升级一台Web服务器。从负载均衡器中取出Web服务器A,对其进行升级,使其重新联机,然后对Web服务器B重复上述步骤。

问题是数据库。我的软件的每个版本都需要针对不同版本的数据库执行-所以我有点“卡住了”。

可能的解决方案

我正在考虑的当前解决方案采用以下规则:

  • 切勿删除数据库表。
  • 永远不要删除数据库列。
  • 切勿重命名数据库列。
  • 切勿重新排列列。
  • 每个存储过程都必须进行版本控制。
    • 含义-编辑后,“ spFindAllThings”将变为“ spFindAllThings_2”。
    • 然后在再次编辑时变成“ spFindAllThings_3”。
    • 相同的规则适用于视图。

虽然,这似乎有点极端-我认为它可以解决问题。该应用程序的每个版本都将以不间断的方式访问数据库。该代码期望视图/存储过程产生某些结果-并使该“合同”有效。问题是-它只是马虎了。我知道我可以在应用程序部署一段时间后清理旧的存储过程,但是感觉很脏。另外-这取决于所有遵循这些规则的开发人员,这大部分都会发生,但是我想有人会犯错。

最后-我的问题

  • 这是草率的还是朴拙的?
  • 还有其他人这样做吗?
  • 其他人如何解决这个问题?

2
您的退出计划在哪里?您如何测试一切正常并且没有回归?
Deer Hunter

3
您不需要“从不”:“仅”需要确保每两个相邻的版本可以同时运行。这限制了您的升级路径,但没有像以前那样严重改变数据库架构。
Joachim Sauer 2013年

谢谢Joachim ...我想讲绝对的,以便使基本思想清晰明了-但您是正确的,我们可以制定一个可以与N个发行版向后兼容的策略,此时我们可以删除不必要的DB对象。
MattW 2013年

2
您将需要适当的回滚计划。有一天,您将需要它。
托尔比约恩Ravn的安徒生

1
以我的经验,对于大多数网站,您可能的解决方案要比其解决的问题差。它将增加的复杂性将比您现在预期的要昂贵。进行更改和添加功能的时间/精力可能要增加很多倍。我只认为这是针对绝对不能网站任何纷纷停工,永远
MGOwen '16

Answers:


14

这是一种非常实用的数据库支持的软件升级方法。它在2003年由Martin Fowler和Pramod Sadalage 进行了描述,随后被撰写在《重构数据库:进化数据库设计》中

当您说它看起来很草率时,我可以明白您的意思,但是当您有意且有预见地完成它时,花时间将未使用的结构从代码库和数据库中重构出来(当它们已被证明不再使用时),它的健壮性比基于升级和回滚脚本的更简单的解决方案。


5

“零停机时间”只是这种方法的许多可能原因之一。通过这种方式保持数据模型向后兼容,可以帮助您解决许多不同的问题:

  • 如果您有很多软件包正在访问数据库,那么如果架构更改影响到它们,则不必全部检查它们(在拥有多个团队且编写程序都访问同一数据库的大型组织中,架构更改可能会变得非常困难)

  • 如果需要,您可以签出其中一个程序的旧版本,它很可能会在新数据库中再次运行(只要您不希望旧程序正确处理任何新列)

  • 将存档数据导入/导出到当前数据库版本要容易得多

这是您列表的附加规则

  • 每个新列都应该为NULLable或提供有意义的默认值

(这可以确保即使不知道新列的旧程序在数据库中创建新记录时也不会破坏任何内容)。

当然,这种方法有一个真正的缺点:您的数据模型质量可能会随着时间下降。并且,如果您完全控制所有访问数据库的应用程序,并且可以在例如重命名列时轻松地重构所有这些应用程序,那么您可能会考虑以一种更简洁的方式重构事物。


4

从一种部署到另一种部署,其种类有所不同。

当然,您永远不能删除表或列。您永远都无法更改破坏接口兼容性的任何内容。您总是可以添加一个抽象层。但是随后您必须对该抽象版本进行版本控制,并对版本进行版本控制。

您需要问自己的问题是,是否每个发行版都以不向后兼容的方式更改了架构?

如果很少有发行版以这种方式更改架构,则数据库问题为静音。只需对应用程序服务器进行滚动部署。

我所见过的两件事对于最大程度地减少停机时间部署有最大的帮助:

  1. 力争向后兼容-至少在单个发行版中。您不会总是实现它,但是我敢打赌,您可以在90%或更多的发行版中实现它,尤其是在每个发行版很小的情况下。
  2. 具有发布前和发布后的数据库脚本。这样,您可以通过在部署应用程序代码之前创建新对象,然后在部署应用程序代码之后删除旧对象来处理重命名或接口更改。如果添加新的不可为空的列,则可以使用填充默认值的触发器在预发行脚本中将其添加为可为空。然后,在发布后,您可以放下触发器。

希望可以将其余的部署保存在维护窗口中。

其他有助于解决少数需要停机的部署的想法:

  • 您可以在代码中建立向后兼容性吗?例如,您的代码有什么方法可以支持多种类型的结果集?如果需要将列从int更改为double,则您的应用程序代码可以将其读取为字符串并进行解析。有点骇人听闻,但是,如果这是使您自己完成发布过程的临时代码,则可能并非世界末日。
  • 存储过程可以帮助您的应用程序代码与架构更改隔离。这只能走得很远,但确实有帮助。

2

您可能需要像这样额外花费一些精力。

  1. 通过导出来备份数据库
  2. 导入备份,但使用发行版(例如myDb_2_1)重命名
  3. 在myDB_2_1上执行数据库发布
  4. “停止” Web Server A上的应用程序池或将其从负载均衡器中取出
  5. 更新Web Server A,运行实施后测试并在必要时回滚
  6. 会话使Web服务器B流血并使Web服务器A重新循环
  7. 升级Web服务器B,然后放回负载平衡器

自然,Web更新将需要新的配置条目来指向新的Db模式。问题是,如果您每月进行一次发布,并且这是一个很小的团队,那么您实际上要进行多少个数据库更改,这些更改不向后兼容?如果您可以通过测试来控制这一点,则可以在没有停机时间的情况下进行自动部署,或者最坏的停机时间只有5分钟。


1
如果在存储备份之后但在停止服务器A之前(当)服务器A上的应用程序写入其数据库怎么办?总会有一个“漏洞窗口”。这些写操作将丢失,这可能是不可接受的。
sleske '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.