Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

Patroni 的主要目标是为 PostgreSQL 提供高可用性。 但 Patroni 只是一个模板,而不是一个现成的工具(一般来说,在文档中是这样说的)。 乍一看,在测试实验室中设置了 Patroni,您可以看到它是一个多么棒的工具,以及它如何轻松地处理我们破坏集群的尝试。 然而,在实践中,在生产环境中,一切并不总是像在测试实验室中那样完美优雅地发生。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

我会告诉你一些关于我自己的事。 我最初是一名系统管理员。 从事网络开发工作。 自 2014 年以来,我一直在 Data Egret 工作。 该公司从事 Postgres 领域的咨询。 而我们正是为 Postgres 服务的,我们每天都与 Postgres 打交道,所以我们在操作方面有不同的专业知识。

而在2018年底,我们开始慢慢使用Patroni。 并且积累了一些经验。 我们以某种方式诊断它,调整它,得出我们的最佳实践。 在这份报告中,我将谈论它们。

除了 Postgres,我还喜欢 Linux。 我喜欢在里面闲逛和探索,我喜欢收集核心。 我喜欢虚拟化、容器、docker、Kubernetes。 所有这些都让我感兴趣,因为旧的管理习惯正在影响。 我喜欢处理监控。 我喜欢 postgres 与管理相关的东西,即复制、备份。 在业余时间,我用 Go 编写。 我不是软件工程师,我只是用 Go 为自己编写代码。 这让我很高兴。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

  • 我想你们中的许多人都知道 Postgres 没有开箱即用的 HA(高可用性)。 要获得 HA,您需要安装一些东西,配置它,努力并获得它。
  • 有几种工具,Patroni 是其中之一,它非常酷地很好地解决了 HA。 但是通过将它全部放在测试实验室并运行它,我们可以看到它一切正常,我们可以重现一些问题,看看 Patroni 如何为他们服务。 我们会看到一切都很好。
  • 但在实践中,我们遇到了不同的问题。 我将谈论这些问题。
  • 我会告诉你我们是如何诊断它的,我们做了什么调整——它是否对我们有帮助。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

  • 我不会告诉你如何安装 Patroni,因为你可以在互联网上 google,你可以查看配置文件来了解它是如何启动的,它是如何配置的。 您可以了解方案、体系结构,并在 Internet 上查找有关它的信息。
  • 别人的经历我就不说了。 我只会谈论我们面临的问题。
  • 我不会谈论 Patroni 和 PostgreSQL 之外的问题。 例如,如果有与平衡相关的问题,当我们的集群崩溃时,我不会谈论它。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

在我们开始报告之前,还有一个小的免责声明。

我们遇到的所有这些问题,都是在运营的前 6-7-8 个月内遇到的。 随着时间的推移,我们得出了内部最佳实践。 我们的问题消失了。 因此,这份报告大约是在六个月前发布的,当时我对它记忆犹新,并且记得很清楚。

在准备报告的过程中,我已经提出了旧的事后分析,查看了日志。 而且在分析问题的过程中,可能会遗忘一些细节,或者有些细节没能完全调查清楚,所以在某些时候看起来似乎没有充分考虑到问题,或者缺少一些信息。 所以我现在请你原谅我。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

什么是帕特罗尼?

  • 这是构建 HA 的模板。 这就是它在文档中所说的。 在我看来,这是一个非常正确的澄清。 Patroni 并不是解决所有问题的灵丹妙药,也就是说,您需要努力让它发挥作用并带来好处。
  • 这是安装在每个数据库服务上的代理服务,是 Postgres 的一种初始化系统。 它启动 Postgres、停止、重新启动、重新配置和更改集群的拓扑结构。
  • 因此,为了存储集群的状态,它的当前表示,就像它看起来的那样,需要某种存储。 而从这个角度来看,Patroni 走的是在外部系统中存储状态的路径。 它是一个分布式配置存储系统。 它可以是 Etcd、Consul、ZooKeeper 或 kubernetes Etcd,即这些选项之一。
  • Patroni 的其中一个特点是您可以开箱即用自动归档器,只需对其进行设置即可。 如果我们拿 Repmgr 来比较,那么 filer 就包含在那里。 使用 Repmgr,我们可以进行切换,但如果我们想要自动文件管理器,则需要额外配置它。 Patroni 已经有一个开箱即用的自动归档器。
  • 还有很多其他的东西。 比如维护配置,倒新副本,备份等等。不过这个不在报告范围内,我就不说了。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

一个小的结果是,Patroni 的主要任务是良好且可靠地执行自动归档,以便我们的集群保持运行并且应用程序不会注意到集群拓扑的变化。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

但是当我们开始使用 Patroni 时,我们的系统变得有点复杂。 如果早些时候我们有 Postgres,那么当使用 Patroni 时,我们会得到 Patroni 本身,我们会得到存储状态的 DCS。 这一切都必须以某种方式起作用。 那么会出什么问题呢?

可能会中断:

  • Postgres 可能会崩溃。 它可以是 master 也可以是 replica,其中一个可能会失败。
  • Patroni 本身可能会破裂。
  • 存储状态的 DCS 可能会中断。
  • 网络可能会中断。

我将在报告中考虑所有这些要点。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

我会考虑案例,因为它们变得更加复杂,而不是从案例涉及许多组件的角度来看。 而且从主观感受来看,这个case对我来说比较难,拆起来比较难……反之,有的case很轻,很容易拆。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

第一种情况是最简单的。 当我们采用数据库集群并将我们的 DCS 存储部署在同一个集群上时就是这种情况。 这是最常见的错误。 这是构建体系结构的错误,即将不同的组件组合在一个地方。

于是,有一个立案人,我们去处理一下事情的经过。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

在这里,我们对文件管理器发生的时间感兴趣。 也就是说,我们对集群状态发生变化的这个时刻感兴趣。

但是归档器并不总是瞬时的,即它不占用任何时间单位,它可以被延迟。 它可以持久。

因此,它有一个开始时间和一个结束时间,即它是一个连续的事件。 我们将所有事件分为三个时间段:我们有时间在申报者之前、申报者期间和申报者之后。 也就是说,我们考虑这个时间线中的所有事件。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

首先,当文件管理器发生时,我们会寻找发生的原因,导致文件管理器发生的原因。

如果我们查看日志,它们将是经典的 Patroni 日志。 他在他们里面告诉我们,服务器变成了master,master的角色已经传给了这个节点。 这里突出显示。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

接下来,我们需要了解 filer 发生的原因,即发生了什么事件导致 master 角色从一个节点移动到另一个节点。 在这种情况下,一切都很简单。 我们在与存储系统交互时出错。 大师意识到他无法与 DCS 一起工作,也就是说,交互存在某种问题。 他说他不能再当大师了,于是辞职了。 “贬身”这句台词正是这么说的。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

如果我们查看文件管理器之前的事件,我们可以在那里看到导致向导继续存在问题的原因。

如果我们查看 Patroni 日志,我们会发现有很多错误、超时,即 Patroni 代理无法与 DCS 一起工作。 在这种情况下,这是在端口 8500 上通信的 Consul 代理。

而这里的问题是 Patroni 和数据库在同一台主机上运行。 Consul 服务器在同一节点上启动。 通过在服务器上创建负载,我们也为 Consul 服务器创建了问题。 他们无法正常沟通。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

一段时间后,当负载消退时,我们的 Patroni 能够再次与代理进行通信。 恢复正常工作。 并且同一台 Pgdb-2 服务器再次成为主服务器。 也就是说,有一个小的翻转,由于这个节点放弃了主人的权力,然后又接管了他们,也就是说,一切都恢复原状。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

而这也算是虚惊一场,或者也算是帕特罗尼做对了。 也就是他意识到自己无法维护集群的状态而取消了他的权限。

由于 Consul 服务器与基地在同一硬件上,因此出现了问题。 相应地,任何负载:无论是磁盘负载还是处理器负载,都会影响与Consul集群的交互。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

而且我们决定它不应该住在一起,我们为 Consul 分配了一个单独的集群。 而且 Patroni 已经在使用一个单独的 Consul,也就是说,有一个单独的 Postgres 集群,一个单独的 Consul 集群。 这是关于如何携带和保存所有这些东西的基本说明,以免它们混在一起。

作为一个选项,您可以调整参数 ttl、loop_wait、retry_timeout,即尝试通过增加这些参数来承受这些短期负载峰值。 但这不是最合适的选择,因为这种加载时间可能会很长。 我们将简单地超越这些参数的这些限制。 这可能并没有什么帮助。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

如您所知,第一个问题很简单。 我们把 DCS 和基地放在一起,我们遇到了问题。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

第二个问题与第一个类似。 相似之处在于我们再次遇到与 DCS 系统的互操作性问题。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

如果我们查看日志,我们将再次看到通信错误。 Patroni 说我无法与 DCS 交互,因此当前的主服务器进入副本模式。

老主人变成了复制品,帕特罗尼在这里工作,这是应该的。 它运行 pg_rewind 来倒回事务日志,然后连接到新的 master 以赶上新的 master。 帕特罗尼在这里锻炼,他应该这样做。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

在这里,我们必须找到文件管理器之前的位置,即导致我们拥有文件管理器的那些错误。 在这方面,Patroni 日志使用起来非常方便。 他在一定的时间间隔内写相同的消息。 而如果我们开始快速滚动这些日志,那么我们会从日志中看到日志发生了变化,这意味着一些问题已经开始了。 我们赶紧回到这个地方,看看会发生什么。

在正常情况下,日志看起来像这样。 检查锁的所有者。 例如,如果所有者发生变化,则可能会发生 Patroni 必须响应的某些事件。 但在这种情况下,我们很好。 我们正在寻找错误开始的地方。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

滚动到错误开始出现的位置后,我们看到我们进行了自动归档。 由于我们的错误与与 DCS 的交互有关,并且在我们的案例中我们使用了 Consul,因此我们还查看了 Consul 日志,了解那里发生了什么。

粗略对比一下 filer 的时间和 Consul 日志中的时间,我们看到我们在 Consul 集群中的邻居开始怀疑 Consul 集群中其他成员的存在。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

如果您还查看其他 Consul 代理的日志,您还可以看到那里正在发生某种网络崩溃。 而Consul集群的所有成员都在怀疑对方的存在。 这是提交人的动力。

如果你看看这些错误之前发生了什么,你会发现有各种各样的错误,比如 deadline,RPC falled,也就是说 Consul 集群成员之间的交互显然存在某种问题.

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

最简单的答案是修复网络。 但对站在讲台上的我来说,说这话很容易。 但在这种情况下,客户并不总是能够负担得起维修网络的费用。 他可能住在一个DC,可能无法修复网络,影响设备。 因此需要一些其他选项。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

有以下选项:

  • 在我看来,即使在文档中,最简单的选项也是禁用 Consul 检查,即简单地传递一个空数组。 我们告诉领事代理不要使用任何支票。 通过这些检查,我们可以忽略这些网络风暴而不启动文件管理器。
  • 另一种选择是仔细检查 raft_multiplier。 这是 Consul 服务器本身的一个参数。 默认情况下,它设置为 5。暂存环境的文档建议使用此值。 事实上,这会影响 Consul 网络成员之间的消息传递频率。 实际上,这个参数影响的是Consul集群成员之间服务通信的速度。 对于生产,已经建议减少它以便节点更频繁地交换消息。
  • 我们提出的另一个选择是为操作系统的进程调度程序增加 Consul 进程在其他进程中的优先级。 有这样一个“nice”参数,它只是决定了操作系统调度程序在调度时考虑的进程的优先级。 我们还降低了 Consul 代理的 nice 值,即增加了优先级,以便操作系统为 Consul 进程提供更多时间来工作和执行它们的代码。 在我们的例子中,这解决了我们的问题。
  • 另一种选择是不使用 Consul。 我有一个朋友是 Etcd 的大力支持者。 我们经常和他争论 Etcd 和 Consul 哪个更好。 但就哪个更好而言,我们通常同意他的观点,即 Consul 有一个代理,应该在每个节点上运行一个数据库。 即Patroni与Consul集群的交互都是通过这个agent进行的。 而这个代理成为瓶颈。 如果代理出现问题,那么 Patroni 将无法再使用 Consul 集群。 这就是问题所在。 Etcd 计划中没有代理。 Patroni 可以直接与 Etcd 服务器列表一起工作,并且已经与它们通信。 在这方面,如果你在公司使用 Etcd,那么 Etcd 可能会是比 Consul 更好的选择。 但是我们的客户总是受到客户选择和使用的限制。 对于所有客户,我们大部分时间都有领事。
  • 最后一点是修改参数值。 我们可以将这些参数调高,希望我们的短期网络问题不会超出这些参数的范围。 这样,如果出现某些网络问题,我们可以减少 Patroni 自动归档的积极性。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

我想很多使用 Patroni 的人都熟悉这个命令。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

此命令显示集群的当前状态。 乍一看,这张照片似乎很正常。 我们有一个主人,我们有一个副本,没有复制滞后。 但是这张图是正常的,直到我们知道这个集群应该有三个节点,而不是两个。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

因此,有一个自动归档。 在这个自动归档之后,我们的副本消失了。 我们需要找出她失踪的原因并将她带回来,让她恢复原状。 我们再次查看日志,看看为什么我们有自动归档。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

在这种情况下,第二个副本成为主副本。 这里没问题。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

我们需要查看掉落的副本以及不在集群中的副本。 我们打开Patroni日志,发现在pg_rewind阶段连接集群的过程中出现了问题。 连接集群需要倒回事务日志,向master请求需要的事务日志,并用它来追赶master。

在这种情况下,我们没有事务日志,副本无法启动。 因此,我们以错误停止 Postgres。 因此它不在集群中。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

我们需要了解为什么它不在集群中以及为什么没有日志。 我们去找新主人,看看他在日志中有什么。 事实证明,当 pg_rewind 完成时,出现了一个检查点。 一些旧的事务日志只是简单地重命名了。 当旧 master 试图连接到新 master 并查询这些日志时,它们已经被重命名,它们只是不存在。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

我比较了这些事件发生时的时间戳。 那里的区别实际上是 150 毫秒,即检查点在 369 毫秒内完成,WAL 段已重命名。 从字面上看,在 517 年,150 毫秒后,旧副本开始倒带。 也就是说,从字面上看,150 毫秒对我们来说已经足够了,以至于副本无法连接和赚钱。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

有哪些选择?

我们最初使用复制槽。 我们认为这很好。 尽管在操作的第一阶段我们关闭了插槽。 在我们看来,如果插槽积累了很多 WAL 段,我们就可以丢弃 master。 他会倒下的。 我们在没有空位的情况下苦苦挣扎了一段时间。 我们意识到我们需要插槽,我们返回了插槽。

但是这里有一个问题,当master去到replica的时候,它会删除slots,并且把WAL段和slots一起删除。 为了消除这个问题,我们决定提高 wal_keep_segments 参数。 它默认为 8 段。 我们将其提高到 1,然后查看我们有多少可用空间。 我们为 wal_keep_segments 捐赠了 000 GB。 也就是说,在切换时,我们始终在所有节点上保留 16 GB 的事务日志。

而且 - 它仍然与长期维护任务相关。 假设我们需要更新其中一个副本。 我们想把它关掉。 我们需要更新软件,也许是操作系统,或者其他东西。 当我们关闭副本时,该副本的插槽也会被删除。 如果我们使用一个小的 wal_keep_segments,那么在长时间没有副本的情况下,事务日志将会丢失。 我们将创建一个副本,它将请求它停止的那些事务日志,但它们可能不在主服务器上。 副本也将无法连接。 因此,我们保留了大量杂志。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

我们有生产基地。 已经有项目在进行中。

有一个文件管理器。 我们进去看了看——一切都井井有条,副本就位,没有复制滞后。 日志中也没有错误,一切都井井有条。

产品团队说应该有一些数据,但我们从一个来源看到它,但我们没有在数据库中看到它。 我们需要了解发生在他们身上的事情。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

很明显 pg_rewind 错过了它们。 我们立即明白了这一点,但去看看发生了什么。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

在日志中,我们总能找到 filer 发生的时间,谁成为了 master,并且我们可以确定谁是旧的 master 以及他什么时候想成为副本,即我们需要这些日志来找出事务日志的数量丢失了。

我们的老主人已经重新启动。 Patroni 在 autorun 中注册。 推出帕特罗尼。 然后他启动了 Postgres。 更准确地说,在启动 Postgres 之前并使其成为副本之前,Patroni 启动了 pg_rewind 进程。 因此,他删除了部分交易日志,下载了新的交易日志并进行了连接。 帕特罗尼在这里巧妙地工作,正如预期的那样。 群集已恢复。 我们有 3 个节点,在文件管理器 3 个节点之后 - 一切都很好。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

我们丢失了一些数据。 我们需要了解我们失去了多少。 我们正在寻找倒带的那一刻。 我们可以在这样的日记条目中找到它。 倒带开始,在那里做了一些事情然后结束。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

我们需要在事务日志中找到老主人离开的位置。 在这种情况下,这就是标记。 而我们还需要第二个标记,也就是旧主人与新主人相差的距离。

我们采用通常的 pg_wal_lsn_diff 并比较这两个标记。 在这种情况下,我们得到 17 兆字节。 很多或一点点,每个人自己决定。 因为对于某些人来说 17 兆字节并不多,对于某些人来说却很多而且无法接受。 在这里,每个人都根据业务需要自行决定。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

但是我们自己发现了什么?

首先,我们必须自己决定——我们是否总是需要 Patroni 在系统重启后自动启动? 经常发生的情况是,我们必须去找老主人,看看他走了多远。 也许检查事务日志的各个部分,看看那里有什么。 并了解我们是否会丢失这些数据,或者我们是否需要以独立模式运行旧的 master 以提取这些数据。

只有在那之后,我们才必须决定我们是可以丢弃这些数据还是可以恢复它,将这个节点作为副本连接到我们的集群。

此外,还有一个“maximum_lag_on_failover”参数。 默认情况下,如果我没记错的话,此参数的值为 1 兆字节。

他是如何工作的? 如果我们的副本在复制延迟中落后 1 兆字节的数据,那么该副本不会参与选举。 如果突然发生文件转移,Patroni 会查看哪些副本滞后。 如果他们落后于大量的事务日志,他们就无法成为高手。 这是一项非常好的安全功能,可以防止您丢失大量数据。

但是存在一个问题,Patroni集群和DCS中的复制滞后是每隔一定时间更新一次。 我认为 30 秒是默认的 ttl 值。

因此,可能会出现一种情况,DCS中的replicas有一个replication lag,但实际上可能是完全不同的lag,也可能根本没有lag,即这个东西不是实时的。 它并不总是反映真实情况。 而且不值得在上面做花哨的逻辑。

损失的风险始终存在。 在最坏的情况下,一个公式,在一般情况下,另一个公式。 也就是我们在规划Patroni的实现,评估我们能丢失多少数据的时候,必须要依赖这些公式,粗略的想象一下我们能丢失多少数据。

有个好消息。 当老夫子先走了,由于有一些后台进程,他可以往前走。 也就是说,有某种 autovacuum,他写入数据,将它们保存到事务日志中。 而且我们很容易忽略并丢失这些数据。 这没有问题。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

这就是如果设置了 maximum_lag_on_failover 并且发生了文件管理器并且您需要选择一个新的主服务器时日志的样子。 副本评估自己无法参加选举。 她拒绝参加领导者的比赛。 然后她等待选择一个新的主人,这样她就可以连接到它。 这是防止数据丢失的附加措施。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

在这里,我们有一个产品团队写道,他们的产品与 Postgres 有问题。 同时,无法访问 master 本身,因为它无法通过 SSH 访问。 而且自动文件也不会发生。

此主机被迫重新启动。 由于重新启动,发生了自动归档,尽管可以进行手动自动归档,正如我现在所理解的那样。 重新启动后,我们已经可以看到当前 master 有什么了。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

同时,我们提前知道我们的磁盘有问题,也就是说,我们已经通过监控知道在哪里挖,要找什么。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

我们进入 postgres 日志,开始查看那里发生了什么。 我们看到提交持续一、两、三秒,这根本不正常。 我们看到我们的 autovacuum 启动非常缓慢且奇怪。 我们在磁盘上看到了临时文件。 也就是说,这些都是磁盘出现问题的指标。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

我们查看了系统 dmesg(内核日志)。 我们看到其中一个磁盘有问题。 磁盘子系统是软件 Raid。 我们查看了 /proc/mdstat,发现缺少一个驱动器。 也就是有8块盘的Raid,我们少了一块。 如果您仔细查看幻灯片,那么在输出中您可以看到我们那里没有 sde。 在我们这里,有条件的说,磁盘掉了。 这引发了磁盘问题,并且应用程序在使用 Postgres 集群时也遇到了问题。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

在这种情况下,Patroni 不会以任何方式帮助我们,因为 Patroni 没有监控服务器状态、磁盘状态的任务。 我们必须通过外部监控来监控这种情况。 我们很快将磁盘监控添加到外部监控中。

有这样一个想法——围栏或看门狗软件可以帮助我们吗? 我们认为他在这种情况下几乎不会帮助我们,因为在出​​现问题期间,Patroni 继续与 DCS 集群交互并且没有发现任何问题。 也就是说,从 DCS 和 Patroni 的角度来看,集群一切正常,尽管实际上磁盘有问题,数据库的可用性也有问题。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

在我看来,这是我研究了很长时间的最奇怪的问题之一,我阅读了很多日志,重新挑选并称之为集群模拟器。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

问题是旧的 master 不能成为一个普通的副本,即 Patroni 启动它,Patroni 显示这个节点作为一个副本存在,但同时它不是一个普通的副本。 现在你会明白为什么了。 这是我在分析该问题时保留下来的内容。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

这一切是如何开始的? 与上一个问题一样,它开始于盘式制动器。 我们提交了一秒钟,两次。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

连接中断,即客户被撕毁。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

有不同程度的阻塞。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

并且,相应地,磁盘子系统不是很灵敏。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

对我来说最神秘的是收到的立即关闭请求。 Postgres 具有三种关闭模式:

  • 当我们等待所有客户端自行断开连接时,这是优雅的。
  • 当我们因为要关闭而强制客户端断开连接时,速度很快。
  • 并且立即。 在这种情况下,immediate 甚至不告诉客户端关闭,它只是在没有警告的情况下关闭。 并且对于所有客户端,操作系统已经发送了一个 RST 消息(一个 TCP 消息,表明连接被中断,客户端没有什么可以捕获的了)。

这个信号是谁发出的? Postgres 后台进程不会相互发送此类信号,即 kill-9。 他们不会互相发送这样的东西,他们只会对这样的事情做出反应,即这是 Postgres 的紧急重启。 谁送的,不知道。

我看了看“最后”命令,我看到一个人也和我们一起登录了这个服务器,但我太害羞了,不敢问问题。 也许是 kill -9。 我会在日志中看到 kill -9,因为Postgres 说它需要 kill -9,但我没有在日志中看到它。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

进一步看,我看到 Patroni 很长时间没有写入日志 - 54 秒。 如果我们比较两个时间戳,大约有 54 秒没有消息。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

在此期间有一个自动文件。 帕特罗尼再次在这里做得很好。 我们的老主人不在,他出事了。 新主人的选举开始了。 这里的一切都很顺利。 我们的 pgsql01 已成为新的领导者。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

我们有一个已经成为大师的副本。 还有第二个回应。 第二个副本有问题。 她试图重新配置。 据我了解,她试图更改 recovery.conf,重新启动 Postgres 并连接到新的主服务器。 她尝试每 10 秒写一次消息,但没有成功。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

在这些尝试过程中,一个立即关闭信号到达了旧主人。 主人重新启动。 恢复也停止了,因为旧的主人进入重启。 也就是说,副本无法连接到它,因为它处于关闭模式。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

在某些时候,它起作用了,但复制没有开始。

我唯一的猜测是 recovery.conf 中有一个旧的主地址。 而当一个新的master出现时,第二个replica仍然试图连接到旧的master。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

当 Patroni 在第二个副本上启动时,节点启动但无法复制。 并且形成了复制滞后,看起来像这样。 也就是说,三个节点都到位了,但是第二个节点落后了。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

同时,如果查看写入的日志,您会发现复制无法启动,因为事务日志不同。 master 提供的那些在 recovery.conf 中指定的事务日志根本不适合我们当前的节点。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

在这里我犯了一个错误。 我不得不来看看 recovery.conf 中的内容,以检验我们连接到错误主机的假设。 但是后来我只是在处理这个,我没有想到,或者我看到副本落后了,必须重新填充,也就是说,我不知何故粗心大意。 这是我的关节。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

30 分钟后,管理员已经来了,即我在副本上重新启动了 Patroni。 我已经结束了它,我认为它必须重新填充。 我想 - 我会重新启动 Patroni,也许会有好结果。 恢复开始了。 甚至基地也开放了,它已经准备好接受连接了。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

复制已开始。 但是一分钟后,她就报错说事务日志不适合她。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

我以为我会重新开始。 我又重启了Patroni,并没有重启Postgres,而是重启了Patroni,希望它能神奇的启动数据库。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

复制再次开始,但事务日志中的标记不同,它们与之前的启动尝试不同。 复制再次停止。 消息已经略有不同。 这对我来说不是很有用。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

然后它发生在我身上 - 如果我重新启动 Postgres 怎么办,此时我在当前主服务器上创建一个检查点以将事务日志中的点向前移动一点以便恢复从另一个时刻开始? 另外,我们还有 WAL 库存。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

我重新启动了 Patroni,在主服务器上做了几个检查点,在副本打开时做了几个重启点。 它有所帮助。 我想了很长时间为什么它有帮助以及它是如何工作的。 复制品开始了。 复制不再被撕裂。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

这样的问题对我来说是比较神秘的问题之一,我仍然对那里到底发生了什么感到困惑。

这意味着什么? Patroni 可以按预期工作,没有任何错误。 但与此同时,这并不能 100% 保证我们一切都好。 副本可能启动,但可能处于半工作状态,应用程序无法使用这样的副本,因为会有旧数据。

在文件管理器之后,您始终需要检查集群是否一切正常,即是否存在所需数量的副本,没有复制滞后。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

当我们解决这些问题时,我会提出建议。 我试图将它们组合成两张幻灯片。 也许,所有的故事都可以合并成两张幻灯片,只讲。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

当你使用 Patroni 时,你必须有监控。 您应该始终知道自动文件转移何时发生,因为如果您不知道自己进行了自动文件转移,就无法控制集群。 那很糟糕。

在每个文件管理器之后,我们总是必须手动检查集群。 我们需要确保我们始终拥有最新数量的副本,没有复制滞后,与流复制相关的日志中没有错误,使用 Patroni,使用 DCS 系统。

自动化可以成功工作,Patroni 是一个非常好的工具。 它可以工作,但这不会使集群达到所需的状态。 如果我们不发现它,我们就会有麻烦。

而且 Patroni 不是灵丹妙药。 我们仍然需要了解 Postgres 是如何工作的,复制是如何工作的以及 Patroni 如何与 Postgres 一起工作,以及如何提供节点之间的通信。 这是必要的,以便能够用您的双手解决问题。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

我如何处理诊断问题? 恰好我们和不同的客户端打交道,没有一个有ELK栈,我们不得不通过打开6个控制台和2个选项卡来整理日志。 在一个选项卡中,这些是每个节点的 Patroni 日志,在另一个选项卡中,这些是 Consul 日志,或 Postgres(如果需要)。 这很难诊断。

我采取了哪些方法? 首先,我总是查看归档器何时到达。 对我来说,这是一个分水岭。 我查看了申报人之前、申报人期间和申报人之后发生的事情。 归档有两个标记:这是开始时间和结束时间。

接下来,我在文件管理器之前的日志中查找事件,即文件管理器之前发生的事件,即我寻找文件管理器发生的原因。

这提供了一幅了解发生了什么以及将来可以做什么的图片,以便不会发生这种情况(因此,没有文件管理器)。

我们通常在哪里看? 我看:

  • 首先,到 Patroni 日志。
  • 接下来,我查看 Postgres 日志或 DCS 日志,具体取决于在 Patroni 日志中找到的内容。
  • 并且系统日志有时还可以了解导致文件管理器的原因。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

我对帕特罗尼感觉如何? 我与帕特罗尼的关系非常好。 在我看来,这是当今最好的。 我知道很多其他产品。 它们是 Stolon、Repmgr、Pg_auto_failover、PAF。 4个工具。 我都试过了。 帕特罗尼是我的最爱。

如果他们问我:“我推荐 Patroni 吗?”。 我会说是的,因为我喜欢Patroni。 我想我学会了如何做饭。

如果您有兴趣了解除了我提到的问题之外 Patroni 还有哪些其他问题,您可以随时查看页面 问题 在 GitHub 上。 那里有许多不同的故事,并讨论了许多有趣的问题。 结果,引入并解决了一些错误,也就是说,这是一本有趣的书。

有一些关于人们搬起石头砸自己脚的有趣故事。 非常翔实。 您阅读并理解没有必要这样做。 我勾选了自己。

我想非常感谢 Zalando 开发这个项目,也就是 Alexander Kukushkin 和 Alexey Klyukin。 Aleksey Klyukin 是合著者之一,他不再在 Zalando 工作,但这两个人开始使用该产品。

而且我认为 Patroni 是一件非常酷的事情。 我很高兴她存在,这对她很有趣。 非常感谢所有为 Patroni 编写补丁的贡献者。 我希望帕特罗尼随着年龄的增长变得更加成熟、冷静和高效。 它已经可以使用了,但我希望它会变得更好。 因此,如果您打算使用 Patroni,请不要害怕。 这是一个很好的解决方案,可以实施和使用。

就这样。 如有疑问,请提问。

Patroni 失败案例或如何使您的 PostgreSQL 集群崩溃。 阿列克谢·列索夫斯基

问题

感谢您的报告! 如果在一个文件管理器之后你仍然需要非常仔细地查看那里,那么为什么我们需要一个自动文件管理器?

因为是新东西。 我们只和她在一起一年。 最好是安全的。 我们想进来看看一切都按照它应该的方式进行。 这是成人不信任的程度——最好仔细检查看看。

比如我们早上去看看,对不对?

不是在早上,我们通常几乎立即就知道了自动归档。 我们收到通知,我们看到发生了自动归档。 我们几乎立刻就去看了。 但是所有这些检查都应该带到监控级别。 如果您通过 REST API 访问 Patroni,则有一段历史记录。 通过历史记录,您可以看到文件管理器发生时的时间戳。 基于此,可以进行监控。 你可以看到历史,那里有多少事件。 如果我们有更多事件,则会发生自动归档。 你可以去看看。 或者我们的监控自动化检查所有副本是否就位,没有滞后,一切都很好。

谢谢大家!

非常感谢精彩的故事! 如果我们将 DCS 集群移动到远离 Postgres 集群的某个地方,那么这个集群也需要定期维护吗? 需要关闭 DCS 集群的某些部分的最佳实践是什么,要对它们做些什么等等? 整个结构如何生存? 你如何做这些事情?

对于一家公司来说,有必要制作一个问题矩阵,如果一个组件或多个组件出现故障会发生什么。 根据这个矩阵,我们依次遍历所有组件并构建场景以防这些组件发生故障。 因此,对于每种故障情况,您都可以制定恢复行动计划。 就 DCS 而言,它是标准基础设施的一部分。 管理员管理它,我们已经依赖管理它的管理员以及他们在发生崩溃时修复它的能力。 如果根本没有DCS,那么我们部署它,但同时我们不会特别监控它,因为我们不对基础设施负责,但我们会就如何监控以及监控什么给出建议。

也就是说,我是否正确理解我需要在对主机执行任何操作之前禁用 Patroni、禁用文件管理器、禁用所有内容?

这取决于我们在 DCS 集群中有多少个节点。 如果有很多节点,并且如果我们只禁用一个节点(副本),那么集群会维持一个法定人数。 Patroni 仍在运营。 没有任何东西被触发。 如果我们有一些影响更多节点的复杂操作,缺少这些操作可能会破坏法定人数,那么 - 是的,暂停 Patroni 可能是有意义的。 它有相应的命令——patronictl pause、patronictl resume。 我们只是暂停,autofiler 那时不工作。 我们在DCS集群上做维护,然后我们解除暂停,继续生活。

非常感谢!

非常感谢您的报告! 产品团队如何看待数据丢失?

产品团队不在乎,团队领导担心。

有什么保证?

保证非常困难。 Alexander Kukushkin 有一份报告“如何计算 RPO 和 RTO”,即恢复时间和我们可以丢失多少数据。 我认为我们需要找到这些幻灯片并研究它们。 据我记得,有关于如何计算这些东西的具体步骤。 我们可以丢失多少事务,我们可以丢失多少数据。 作为一种选择,我们可以在 Patroni 级别使用同步复制,但这是一把双刃剑:我们要么拥有数据可靠性,要么失去速度。 有同步复制,但也不能保证 100% 防止数据丢失。

阿列克谢,感谢您的精彩报告! 有使用 Patroni 进行零级保护的经验吗? 也就是配合同步备用? 这是第一个问题。 第二个问题。 您使用了不同的解决方案。 我们使用了 Repmgr,但没有自动文件管理器,现在我们计划包括自动文件管理器。 我们将 Patroni 视为替代解决方案。 与 Repmgr 相比,您有什么优势?

第一个问题是关于同步副本的。 这里没有人用同步复制,因为大家都很害怕(几个客户端已经在用了,原则上没注意到性能问题—— 讲者注). 但是我们给自己制定了一个规则,一个同步复制集群至少要有三个节点,因为如果我们有两个节点,如果master或者replica出现故障,那么Patroni会把这个节点切换到Standalone模式,让应用继续运行工作。 在这种情况下,存在数据丢失的风险。

关于第二个问题,我们用过Repmgr,由于历史原因,现在还在用一些客户端。 可以说什么? Patroni 附带了一个开箱即用的自动文件管理器,Repmgr 附带了自动文件管理器作为需要启用的附加功能。 我们需要在每个节点上运行 Repmgr 守护进程,然后我们可以配置自动文件管理器。

Repmgr 检查 Postgres 节点是否存活。 Repmgr 进程检查彼此的存在,这不是一种非常有效的方法。 可能存在复杂的网络隔离情况,其中大型 Repmgr 集群可能会分裂成几个较小的集群并继续工作。 我已经很长时间没有关注 Repmgr 了,也许它已经修复了……也可能没有。 但是像 Stolon、Patroni 那样,在 DCS 中移除关于集群状态的信息是最可行的选择。

阿列克谢,我有一个问题,也许是一个更简单的问题。 在第一个示例中,您将 DCS 从本地计算机移动到远程主机。 我们明白,网络是一个有自己特点的东西,它是独立存在的。 如果由于某种原因 DCS 集群变得不可用,会发生什么情况? 原因我就不说了,可以有很多:从网络人的歪手到真正的问题。

我没有大声说出来,但 DCS 集群也必须进行故障转移,即节点数量为奇数,才能满足法定人数。 如果 DCS 集群变得不可用,或者无法满足法定人数,即某种网络分裂或节点故障,会发生什么情况? 在这种情况下,Patroni 集群进入只读模式。 Patroni 集群无法确定集群的状态以及要做什么。 它无法联系 DCS 并在那里存储新的集群状态,因此整个集群进入只读状态。 并等待操作员的手动干预或 DCS 恢复。

粗略地说,DCS对我们来说成为一种与基地本身一样重要的服务?

是的是的。 在如此多的现代公司中,服务发现是基础设施不可或缺的一部分。 它甚至在基础设施中甚至没有数据库之前就已经实施了。 相对来说,基础设施上线,部署在数据中心,我们马上就有了Service Discovery。 如果是Consul,那么DNS就可以建在上面。 如果这是 Etcd,那么可能会有来自 Kubernetes 集群的一部分,其中将部署其他所有内容。 在我看来,服务发现已经是现代基础设施不可或缺的一部分。 他们比数据库更早地考虑它。

谢谢大家!

来源: habr.com

添加评论