我们在Gitlab.com升级了PostgreSQL

2020-09-13 05:08:28

早在2020年5月,我们就与OnGres合作,将GitLab.com的主要Postgres集群从9.6版升级到了11版。我们在维护期间对其进行了升级,一切都按计划进行。我们拆开所有涉及的内容-从规划、测试和全流程自动化-以近乎完美地执行PostgreSQL升级。整个操作过程都被录制下来了,你可以在GitLab上不加过滤地观看。

最大的挑战是通过精心策划的PG_UPGRADE进行完整的机队重大升级。我们需要有一个回滚计划,以便在恢复时间目标之后立即优化我们的容量,同时保持12节点群集的6TB数据一致性,每秒为来自大约600万用户的300.000个聚合事务提供服务。

解决工程挑战的最佳方式是遵循设计图和设计文档。在创建蓝图的过程中,您需要定义我们试图解决的问题,评估最合适的解决方案,并考虑每个解决方案的优缺点。这里有一个指向该项目蓝图的链接。

蓝图之后是设计过程。我们在设计过程中详细介绍了实现过程,其中我们解释了执行设计所涉及的步骤和要求。此处链接了项目中的设计文档。

我们在GitLab 13.0中做出了一个业务决定,停止对PostgreSQL 10.0的支持。PostgreSQL版本9.6将于2021年11月停产,因此我们需要采取行动。

以下是PostgreSQL版本9.6和版本11之间在功能方面的一些主要区别:

新的PostgreSQL版本附带逻辑复制-版本10中引入的用于分发数据的发布/订阅框架。此功能可实现更顺畅的未来升级,并简化其他相关流程。

基于仲裁的提交,可确保我们的事务在群集的指定节点中提交。

PostgreSQL群集的基础架构容量包括12个用于OLTP和异步管道的N1-Highmem-96 GCP实例,外加两个不同规格的BI节点,每个节点具有96个CPU核心和614 GB RAM。群集HA是通过Patroni进行管理和配置的,它通过领事群集及其所有副本保持一致的领导者选举,使用复制插槽进行异步流复制,并针对GCS存储桶进行WAL发货。我们的设置当前使用Patroni HA解决方案,该解决方案不断收集有关群集、领导者检测和节点可用性的关键信息。它是使用Consul的关键功能(如DNS服务)实现的,DNS服务进而更新PgBouler端点,从而为流量保持不同的读写和只读架构。

出于HA目的,其中两个副本位于只读服务器列表池之外,由API使用,并由领事DNS提供服务。在对GitLab的架构进行了几次增强之后,我们能够将机队规模缩减到7个节点。

此外,整个集群每周平均每秒处理约181,000个事务。如下图所示,流量在周一增加,并在直到周五/周六的一周内保持吞吐量。流量数据对于设置适当的维护窗口至关重要,这样我们才能影响最少的用户。

该车队在一天中最繁忙的时间达到了每秒25万笔交易。

它还在处理每秒30万笔交易的峰值。GitLab.com正在达到每秒6万个连接。

PostgreSQL 11上不应该有回归。我们开发了一个自定义基准来执行广泛的回归测试。目标是确定PostgreSQL11中潜在的查询性能下降。

保留9.6集群示例:不是所有节点都应该升级,作为回滚过程,应该在9.6中保留一些节点。

为这些测试开发Ansible-Playbook并在PostgreSQL环境(使用Staging中的备份创建)上进行测试。

我们使用了单独的环境,可以随时停止、启动或恢复备份,专注于开发,并且能够在升级前不久恢复环境。

我们使用来自Staging的备份来使升级项目与环境保持联系,在环境中我们面临一些挑战,例如迁移数据库中不同的监控过程。

与Chef中的配置管理集成,并从数据库磁盘执行可在恢复方案中使用的快照。

我们告诉我们的客户,我们将安排一个维护窗口,目标是尽可能减少对他们工作的影响,并执行安全升级,而不会有任何数据丢失的风险。

在迭代和测试与我们的配置管理的集成之后,我们开始在试运行阶段执行端到端测试。这些测试是在内部宣布的,因此共享此环境的其他团队将知道试运行将在一段时间内不可用。

飞行前对环境进行检查。我们有时会发现证书有问题,或者做一些微小的调整来提高测试的效率。

停止所有到GitLab.com的应用和流量,在CloudFlare和HA-Proxy中增加维护模式,停止所有访问数据库、Sidekiq、WorkHorse、WEB-API等的应用。

从六节点群集升级三个节点。我们在生产中也有类似的策略,并考虑了回滚场景。

首先在领导者数据库节点上执行PostgreSQL升级的Ansible-Playbook,然后在辅助节点上执行Ansible-Playbook。

关于升级后:我们在Ansible-Playbook中执行了一些自动化测试,检查复制和数据是否一致。

接下来,我们启动应用程序以使我们的QA团队能够执行几个测试套件。他们在升级后的数据库上执行本地单元测试。我们调查了否定的结果。

测试完成后,我们再次停止应用程序以将临时群集恢复到版本9.6,并关闭升级到版本11的节点,然后启动旧群集。在Patroni将提升其中一个节点的位置,启动应用程序,群集可以收到返回的流量。我们将Chef配置恢复到集群9.6,并重新构建了这些数据库,以便有6个节点为下一次测试做好准备。

在生产中,这些步骤与转移非常相似,我们的计划是迁移8个节点,留下4个作为备份:

开始验证测试并恢复流量。我们执行了所需的最少数量的测试,因此我们可以在狭窄的维护窗口内容纳所有测试。

只有在数据库一致性出现任何问题或QA测试出错的情况下,才会调用回滚计划。这些步骤包括:

使用9.6版中的四个节点初始化群集。有了这四个节点,我们可以在流量较少时恢复GitLab.com的活动。

使用维护期间和升级之前拍摄的磁盘快照映像重新创建其他节点。

升级的所有步骤都在用于执行项目的模板中进行了详细说明。

Pg_upgrade过程允许我们将数据文件从PostgreSQL升级到较新的PostgreSQL主要版本,而无需使用需要更多停机时间的转储/重新加载策略。

正如PostgreSQL官方文档中所解释的那样,pg_upgrade工具避免执行dump/restore方法来升级PostgreSQL版本。在继续使用此工具之前,有一些重要的细节需要查看。主要的PostgreSQL版本添加了经常更改系统表布局的新功能,但内部数据存储格式很少更改。如果主要版本更改了数据存储格式,则无法使用PG_UPGRADE,因此我们必须验证主要版本之间包括哪些更改。

重要的是,任何外部模块也是二进制兼容的,尽管pg_upgrade不能检查这一点。对于GitLab升级,我们在升级前卸载了postgres_exporter等视图/扩展,以便在升级后重新创建它们(出于兼容性原因稍作修改)。

在执行升级之前,必须安装新版本的二进制文件。来自PostgreSQL和扩展的新二进制文件安装在列出要升级的主机集中。

使用pg_upgrade时有一些选项。我们选择在Leader节点上使用PG_Upgrade的链接模式,因为我们的维护窗口很窄,只有两个小时。该方法避免了通过inode硬链接文件来复制6TB数据文件。缺点是旧数据群集无法回滚到9.6。我们通过保留在9.6中的复制副本提供回滚路径,并将GCP快照作为次要选择。从头开始重建复制副本也不是一个选项,因此我们使用rsync使用增量功能对其进行升级。PG_UPGRADE';的文档说:从主服务器上位于新旧数据库集群目录之上的目录中,在每个备用服务器的主服务器上运行此命令。

Ansible-Playbook通过从引导者节点到每个副本都有一个任务,从新旧数据目录的父目录触发rsync命令来实现这一步骤。

任何迁移或数据库升级都需要在执行最终生产升级之前进行回归测试。对于团队来说,数据库测试是此过程中的关键步骤,根据来自生产的查询负载(在表PG_STAT_STATEMENTS中捕获)执行性能测试。这些都是在同一数据集中执行的-9.6版本执行一次,版本11执行另一次迭代。该过程在以下公开问题中捕获:

最后,基于OnGres在这一基准上的工作,GitLab将在未来进行一项新的基准测试:

在升级项目期间,升级团队对基础架构即代码(IAC)和自动化有着坚定的承诺:所有流程都必须完全自动化,以便在维护窗口期间将任何人为错误降至最低。本期GitLab PG_Upgrade模板详细介绍了PG_UPGRADE执行的所有步骤。

GitLab.com环境由Terraform和Chef管理。升级的所有自动化都是通过Ansible 2.9剧本和角色编写的,其中我们使用了两个Ansible剧本来自动化升级:

剧本是逐个任务交互运行的,为操作员提供了在任何给定执行点跳过或暂停的能力。在升级的试运行过程中,所有参与测试和迭代的团队都对每个步骤进行了审查。试运行环境允许我们使用与我们计划在生产中使用的相同过程来排练和发现问题。在执行并迭代了试运行中的自动化过程之后,我们实现了从PostgreSQL9.6到版本11的近乎完美的升级。

为了完成发布,QA GitLab团队报告了一些测试中发生的错误。请在此问题说明中找到此工作的参考资料。

该过程的第一部分是升级前部分,该部分处理为回滚保留的实例。我们进行了相应的分析,以确保新群集可以在不损失吞吐量的情况下从12个实例中的8个开始,为潜在的回滚方案保留4个实例-在这种情况下,它们可以通过标准的Patroni群集同步作为9.6群集。

在此阶段还需要停止与Postgres相关的服务,如PgBouner、Chef client和Patroni服务。

在继续升级之前,必须通知Patroni避免任何虚假的领导人选举,通过GCP快照(使用相应的低级备份API)进行一致备份,并通过Chef Run应用新设置。

升级主节点数据后,将触发rsync进程以将数据与复制副本同步。升级完成后,启动了Patroni服务,并且所有副本都可以轻松地使用新的群集配置。

二进制文件是由Chef安装的,新集群在版本上的设置是在同一个MR中定义的,该MR将从GitLab.com安装数据库中使用的扩展。

最后一个阶段涉及恢复流量,运行较早的吸尘器,最后启动PgBouler和Chef客户端服务。

最后,为执行生产升级做好充分准备,团队在那个周日(一些人在夜间,另一些人在清晨)在UTC时间上午08:45会面。这项服务最多会停机两个小时。当最后一次通知发出时,工程队被允许开始这一程序。

升级过程从停止流量和相关服务开始,以避免用户进入网站。

下图显示了服务在升级之前、维护期(图中的缺口)和恢复流量之后的流量和HTTP统计数据。

完成整个工作的总时间是四个小时,只需要两个小时的停机时间。

我们录制了完整的PostgreSQL升级,并将其未经过滤地发布到GitLab。预热爆米花🍿。

感谢Alvaro Hernandez和Sergio Ostapowicz共同撰写了这篇博客文章,感谢OnGres团队做出的贡献并与GitLab团队一起执行升级。