通过受版本控制的服务更新来消除停机

从历史上看,管理员需要让服务器离线来更新和升级本地软件。 然而,对于全球 24×7 服务来说,停机是完全不可能的。 许多现代云服务是用户运营业务的关键依赖。 任何时候都不会是关闭系统的好时机,那么团队如何在安装重要的安全和功能更新的同时提供持续的服务呢?

通过使用版本化更新,这些关键服务可以在客户积极使用它们的同时从一个版本无缝过渡到另一个版本。 并非所有更新都很难。 更新前端布局或样式很容易。 对功能的更改可能很棘手,但有一些众所周知的做法可以降低迁移风险。 然而,数据层带来的变化带来了一类需要特别考虑的新挑战。

单独更新图层

通过在多个数据中心和单独的数据存储中提供分布式在线服务,并非所有内容都可以同时更改。 如果典型的服务被划分为应用程序代码和数据库,它们的版本可能是相互独立的,那么其中一方需要吸收处理版本控制的复杂性。

通常,版本控制在应用程序代码中更容易处理。 较大的系统通常有相当多的遗留代码,例如驻留在其数据库中的 SQL。 应用程序代码应该处理复杂性,而不是使此 SQL 进一步复杂化。 具体来说,您可以创建一组理解 SQL 版本控制的工厂类。

在每次冲刺过程中,使用该版本创建一个新的接口,以便始终有与每个数据库版本匹配的代码。 您可以在部署期间轻松回滚任何二进制文件。 如果在部署新的二进制文件后出现问题,请恢复到以前的代码。 如果二进制部署成功,则启动数据库服务。

实际上它是如何工作的呢? 例如,假设您的团队当前正在部署冲刺 123。 二进制文件理解冲刺 123 数据库模式,它们理解冲刺 122 模式。 一般模式是使用 SQL 模式的两个版本/冲刺 N 和 N-1。 二进制文件查询数据库,确定与哪个模式版本会话,然后加载适当的绑定。 然后,应用程序代码处理新数据模式还不可用的情况。 一旦新版本可用,应用程序代码就可以开始使用最新数据库版本所启用的新功能。

仅使用数据层进行前滚

数据库升级后,如果出现问题,服务将处于前滚状态。 在线数据库迁移是复杂的,通常是多步骤的,因此向前滚动通常是解决问题的最佳方式。 换句话说,如果升级失败,那么回滚也可能失败。 投资于构建和测试团队从未想过要使用的回滚代码几乎没有价值。

部署顺序

考虑一个场景,您需要向数据库中添加一组列并转换一些数据。 这种转换需要对用户不可见,这意味着尽可能避免表锁,然后在尽可能短的时间内保持锁,这样它们就不会被察觉。

我们要做的第一件事是操作数据,可能是在并行表中使用 SQL 触发器来保持数据同步。大型数据迁移和转换有时必须是跨多个冲刺的多个部署的多步骤。

一旦并行创建了额外的数据或新模式,团队就会进入应用程序代码的部署模式。 在部署模式中,当代码调用数据库时,它首先获取模式上的锁,然后在运行存储过程后释放它。 在发出对数据库的调用和运行存储过程之间,数据库不能更改。

升级代码充当架构编写器,并请求对架构进行编写器锁定。 应用程序代码优先获取读卡器锁,而升级代码则在后台尝试获取写入器锁。 在写入程序锁定下,只允许对表执行少量非常快速的操作。 然后释放锁,应用程序记录数据库的新版本正在使用,并使用与新数据库版本匹配的接口。

数据库升级都是使用迁移模式执行的。 一组代码和脚本查看数据库的版本,然后进行增量更改,将模式从旧版本迁移到新版本。 所有迁移都是自动化的,并通过发布管理服务推出。

Web UI 也必须在不干扰用户的情况下进行更新。 升级 JavaScript 文件、样式表或图像时,请避免混合客户端加载的新旧版本。 这可能导致错误,可能会丢失正在进行的工作,例如用户正在编辑的字段。 因此,您应该将所有与部署相关联的文件放在一个单独的版本化文件夹中,从而对所有 JavaScript、CSS 和图像文件进行版本化。 当 Web UI 调用回应用层时,将加载具有指定版本的资产。 只有当用户操作导致整个页面刷新时,新的 Web UI 才会加载到浏览器中。 用户的体验不会因升级而中断。

后续步骤

几十年来,Microsoft 一直是全球最大的软件开发公司之一。 了解 Microsoft 如何使用 DevOps 操作可靠的系统