百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 文章教程 > 正文

一次近乎完美的PostgreSQL版本大升级实践

xsobi 2024-12-01 05:36 1 浏览

2020 年 5 月,我们与 OnGres 合作,对 GitLab 上的 Postgres 集群进行版本大更新,从 9.6 版本升级到 11 版本。升级全部在维护窗口内运行,没有丝毫差错;更新中所有涉及的内容、计划、测试,以及全流程自动化,全部进行拆包,只为实现一次近乎完美的 PostgreSQL 升级。

本次版本更新,我们面临的最大难题在于如何利用一个规划完善的 pg_upgrade,方便且高效地对整体项目进行重要版本升级。为此,我们需要制定一个回滚计划,以保证 12 节点集群的 6 TB 数据一致的同时,优化恢复目标时间(RTO)后的容量,为 600 万用户提供每秒 300000 次的聚合交易服务。

解决工程难题的最佳方案是按照蓝图和设计文档行事。在创建蓝图的过程中,我们需要定义目标问题,评估最合适的解决方案,并考虑每个解决方案的优缺点。

在此,我们附上为这个项目准备的蓝图链接。

我们为什么要升级PostgreSQL

我们决定在 GitLab 13.0 中停止对 PostgreSQL 10.0 的支持,而 PostgreSQL 9.6 版本将在 2021 年 11 月 EOL(项目终止),因此,我们需要采取相应的行动。

以下是 PostgreSQL9.6 和 11 版本之间的主要区别:

  • 表分区支持 LIST、RANGE,以及 HASH
  • 存储过程支持事务
  • 即时编译(JIT)加快查询表达式的运行速度
  • 并行查询,增加并行化数据定义功能
  • 新版本的 PostgreSQL 继承了版本 10 中的“逻辑复制——分发数据的发布 / 订阅框架”,该功能可以使日后的升级更加顺滑,简化了其他相关流程
  • 基于 Quorum 的提交(commit),确保事务能在集群中指定节点进行提交
  • 提升了通过分区表进行查询的性能

环境与架构

PostgreSQL 集群的基础架构容量由 12 个服务于 OLTP 以及异步管道的 n1-highmem-96 GCP 示例组成,同时还有两个不同规格的 BI 节点,每个节点都有 96 个 CPU 内核以及 614GB 的 RAM。HA 集群通过 Patroni 进行管理和配置,以保证 Consul 集群及其所有复制体在异步流复制中,使用复制槽和 WAL 对 GCS 存储桶进行复制工作时的 leader 选举一致性。

我们的配置目前使用的是 Patroni HA 解决方案,它会不断收集集群、leader 检测,以及节点可用性的关键信息。该解决方案采用 Consul 的 DNS 服务等关键功能来实现,进而更新 PgBouncer 端点,确保读写和只读流量使用不同架构。

因为 HA 的缘故,其中两个复制体不在只读服务器列表池中,而是由 Consul DNS 支持,服务于 API。对 GitLab 架构的几次改进后,我们得以将项目整体降到 7 个节点。

此外,我们的整个集群平均每周要处理大约 181000 个交易每秒,如下图所示,流量会在周一有明显增加,并在周一至周五 / 六内保持该吞吐量。我们需要让维护影响到尽量少的用户,因此流量数据的统计对于设置合适的维护窗口至关重要。

项目整体在全天中最忙碌的时刻可以到达 25000 交易每秒。

与此同时,项目处理的交易峰值可以到达每秒 30 万次交易,GitLab.com 能到达每秒 6 万次连接。

我们的升级需求

在生产环境进行升级前,我们首先确定了一些需求:

  • PostgreSQL 版本 11 上不能有回归。我们开发了一个自定义基准测试来运行更广泛的回归测试,目标是识别 PostgreSQL 11 中潜在的查询性能下降。
  • 升级应当针对整体项目,并在维护窗口内完成。
  • 使用 pg_upgrade 升级,其依赖于物理层面,而非逻辑或者复制。
  • 保留一个 9.6 版本的集群样本。并非所有节点都需要升级,我们应保留一些 9.6 版本的节点以备回滚。
  • 升级应全自动化,以降低人类失误的可能性。
  • 全部数据库升级的维护窗口只有 30 分钟。
  • 升级应留有记录并将其发布。

项目

为使生产升级能顺利运行,我们将项目划分为以下几个阶段:

第一阶段:在封闭环境中开发自动化

  • 开发 ansible-playbook ,并在 staging 上备份的 PostgreSQL 环境中进行测试。
  • 独立环境的使用让我们可以随时停止、启动,或者恢复备份,也让我们专注开发,并得以将环境随时回滚到升级前。
  • 我们使用 staging 上的备份在环境中进行项目升级,在这个过程中,我们也遇到一些诸如在迁移数据库的过程中如何监视不同程序之类的挑战。

第二阶段:在 staging 中将升级开发与配置管理进行分段式融合

  • 在 Chef 中集成配置管理,并运行数据库磁盘中的一个快照(可用于还原更新前状态)。
  • 通知用户,本次维护窗口将力争对他们工作的影响降到最低,并在没有数据损失风险的情况下进行安全升级。
  • 在对配置管理进行迭代和集成测试后,我们开始在 staging 上运行端到端测试。这些测试内容是在内部公开的,所以其他共享这个环境的团队会知道 staging 在这段时间暂时不可用。

第三阶段:在 staging 上测试端到端升级

  • 正式运行前对环境的检查。我们有时候会在这一步发现认证的问题,有时候也会做一些能提升测试效率的小调整。
  • 停止 GitLab 上所有应用和流量,在 CloudFlare 和 HA-proxy 上添加维护模式,停止包括数据库、sidekiq、workhorse、WEB-API 等一切能访问数据库的应用。
  • 升级集群中六个节点中的三个。与生产中部分场景的策略类似,我们同样准备了回滚方案。
  • 为 PostgreSQL 的更新运行 ansible-playbook。首先是数据库 leader 节点,之后是一些二级节点。
  • 升级之后:我们在 ansible-playbook 中运行了一些自动化测试,用以检测复制数据与原数据是否相符。
  • 接下来启动应用程序,让我们的 QA 团队能运行一些测试。他们在升级后的数据库上运行了本地单元测试,我们对负面结果进行了调查。
  • 测试结束后,我们再次停止程序运行,并将 staging 集群还原到 9.6 版本,将升级过后的节点关闭到版本 11,最后启动旧版集群。Patroni promote 其中一个节点,启动应用后集群就可以收到流量反馈。我们将 Chef 的配置恢复到集群 9.6 版本后重建数据库,留出六个节点为下次测试做准备。

我们总共在 staging 中运行过 7 次测试,并通过反馈不断完善程序。

第四阶段:升级进入生产环境

生产环境的步骤与 staging 中类似,我们计划迁移八个节点,留下四个作为备份。

  • 执行项目前期检查
  • 宣布维护开始
  • 运行 ansible-playbook 以停止流量和应用
  • 运行 ansible-playbook 以进行 PostgreSQL 升级
  • 开始验证测试并恢复流量。我们只运行了必需的测试,才能在短暂的维护窗口内完成所有内容

回滚计划只会在数据库不一致或者 QA 测试出错时才调用,以下是具体步骤:

  • 停止 PostgreSQL 11 集群
  • 还原 Chef 中配置到 PostgreSQL 9.6
  • 用 9.6 版本中的四个节点初始化集群。通过这四个节点,我们可以在流量较低的时候恢复 GitLab 上的活动。
  • 开始接收流量,借此可以尽量减少停机时间。
  • 使用在维护期间和升级前的磁盘快照恢复其他节点

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

pg_upgrade 运行原理

pg_upgrade 让我们可以在不用 dump/reload 策略,不用更多停机时间的情况下,将 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 的官方文档也有写:“从主服务器上位于旧数据库集群目录和新数据库集群目录上方的目录中,在每个备用服务器的 primary 上运行此命令。”

ansible-playbook 对于这一步的实现,是通过从 leader 节点到每一个副本都有一个任务,在新旧数据目录中的父目录中触发 rsync 命令。

回归测试的基准

任何的迁移或数据库升级都需要在最终的生产升级前进行回归测试。对团队来说,数据库测试在升级过程中是至关重要的一步,根据生产过程中的查询数额来进行性能测试,将结果存到 pg_stat_statement 表中。这些都是在同一个数据集中运行的,一次是在 9.6 版本,一次是在 11 版本的迭代。这一步过程可以在下面这个公共的 issue 中找到:

  • 工具的准备
  • 创建测试环境
  • 计算容量
  • 使用 JMeter 工具运行基准测试

最后,根据 OnGres 在这一基准测试上的工作,GitLab 将在未来跟进新的基准测试。

  • 主要生产数据库集群的能力评估
  • 数据库容量及饱和度分析

升级过程:全自动就完事了

在升级项目中,升级团队坚持使用自动化和基础架构及代码工具(IaC)。所有流程必须全部自动化,以减少在维护窗口的人为失误。pg_upgrade 所有的运行步骤都可以在这个 GitLab 的 pg_upgrade 的模板 issue 上找到详细说明。

GitLab.com 的环境由 Terraform 和 Chef 共同管理,所有的升级自动化都是用 Ansible 2.9 的 playbook 和 roles 编写的,我们用了两个 ansible-playbook 来完成升级自动化:

一个 ansible-playbook 控制流量和应用:

  • 将 Cloudflare 设置为维护状态,不接受流量:
  • 停止 HA-proxy
  • 停止访问数据库的中间件:Sidekiq、Workhorse、WEB-API

另一个 ansible-playbook 运行升级过程:

  • 协调所有数据库和连接池的流量
  • 控制 Patroni 集群和 Consul 实例
  • 在主节点和次级节点上执行升级
  • 收集升级后的统计数据
  • 使用 Chef 同步更改,以保持配置管理的完整性
  • 验证集群的完整性和状态
  • 执行 GCP 快照
  • (可能的)回滚过程

playbook 以交互方式逐个运行所有任务,让程序员得以在任意给定执行点跳过或暂停程序。参与 staging 测试和迭代的所有团队成员都要过目升级过程中的所有步骤,staging 环境让我们通过演习提前找到升级过程中潜在的漏洞。而执行和迭代 staging 中自动化过程则让我们实现了 PostgreSQL 9.6 版本至 11 版本的基本无缺陷升级。

为完成本次的版本升级,GitLab 的 QA 团队将部分测试中发现的问题反馈给我们,这一部分的工作可以在这条 issue 中找到。

PostgreSQL 预升级的步骤

升级工作的第一步是“预升级”,这里涉及到预留给回滚的示例。我们做了相应分析,以确保新的集群可以不丢失吞吐量的情况下,以 8 个示例为起点,保留 4 个通过标准 Patroni 集群同步的 9.6 版本示例,为后续可能需要的回滚情况准备(共计 12 个实例)。

在这个阶段,我们还需要停止依赖 PostgreSQL 的服务,诸如 PgBouncer、Chef 客户端,以及 Patroni 服务。

在正式开始更新前,必须要告知 Patroni,避免任何虚假 leader 选举,通过 GCP 快照(通过对应低级备份API 获得)进行一致的备份,并通过运行Chef 应用新的设置。

PostgreSQL 升级阶段

首先,停止所有节点。

然后,运行以下检查:

  • pg_upgrade 版本检查
  • 验证所有节点都已同步,并且不再接受任何流量

一旦主节点数据升级完毕,就会触发 rsync 进程以同步所有副本数据。在升级完成后,启动 Patroni 服务,这样所有副本都能轻松更新至新集群的配置。

通过 Chef 安装二进制文件,新集群在版本方面的设置是在同一个 MR 中定义的,MR 源自 GitLab.com,可以安装用于数据库中的拓展项。

最后一个阶段则包括恢复流量、运行初始的真空期,以及最后的启动 PgBouncer 和 Chef 客户端服务。

迁移日

到了最后,我们为运行生产线上升级做好了万全准备,团队在周日一早 8:45 UTC 开始会议(对有的人来说是晚上)。服务将最多下线两小时,当最终的通知下达后,工程团队终于可以开始进行。

升级过程由停止所有流量及相关服务开始,这是为了避免用户在更新中途访问网站。

下面图表显示在服务更新之前,维护期间(图标中的空白部分)、以及维护结束、流量恢复后的流量和 HTTP 数据统计。

整个流程共花费四个小时,其中仅包括两小时断线时间。

此外,我们录下了PostgreSQL 更新的全过程并发布在 GitLab Unfiltered 上。

原文链接:

https://about.gitlab.com/blog/2020/09/11/gitlab-pg-upgrade/

关注我并转发此篇文章,私信我“领取资料”,即可免费获得InfoQ价值4999元迷你书,点击文末「了解更多」,即可移步InfoQ官网,获取最新资讯~

相关推荐

【互联网那些事】高效开发Android App的10个建议

  假如要GooglePlay上做一个最失败的案例,那最好的秘诀就是界面奇慢无比、耗电、耗内存。接下来就会得到用户的消极评论,最后名声也就臭了。即使你的应用设计精良、创意无限也没用。  耗电或者内存...

手机APP开发方式有哪些? 手机app的开发模式有哪三种?

微信小程序开发定制_软件开发_APP开发_网站制作-优软软件开发...

Android开发入门(一):Android系统简介

Android系统是Google公司在2008年推出的一款智能移动设备操作系统,通过不断地版本迭代,目前已经推出到Android11版本了。Android系统广泛应用在手机、平板、电视等各种电子设...

物联网app开发流程 物联网app开发工具

现在随着科技的发展,很多产品都想用一个手机app去显示他的参数数据或者通过手机app去控制它。但是很多人不知道他的流程。今天我就来说下物联网app开发流程。首先需要把物联网app开发流程分2个步骤,一...

Android开发进阶 | 如何学习 Android Framework?

大部分有“如何学习Framework源码”这个疑问的,应该大都是应用层开发。应用层是被Framework层调用执行的,知道自己的代码是怎么被调用的,才能理解程序的本质,理解本质有助于解决遇到的...

快速实现APP混合开发(Hybrid App开发)攻略

前言:...

三个阶段带你了解一款app开发的完整流程

第一个阶段需求阶段:1.需求讨论--开发类型、开发平台、具体的产品功能需求、项目预计完成时间、预算2.需求评估--确认合作后评估具体的预算3.界面设计--设计部门进行产品界面设计,形成效果图...

Android 开发中文引导-应用小部件

应用小部件是可以嵌入其它应用(例如主屏幕)并收到定期更新的微型应用视图。这些视图在用户界面中被叫做小部件,并可以用应用小部件提供者发布。可以容纳其他应用部件的应用组件叫做应用部件的宿主(1)。下面的截...

手机软件开发从零开始【Android第2篇Hello】

Hello,朋友们我们又见面了。上一篇我们讲到了《Android开发环境搭建【Android基础第1篇】》,错过的朋友可以点击文章末尾的“阅读原文”查看。另外需要下载JDK和ADT-bundle工具的...

「全栈工程师之梦的开始--安卓开发(二)」开发安卓app

在配置好jdk开发环境、安装好开发工具Androidstudio后,我们就可以开始开发安卓app了。首先,我们需要先了解下android的术语。...

二、Android界面开发 android 开发

学习目标了解Android常用布局了解Android常用控件...

如何开发一款APP既快捷也简便 开发一款app的步骤

具体较为简单的步骤可以选择用androidstudio开发app1、打开软件,在菜单中选择file-》newproject打开创建向导。2、配置项目,确定各个名称和存放项目的存放路径;Applic...

安卓开发中的“Android高手”,需要具备哪些技术?

前言成为一名安卓开发者很容易,但是要成为一名“Android高手”却不那么容易;...

移动开发(一):使用.NET MAUI开发第一个安卓APP

对于工作多年的C#程序员来说,近来想尝试开发一款安卓APP,考虑了很久最终选择使用.NETMAUI这个微软官方的框架来尝试体验开发安卓APP,毕竟是使用VisualStudio开发工具,使用起来也...

微软推出PowerApps:零基础开发Win10/iOS/安卓企业应用

IT之家讯微软今天面向企业宣布了全新的应用开发解决方案PowerApps,让Windows(包括Win10)、iOS以及安卓应用的开发和分发变得更加简单。PowerApps的用户界面与Office办...