2020年5月,我们与OnGres合作,对GitLab上的Postgres集群进行了一次大版本更新,从9.6版本到11版本,所有升级都在维护窗口内运行,没有任何错误;更新中涉及的所有内容、规划、测试、全流程自动化全部解包,实现近乎完美的PostgreSQL升级。在本次版本更新中,我们面临的最大挑战是如何利用一个精心策划的pg_upgrade,方便高效的对整个项目进行重要的版本升级。为此,我们需要制定回滚计划,确保12节点集群6TB数据的一致性,同时优化恢复目标时间(RTO)后的容量,为600万用户提供每秒30万笔聚合交易。应对工程挑战的最佳解决方案是遵循蓝图和设计文档。在创建蓝图的过程中,我们需要定义目标问题,评估最合适的解决方案,并考虑每个解决方案的优缺点。在这里,我们附上为该项目准备的蓝图的链接。https://gitlab.com/gitlab-com/gl-infra/readiness/-/tree/master/library/database/postgres/Postgresql-upgrade/blueprint/1.我们为什么要升级PostgreSQL我们决定停在GitLab13.0支持PostgreSQL10.0,而PostgreSQL9.6版本将于2021年11月EOL(项目结束),因此我们需要采取相应行动。以下是PostgreSQL9.6和11版本的主要区别:表分区支持LIST、RANGE、HASH;存储过程支持事务;即时编译(JIT)加速查询表达式的执行;并行查询,增加并行数据定义功能;新版PostgreSQL继承了版本10中的“逻辑复制——分发数据的发布/订阅框架”,可以让以后的升级更加顺畅,并简化其他相关流程;基于Quorum的提交(commit),保证事务可以在集群指定的节点上提交;通过分区表提高查询性能。2.环境和架构PostgreSQL集群的基础设施容量由12个n1-highmem-96GCP实例组成,服务于OLTP和异步管道。同时,它还有两个不同规格的BI节点,每个节点有96个CPU核心和614GB内存。HA集群通过Patroni进行管理和配置,以确保Consul集群及其在异步流复制中的所有副本的领导者选举一致,使用复制槽和WAL复制GCS桶。https://github.com/zalando/patroni我们的配置目前使用PatroniHA解决方案,它会持续收集有关集群、领导者检测和节点可用性的关键信息。该解决方案是使用Consul的DNS服务等关键功能实现的,该服务反过来会更新PgBouncer端点,以确保读写和只读流量使用不同的模式。由于HA原因,GitLab.com架构有两个副本不在只读服务器列表池中,但由服务于API的ConsulDNS支持。在对GitLab架构进行了几次改进之后,我们能够将项目整体减少到7个节点。另外,我们整个集群平均每周每秒约181,000个事务,如下图所示,周一的流量会明显增加,并从周一到周五/周六保持这个吞吐量。我们需要让维护影响尽可能少的用户,所以流量数据的统计对于设置合适的维护窗口非常重要。GitLab.com上整体的连接统计项目数量在一天中最繁忙的时候可以达到每秒25,000个事务。GitLab.com上的commit数量统计同时,项目处理的交易峰值可以达到每秒30万笔交易,GitLab.com可以达到每秒6万个连接。3.我们的升级要求在升级生产环境之前,我们首先确定了一些要求:PostgreSQL11不能有回归。我们开发了一个自定义基准来运行更广泛的回归测试,目的是识别PostgreSQL11中潜在的查询性能下降;升级应该在整个项目范围内进行,并在维护窗口内完成;使用pg_upgrade升级,这取决于物理层,而不是逻辑或复制。保留9.6集群的样本。并不是所有的节点都需要升级,我们应该保留一些9.6的节点用于回滚;升级应该完全自动化,以减少人为错误的可能性;所有数据库升级的维护窗口只有30分钟;应该记录升级并将其发布。4.项目为了让生产升级顺利进行,我们将项目分为以下四个阶段:第一阶段:在封闭环境下开发自动化1)开发ansible-playbook,并在staginghttps备份的PostgreSQL环境中进行测试://gitlab.com/gitlab-com/gl-infra/db-migration/-/tree/master/pg-upgrade2)独立环境的使用让我们可以随时停止、启动或恢复备份,并且也让我们可以专注于开发,环境可以随时回滚到升级前。3)我们使用staging上的备份进行环境中的项目升级。在这个过程中,我们也遇到了一些挑战,比如在迁移数据库的过程中如何监控不同的程序。第二阶段:升级开发和配置管理在staging阶段的集成1)在Chef中集成配置管理,在数据库磁盘中运行快照(可用于恢复更新前的状态)。2)告知用户,本次维护窗口将努力将对其工作的影响降到最低,并在不存在数据丢失风险的情况下进行安全升级。3)在对配置管理进行迭代和集成测试后,我们开始在登台上运行端到端测试。这些测试是在内部公开的,所以共享这个环境的其他团队会知道在这段时间内暂存暂时不可用。第三阶段:在staging上测试端到端升级正式运行前检查环境。我们有时会在这一步发现认证问题,有时会做一些小的调整来提高测试效率;停止GitLab上的所有应用和流量,在CloudFlare和HA-proxy上增加维护模式,停止包括database、sidekiq、workhorse、WEB-API等可以访问数据库的应用;升级集群中六个节点中的三个。类似于生产中一些场景的策略,我们也准备了一个回滚场景;为PostgreSQL更新运行ansible-playbook。首先是数据库leader节点,后面是一些secondary节点;升级后:我们在ansible-playbook中运行一些自动化测试来检查复制的数据是否与原始数据匹配;接下来,启动应用程序,让我们的QA团队可以运行一些测试。他们在升级后的数据库上运行了本地单元测试,我们调查了负面结果。测试结束后,我们再次停止程序,将staging集群恢复到9.6版本,关闭升级节点到11版本,最后启动老版本集群。Patroni会推广其中一个节点,应用启动后集群可以收到流量反馈。我们将Chef配置恢复到9.6版本的集群,并重建数据库,留出6个节点用于下一次测试。我们在暂存阶段总共进行了7次测试,并根据反馈继续完善程序。Phase4:UpgradingintoProduction生产环境中的步骤与staging中的步骤类似,我们计划迁移8个节点并留下4个作为备份。执行项目前检查;宣布维修开始;运行ansible-playbook来停止流量和应用程序;运行ansible-playbook进行PostgreSQL升级;开始验证测试并恢复流量。我们只运行了必要的测试,以在短暂的维护窗口内完成所有工作。回滚计划只会在数据库不一致或QA测试错误时调用。具体步骤如下:停止PostgreSQL11集群;将Chef中的配置恢复到PostgreSQL9.6;在9.6版本中用四个节点初始化集群。有了这四个节点,我们可以在低流量期间恢复GitLab上的活动;开始接收流量,从而最大限度地减少停机时间;在维护期间和升级之前使用磁盘快照恢复其他节点;upgradeall步骤都在用于运行项目的模板中详细说明。五、pg_upgrade运行原理pg_upgrade允许我们将PostgreSQL数据文件升级到未来的主要版本,无需dump/reload策略,也无需更多的停机时间。https://www.postgresql.org/docs/11/pgupgrade.html正如官方PostgreSQL文档中所写,pg_upgrade工具通过避免执行转储/恢复来升级PostgreSQL版本。这里有几个细节需要注意:PostgreSQL的主要版本增加了新特性,这些特性经常改变系统表的布局,但内部数据存储格式基本保持不变。如果大版本升级改变了数据格式,pg_upgrade就不能继续使用了。因此,我们必须首先验证这些版本之间发生了什么变化。同样重要的是要注意任何外部模块必须是二进制兼容的,尽管你不能用pg_upgrade来检查这一点。对于GitLab的更新,我们在升级前卸载了postgres_exporter等视图和扩展,以便升级后重新创建。出于兼容性原因,我们需要稍微修改它们。https://github.com/wrouesnel/postgres_exporter在更新之前,您必须先安装新版本的二进制文件。新的PostgreSQL二进制文件和扩展安装在需要升级的主机上。pg_upgrade在使用时有很多选项。我们选择在Leader节点上使用pg_upgrade的链式模式,因为维护窗口很短,只有两个小时。这种模式可以通过inode硬链接文件,避免了复制6TB文件的麻烦。缺点是旧数据集群无法回滚到9.6版本。我们保留了9.6版的副本和GCP快照作为我们回退计划的回退路径。由于从头开始重建副本是不可能的,我们选择使用rsync的增量功能进行升级。pg_upgrade的官方文档也写道:“从主服务器上的旧数据库集群目录和新数据库集群目录上方的目录,在每个备用服务器的主服务器上运行此命令。”Ansible-playbook对于这一步的实现,是通过leader节点到每个replica的task,在新老数据目录下的父目录下触发rsync命令。6.回归测试基准任何迁移或数据库升级都需要在最终生产升级之前进行回归测试。对于团队来说,数据库测试是升级过程中至关重要的一步。根据生产过程中的查询次数进行性能测试,结果存储在pg_stat_statement表中。这些都在同一个数据集上运行,一次在9.6版,一次在11版迭代。这个循序渐进的过程可以在以下公共问题中找到:https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/准备工具;创建测试环境;计算能力;使用JMeter工具运行基准测试。最后,基于OnGres在这个benchmark上的工作,GitLab未来会跟进新的benchmark。主要生产数据库集群的容量评估;数据库容量和饱和度分析。7.升级过程:全自动完成。在升级项目中,升级团队坚持使用自动化和基础设施及代码工具(IaC)。所有流程都必须完全自动化,以减少维护窗口期间的人为错误。pg_upgrade的所有运行步骤都可以在这个GitLabpg_upgradetemplateissue中找到。https://gitlab.com/gitlab-com/gl-infra/db-migration/-/blob/master/.gitlab/issue_templates/pg_upgrade.mdGitLab.com的环境由Terraform和Chef共同管理,所有升级是自动化的它是用Ansible2.9剧本和角色编写的。我们使用两个ansible-playbook来完成升级自动化:一个ansible-playbook控制流量和应用:将Cloudflare设置为维护状态,不接受流量;停止HA代理;停止访问数据库的中间件:Sidekiq、Workhorse、WEB-API。另一个ansible-playbook运行升级过程:协调所有数据库和连接池的流量;控制Patroni集群和Consul实例;在主节点和辅助节点上执行升级;收集升级后的统计数据;使用Chef同步更改以保持配置管理的完整性;集群完整性和状态的验证;执行GCP快照;(可能)回滚过程。剧本以交互方式逐一运行所有任务,允许程序员在任何给定的执行点跳过或暂停程序。参与分阶段测试和迭代的所有团队成员都必须完成升级过程中的所有步骤。staging环境可以让我们通过练习提前发现升级过程中潜在的漏洞。在staging中执行和迭代自动化过程使我们能够实现从PostgreSQL9.6版到11版的基本无错误升级。为了完成此版本升级,GitLab的QA团队向我们反馈了测试中发现的一些问题,而这部分工作可以在本期中找到。https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/106#note_332170837八、PostgreSQL升级前的步骤升级工作的第一步是“升级前”,涉及预留给回滚的例子。我们做了相应的分析,保证新集群不会丢失吞吐量。从8个示例开始,我们将4个9.6版本的示例与标准Patroni集群同步,为后续可能需要的回滚做准备(总共12个实例)。在这个阶段,我们还需要停止依赖PostgreSQL的服务,比如PgBouncer、Chef客户端、Patroni服务。必须在更新正式开始之前通知Patroni,以避免任何虚假的领导者选举,通过GCP快照(通过相应的低级备份API获得)进行一致的备份,并通过运行Chef应用新设置。https://www.cybertec-postgresql.com/en/exclusive-backup-deprecated-what-now/?gclid=CjwKCAjwltH3BRB6EiwAhj0IUBjiSxBdmS11SUpITLCmk-oPkBa7udOWyA6bK6hig8neaiJc8n1WexoCq8UQAvD_BwE升级阶段九,PostgreSQL首先检查以下版本,停止所有节点:;验证所有节点是否同步并且不再接受任何流量。主节点数据升级后,会触发rsync进程同步所有副本数据。升级完成后,启动Patroni服务,以便所有副本都可以轻松更新到新集群的配置。通过Chef安装二进制文件,新集群的版本设置在来自GitLab.com的同一个MR中定义,可以为数据库中的扩展安装。最后阶段包括恢复流量、运行初始真空期以及最后启动PgBouncer和Chef客户端服务。10.迁移日结束,我们做好了在生产线上运行升级的充分准备,团队于周日早上8:45UTC开始开会(有些人是晚上)。服务最多会下线两个小时,等到最后通知下达后,工程团队才终于可以上手。升级过程从停止所有流量和相关服务开始,这是为了防止用户在更新期间访问网站。下图为服务更新前、维护期间(图标空白部分)、维护结束恢复流量后的流量和HTTP统计。GitLab.com上的统计数据显示,整个过程从开始到结束耗时四个小时,其中只有两个小时的停机时间。此外,我们记录了PostgreSQL更新的全过程,并发布在GitLabUnfiltered上。https://youtu.be/TKODwTtKWew
