我们如何在 Patroni 上构建可靠的 PostgreSQL 集群

我们如何在 Patroni 上构建可靠的 PostgreSQL 集群

如今,无论何时何地都需要服务的高可用性,而不仅仅是在大型昂贵的项目中。 仍然可以找到带有“抱歉,维护正在进行”消息的暂时不可用站点,但通常会引起居高临下的微笑。 让我们在云中添加这样的生活,当启动额外的服务器时,您只需要调用一次 API,而无需考虑“硬件”操作。 不再有任何借口可以解释为什么没有使用集群技术和冗余来可靠地构建关键系统。

我们将告诉您我们考虑了哪些解决方案来确保我们服务中数据库的可靠性以及我们的成果。 加上一个具有深远结论的演示。

高可用性架构的遗留问题

在各种开源系统的开发背景下,这一点更加明显。 随着需求的增加,传统解决方案被迫添加高可用性技术。 而且它们的质量参差不齐。 下一代解决方案将高可用性置于其架构的核心。 例如,MongoDB 将集群定位为其主要用例。 集群水平扩展,这是该DBMS的强大竞争优势。

让我们回到 PostgreSQL。 这是最古老的流行开源项目之一,其首次发布于上世纪 95 年。 长期以来,项目组并不认为高可用性是系统需要解决的问题。 因此,用于创建数据副本的复制技术仅在 8.2 年的 2006 版本中内置,但它是基于文件的(日志传送)。 2010年,9.0版本引入了流复制,它是创建各种集群的基础。 事实上,这对于那些在 Enterprise SQL 或现代 NoSQL 之后熟悉 PostgreSQL 的人来说是非常惊讶的 - 社区的标准解决方案只是具有同步或异步复制的主副本对。 同时,在库存中,master的切换是手动完成的,切换client的问题也建议独立解决。

我们如何决定打造可靠的 PostgreSQL 以及我们为此选择的内容

然而,如果没有大量的项目和工具帮助构建不需要持续关注的容错解决方案,PostgreSQL 就不会变得如此受欢迎。 在云端 Mail.ru 云解决方案 自 DBaaS 推出以来,(MCS) 单一 PostgreSQL 服务器和具有异步复制功能的主副本对已经可用。

当然,我们希望让每个人的生活变得更轻松,并提供一个 PostgreSQL 安装,它可以作为高可用服务的基础,而无需持续监控并在晚上醒来进行切换。 在这一领域,既有经过验证的旧解决方案,也有使用最新开发成果的新一代实用程序。

如今,高可用性的问题不再依赖于冗余(这是不言而喻的),而是依赖于共识——Leader 选举算法。 大多数情况下,重大事故的发生并不是因为缺少服务器,而是因为共识问题:没有选出新的领导者、两个领导者出现在不同的数据中心等。 示例 - Github MySQL 集群上的一次事故 - 他们写道 详细的事后分析.

这件事的数学基础是非常严肃的。 一方面,有 CAP定理,这对构建 HA 解决方案的可能性施加了理论上的限制,另一方面,经过数学证明的用于确定共识的算法,例如 的Paxos и 。 在此基础上,出现了相当流行的DCS(去中心化共识系统)——Zookeeper、etcd、Consul。 因此,如果决策系统运行在某种独立编写的算法上,您应该极其谨慎地对待它。 在分析了大量系统后,我们选择了Patroni,一个主要由Zalando开发的开源系统。

作为一个抒情的题外话,我想说的是,我们还考虑了多主解决方案,即可以水平扩展以进行记录的集群。 然而,出于两个主要原因,他们决定不创建这样的集群。 首先,此类解决方案复杂度高,因此漏洞也较多。 很难为所有情况制定一个稳定的解决方案。 其次,这种情况下PostgreSQL就不再是纯粹的(原生的)了,有些功能将无法使用,有些应用程序在运行过程中可能存在隐藏的bug。

帕特罗尼

那么帕特罗尼是如何工作的呢? 开发人员并没有重新发明轮子,而是建议使用一种经过验证的 DCS 解决方案作为基础。 配置同步、领导者选举和法定人数的所有问题都留给他。 为此我们选择了 etcd。

接下来,Patroni 处理 PostgreSQL 上所有设置和复制设置的正确应用,以及切换和故障转移命令的执行(即主服务器的定期和紧急切换)。 具体来说,在 MCS 云中,您可以创建一个由主节点、同步副本和一个或多个异步副本组成的集群。 同步副本的存在确保了至少 2 个服务器上数据的安全,并且该副本将成为主要的“主候选者”。

由于 etcd 部署在同一台服务器上,因此建议服务器数量为 3 或 5 台以获得最佳仲裁。 这样的集群可以水平扩展以进行读取(我在上面写了有关扩展写入的文章)。 但是,请注意,异步副本很容易出现滞后,尤其是在高负载下。

使用此类只读副本(热备用)适合报告或分析任务,并减轻主服务器上的负载。

如果您想自己制作这样的集群,您将需要:

  • 准备3台或以上服务器,配置它们之间的IP地址和防火墙规则;
  • 安装 etcd、Patroni、PostgreSQL 服务的软件包;
  • 配置etcd集群;
  • 配置 patoni 服务以与 PostgreSQL 一起使用。

也就是说,总共需要正确编写十几个配置文件,并且不能在任何地方出错。 为此,您绝对应该使用配置管理工具,例如 Ansible。 然而,仍然没有高可用的 TCP 平衡器。 制作它是一项单独的工作。

对于那些需要现成的集群,但又不想修改这一切的人,我们试图简化生活,并在我们的云中的 Patroni 上制作了一个现成的集群,您可以免费测试它。 除了集群本身之外,我们还做了:

  • TCP平衡器; 在不同的端口上,它始终分别指向当前的主副本、同步副本或异步副本;
  • 用于切换活动 Patroni 主控的 API。

可以通过 MCS 云 API 和 Web 控制台访问它们。

演示

为了测试 MCS 云中 PostgreSQL 集群的功能,让我们看看实时应用程序在 DBMS 出现问题时的行为方式。

下面是一个应用程序的代码,它将记录人工事件并将其报告到屏幕上。 如果出现错误,它将报告此情况并继续循环工作,直到我们使用 Ctrl + C 组合将其停止。

from __future__ import print_function

from datetime import datetime
from random import randint
from time import sleep
import psycopg2


def main():
    try:
        connection = psycopg2.connect(user = "admin",
                                      password = "P@ssw0rd",
                                      host = "89.208.87.38",
                                      port = "5432",
                                      database = "myproddb")

        cursor = connection.cursor()
        cursor.execute("SELECT version();")
        record = cursor.fetchone()
        print("Connection opened to", record[0])

        cursor.execute(
            "INSERT INTO log VALUES ({});".format(randint(1, 10000)))
        connection.commit()
        cursor.execute("SELECT COUNT(event_id) from log;")
        record = cursor.fetchone()
        print("Logged a value, overall count: {}".format(record[0]))
    except Exception as error:
        print ("Error while connecting to PostgreSQL", error)
    finally:
        if connection:
            cursor.close()
            connection.close()
            print("Connection closed")


if __name__ == '__main__':
    try:
        while True:
            try:
                print(datetime.now())
                main()
                sleep(3)
            except Exception as e:
                print("Caught error:n", e)
                sleep(1)
    except KeyboardInterrupt:
        print("exit")

该应用程序需要 PostgreSQL 才能工作。 让我们使用 API 在 MCS 云中创建一个集群。 在常规终端中,OS_TOKEN 变量包含用于访问 API 的令牌(可以使用 openstack token issues 命令获取),我们将键入以下命令:

创建集群:

cat <<EОF > pgc10.json
{"cluster":{"name":"postgres10","allow_remote_access":true,"datastore":{"type":"postgresql","version":"10"},"databases":[{"name":"myproddb"}],"users":[{"databases":[{"name":"myproddb"}],"name":"admin","password":"P@ssw0rd"}],"instances":[{"key_name":"shared","availability_zone":"DP1","flavorRef":"d659fa16-c7fb-42cf-8a5e-9bcbe80a7538","nics":[{"net-id":"b91eafed-12b1-4a46-b000-3984c7e01599"}],"volume":{"size":50,"type":"DP1"}},{"key_name":"shared","availability_zone":"DP1","flavorRef":"d659fa16-c7fb-42cf-8a5e-9bcbe80a7538","nics":[{"net-id":"b91eafed-12b1-4a46-b000-3984c7e01599"}],"volume":{"size":50,"type":"DP1"}},{"key_name":"shared","availability_zone":"DP1","flavorRef":"d659fa16-c7fb-42cf-8a5e-9bcbe80a7538","nics":[{"net-id":"b91eafed-12b1-4a46-b000-3984c7e01599"}],"volume":{"size":50,"type":"DP1"}}]}}
EOF

curl -s -H "X-Auth-Token: $OS_TOKEN" 
-H 'Accept: application/json' 
-H 'Content-Type: application/json' 
-d @pgc10.json https://infra.mail.ru:8779/v1.0/ce2a41bbd1434013b85bdf0ba07c770f/clusters

我们如何在 Patroni 上构建可靠的 PostgreSQL 集群

当集群切换到 ACTIVE 状态时,所有字段都将收到当前值——集群已准备就绪。

在图形用户界面中:

我们如何在 Patroni 上构建可靠的 PostgreSQL 集群

让我们尝试连接并创建一个表:

psql -h 89.208.87.38 -U admin -d myproddb
Password for user admin:
psql (11.1, server 10.7)
Type "help" for help.

myproddb=> CREATE TABLE log (event_id integer NOT NULL);
CREATE TABLE
myproddb=> INSERT INTO log VALUES (1),(2),(3);
INSERT 0 3
myproddb=> SELECT * FROM log;
 event_id
----------
        1
        2
        3
(3 rows)

myproddb=>

我们如何在 Patroni 上构建可靠的 PostgreSQL 集群

在应用程序中,我们将指示连接到 PostgreSQL 的当前设置。 我们将指定 TCP 平衡器的地址,从而无需手动切换到主地址。 让我们启动它吧。 如您所见,事件已成功记录到数据库中。

我们如何在 Patroni 上构建可靠的 PostgreSQL 集群

定时主备倒换

现在让我们在计划的主切换期间测试应用程序的运行情况:

我们如何在 Patroni 上构建可靠的 PostgreSQL 集群

我们正在监控该应用程序。 我们看到应用程序确实被中断了,但只需要几秒钟,在本例中最多为 9 秒钟。

我们如何在 Patroni 上构建可靠的 PostgreSQL 集群

车祸

现在让我们尝试模拟当前主虚拟机的崩溃。 您可以简单地通过 Horizo​​n 界面关闭虚拟机,但这将是常规关闭。 此类切换将由所有服务处理,包括 Patroni。

我们需要不可预测的关闭。 因此,我要求我们的管理员以非标准方式关闭虚拟机(当前的主机)以进行测试。

我们如何在 Patroni 上构建可靠的 PostgreSQL 集群

与此同时,我们的应用程序继续运行。 当然,主站的这种紧急切换不能被忽视。

2019-03-29 10:45:56.071234
Connection opened to PostgreSQL 10.7 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Logged a value, overall count: 453
Connection closed
2019-03-29 10:45:59.205463
Connection opened to PostgreSQL 10.7 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Logged a value, overall count: 454

Connection closed
2019-03-29 10:46:02.661440
Error while connecting to PostgreSQL server closed the connection unexpectedly
        This probably means the server terminated abnormally
        before or while processing the request.

Caught error:
 local variable 'connection' referenced before assignment
……………………………………………………….. - здесь какое-то количество ошибок
2019-03-29 10:46:30.930445
Error while connecting to PostgreSQL server closed the connection unexpectedly
        This probably means the server terminated abnormally
        before or while processing the request.

Caught error:
 local variable 'connection' referenced before assignment
2019-03-29 10:46:31.954399
Connection opened to PostgreSQL 10.7 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Logged a value, overall count: 455
Connection closed
2019-03-29 10:46:35.409800
Connection opened to PostgreSQL 10.7 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Logged a value, overall count: 456
Connection closed
^Cexit

正如您所看到的,应用程序能够在不到 30 秒的时间内继续工作。 是的,一定数量的服务用户将有时间注意到问题。 然而,这是严重的服务器故障;这种情况并不经常发生。 在这种情况下,一个人(管理员)几乎没有时间做出快速反应,除非他坐在控制台上准备好切换脚本​​。

结论

在我看来,这样的集群为管理员提供了巨大的优势。 事实上,应用程序以及用户不会注意到数据库服务器的严重故障和故障。 您不必急于修复任何内容并切换到临时配置、服务器等。 如果这样的解决方案用作云中的现成服务,那么您将不需要浪费时间准备它。 才有可能做一些更有趣的事情。

来源: habr.com

为具有 DDoS 保护、VPS VDS 服务器的站点购买可靠的主机 🔥 购买具备 DDoS 防护的可靠网站托管服务,包括 VPS 和 VDS 服务器 | ProHoster