在 RHEL 或 CentOS 上使用 Patroni 部署 PostgreSQL 以实现高可用性

wonchaofan / 2024-08-09 / 原文

本指南提供了有关如何在 Red Hat Enterprise Linux 或 CentOS 上使用 Patroni 设置高可用性 PostgreSQL 集群的说明。

注意事项

  1. 这是一个示例部署,其中 etcd 与 Patroni 和 PostgreSQL 在同一台主机上运行,​​并且有一个专用的 HAProxy 主机。或者,etcd 可以在不同的节点集上运行。

    如果 etcd 与 Patroni 和 PostgreSQL 部署在同一台主机上,出于性能原因,建议为 etcd 和 PostgreSQL 使用单独的磁盘系统。

对于此设置,我们使用运行 Red Hat Enterprise Linux 8 的节点作为基础操作系统:

节点名称应用IP地址
节点1 Patroni、PostgreSQL、etcd 10.104.0.1
节点2 Patroni、PostgreSQL、etcd 10.104.0.2
节点3 Patroni、PostgreSQL、etcd 10.104.0.3
HAProxy 演示 HAProxy 10.104.0.6
由于存在安全风险,我们建议不要将运行 Patroni / etcd / PostgreSQL 的主机/节点暴露到公共网络。使用防火墙、虚拟网络、子网等保护数据库主机免受任何类型的攻击。

初始设置

/etc/hosts在文件中设置主机名

名称解析不是必需的,但它使整个设置更易读且更不容易出错。在这里,我们不是配置 DNS,而是通过更新文件来使用本地名称解析/etc/hosts。通过将其主机名解析为其 IP 地址,我们使节点知道彼此的名称并允许它们无缝通信。

  • 在每个节点上运行以下命令。将节点名称分别更改为node1node2node3

sudo hostnamectl set-hostname node-1
  • 修改每个 PostgreSQL 节点的文件,使其包含其余节点的主机名和 IP 地址。在所有节点上的文件/etc/hosts末尾添加以下内容:/etc/hosts

节点

# Cluster IP and names 
10.104.0.1 node1 
10.104.0.2 node2 
10.104.0.3 node3

HAproxy 演示

HAProxy 实例应在其文件中具有所有三个节点的名称解析/etc/hosts。在文件末尾添加以下几行:

# Cluster IP and names
10.104.0.6 HAProxy-demo
10.104.0.1 node1
10.104.0.2 node2
10.104.0.3 node3

安装软件

在 上安装 Percona Distribution for PostgreSQL node1node2node3从 Percona 存储库安装:

安装一些 Python 和辅助包来帮助 Patroni 和 etcd

sudo yum install python3-pip python3-devel binutils

安装 etcd、Patroni、pgBackRest 包。

sudo yum install percona-patroni \
etcd python3-python-etcd\
percona-pgbackrest

停止并禁用所有已安装的服务:

sudo systemctl stop {etcd,patroni,postgresql}
systemctl disable {etcd,patroni,postgresql}

配置 etcd 分布式存储

分布式配置存储提供了一种可靠的方法来存储需要由大规模分布式系统访问的数据。最流行的分布式配置存储实现是 etcd。etcd 部署为集群以实现容错,并且需要奇数个成员 (n/2+1) 才能就集群状态的更新达成一致。etcd 集群有助于在故障转移期间在节点之间建立共识,并管理三个 PostgreSQL 实例的配置。

集群etcd首先在一个节点上启动,然后使用命令将后续节点添加到第一个节点上add

配置node1

  • 创建配置文件。您可以编辑示例配置文件/etc/etcd/etcd.conf.yaml或创建自己的配置文件。将节点名称和 IP 地址替换为您节点的实际名称和 IP 地址。
/etc/etcd/etcd.conf.yaml
name: 'node1' #成员的可读性的名字. initial-cluster-token: PostgreSQL_HA_Cluster_1 #在启动期间用于 etcd 集群的初始化集群记号(cluster token)。 initial-cluster-state: new #初始化集群状态("new" or "existing")。在初始化静态(initial static)或者 DNS 启动 (DNS bootstrapping) 期间为所有成员设置为 new 。如果这个选项被设置为 existing , etcd 将试图加入已有的集群。如果设置为错误的值,etcd 将尝试启动但安全失败。 initial-cluster: node1=http://10.104.0.1:2380 #为启动初始化集群配置。 data-dir: /var/lib/etcd #数据目录的路径 initial-advertise-peer-urls: http://10.104.0.1:2380 #列出这个成员的伙伴 URL 以便通告给集群的其他成员。默认:"http://localhost:2380" listen-peer-urls: http://10.104.0.1:2380 #用于监听伙伴通讯的URL列表。 advertise-client-urls: http://10.104.0.1:2379 #列出这个成员的客户端URL,默认: "http://localhost:2379" listen-client-urls: http://10.104.0.1:2379 #用于监听客户端通讯的URL列表。

启动etcd服务以应用更改node1

sudo systemctl enable --now etcd
sudo systemctl status etcd

检查 etcd 集群成员node1

sudo etcdctl member lis

示例输出:

21d50d7f768f153a: name=default peerURLs=http://10.104.0.5:2380 clientURLs=http://10. 104.0.5:2379 isLeader=true

添加node2到集群。在 node1上运行以下命令

$ sudo etcdctl member add node2 http://10.104.0.2:2380
Added member named node2 with ID 10042578c504d052 to cluster

etcd_NAME="node2"
etcd_INITIAL_CLUSTER="node2=http://10.104.0.2:2380,node1=http://10.104.0.1:2380"
etcd_INITIAL_CLUSTER_STATE="existing"

配置node2

创建配置文件。您可以编辑示例配置文件/etc/etcd/etcd.conf.yaml或创建自己的配置文件。将节点名称和 IP 地址替换为您节点的实际名称和 IP 地址。

/etc/etcd/etcd.conf.yaml

name: 'node2'
initial-cluster-token: PostgreSQL_HA_Cluster_1
initial-cluster-state: existing
initial-cluster: node1=http://10.104.0.1:2380,node2=http://10.104.0.2:2380
data-dir: /var/lib/etcd
initial-advertise-peer-urls: http://10.104.0.2:2380 
listen-peer-urls: http://10.104.0.2:2380
advertise-client-urls: http://10.104.0.2:2379
listen-client-urls: http://10.104.0.2:2379

启动etcd服务以应用更改node2

sudo systemctl enable --now etcd
sudo systemctl status etcd

配置node3

添加node3到集群。node1上运行以下命令

sudo etcdctl member add node3 http://10.104.0.3:2380

在 node3上,创建配置文件。您可以编辑示例配置文件/etc/etcd/etcd.conf.yaml或创建自己的配置文件。将节点名称和 IP 地址替换为您的节点的实际名称和 IP 地址:

/etc/etcd/etcd.conf.yaml

name: 'node1'
initial-cluster-token: PostgreSQL_HA_Cluster_1
initial-cluster-state: existing
initial-cluster: node1=http://10.104.0.1:2380,node2=http://10.104.0.2:2380,node3=http://10.104.0.3:2380
data-dir: /var/lib/etcd
initial-advertise-peer-urls: http://10.104.0.3:2380 
listen-peer-urls: http://10.104.0.3:2380
advertise-client-urls: http://10.104.0.3:2379
listen-client-urls: http://10.104.0.3:2379

启动etcd服务以应用更改。

sudo systemctl enable --now etcd
sudo systemctl status etcd

检查 etcd 集群成员。

sudo etcdctl member list
2d346bd3ae7f07c4: name=node2 peerURLs=http://10.104.0.2:2380 clientURLs=http://10.104.0.2:2379     isLeader=false
8bacb519ebdee8db: name=node3 peerURLs=http://10.104.0.3:2380 clientURLs=http://10.104.0.3:2379     isLeader=false
c5f52ea2ade25e1b: name=node1 peerURLs=http://10.104.0.1:2380 clientURLs=http://10.104.0.1:2379     isLeader=true

配置 Patroni

在所有节点上运行以下命令。您可以并行执行此操作:

  1. 导出并创建环境变量以简化配置文件的创建:

    • 节点名称:
export NODE_NAME=`hostname -f` #
  • 节点 IP:
export NODE_IP=`hostname -i | awk '{print $1}'`
  • 创建变量来存储 PATH:
DATA_DIR="/var/lib/pgsql/data/"
PG_BIN_DIR="/usr/pgsql-15/bin"

注意:检查操作系统上数据和 bin 文件夹的路径,并相应地更改变量。

  • 赞助人信息:
NAMESPACE="percona_lab"
SCOPE="cluster_1

创建 Patroni 所需的目录

  • 创建用于存储配置文件的目录并使其归用户所有postgres
sudo mkdir -p /etc/patroni/
sudo chown -R  postgres:postgres /etc/patroni/
  • 创建数据目录来存储 PostgreSQL 数据。将其所有权更改为用户postgres并限制对它的访问
sudo mkdir /data/pgsql -p
sudo chown -R postgres:postgres /data/pgsql
sudo chmod 700 /data/pgsql

创建/etc/patroni/patroni.yml配置文件。添加以下配置:

echo "
namespace: ${NAMESPACE}
scope: ${SCOPE}
name: ${NODE_NAME}

restapi:
    listen: 0.0.0.0:8008
    connect_address: ${NODE_IP}:8008

etcd3:
    host: ${NODE_IP}:2379

bootstrap:
  # this section will be written into Etcd:/<namespace>/<scope>/config after initializing new cluster
  dcs:
      ttl: 30
      loop_wait: 10
      retry_timeout: 10
      maximum_lag_on_failover: 1048576
      slots:
          percona_cluster_1:
            type: physical

      postgresql:
          use_pg_rewind: true
          use_slots: true
          parameters:
              wal_level: replica
              hot_standby: "on"
              wal_keep_segments: 10
              max_wal_senders: 5
              max_replication_slots: 10
              wal_log_hints: "on"
              logging_collector: 'on'

  # some desired options for 'initdb'
  initdb: # Note: It needs to be a list (some options need values, others are switches)
      - encoding: UTF8
      - data-checksums

  pg_hba: # Add following lines to pg_hba.conf after running 'initdb' #初始化集群后配置设置
      - host replication replicator 127.0.0.1/32 trust
      - host replication replicator 0.0.0.0/0 md5
      - host all all 0.0.0.0/0 md5
      - host all all ::0/0 md5

  # Some additional users which needs to be created after initializing new cluster,#数据库需要创建的用户
  users:
      admin:
          password: qaz123
          options:
              - createrole
              - createdb
      percona:
          password: qaz123
          options:
              - createrole
              - createdb 

postgresql:
    cluster_name: cluster_1
    listen: 0.0.0.0:5432
    connect_address: ${NODE_IP}:5432
    data_dir: ${DATA_DIR}
    bin_dir: ${PG_BIN_DIR}
    pgpass: /tmp/pgpass
    authentication:
        replication:
            username: replicator
            password: replPasswd
        superuser:
            username: postgres
            password: qaz123
    parameters:
        unix_socket_directories: "/var/run/postgresql/"
    create_replica_methods:
        - basebackup
    basebackup:
        checkpoint: 'fast'

tags:
    nofailover: false
    noloadbalance: false
    clonefrom: false
    nosync: false
" | sudo tee -a /etc/patroni/patroni.yml

检查 systemd 单元文件patroni.service是否已在 /etc/systemd/system中创建。如果已创建,请跳过此步骤。

若未创建,请手动创建,并在其中指定以下内容:

/etc/systemd/system/patroni.service

[Unit]
Description=Runners to orchestrate a high-availability PostgreSQL
After=syslog.target network.target 

[Service]
Type=simple 

User=postgres
Group=postgres 

# Start the patroni process
ExecStart=/bin/patroni /etc/patroni/patroni.yml 

# Send HUP to reload from patroni.yml
ExecReload=/bin/kill -s HUP $MAINPID 

# only kill the patroni process, not its children, so it will gracefully stop postgres
KillMode=process 

# Give a reasonable amount of time for the server to start up/shut down
TimeoutSec=30 

# Do not restart the service if it crashes, we want to manually inspect database on failure
Restart=no 

[Install]
WantedBy=multi-user.target

了解systemd新服务:

sudo systemctl daemon-reload

现在是时候启动 Patroni 了。您需要在所有节点上(但不是并行)执行以下命令。从node1第一个节点开始,等待服务上线,然后逐个处理其他节点,始终等待它们与主节点同步:

sudo systemctl enable --now patroni
sudo systemctl restart patroni

当 Patroni 启动时,它会按照配置文件引导部分中的指令初始化 PostgreSQL(因为该服务当前未运行且数据目录为空)。

  1. 检查服务是否有错误:

sudo journalctl -fu patroni

一个常见错误是 Patroni 抱怨 pg_hba.conf 文件中缺少正确的条目。如果您看到此类错误,则必须手动添加或修复该文件中的条目,然后重新启动服务。

更改 patroni.yml 文件并重新启动服务不会产生任何影响,因为 bootstrap 部分指定了在节点中首次启动 PostgreSQL 时要应用的配置。即使修改了 Patroni 配置文件并重新启动服务,它也不会重复该过程。

如果 Patroni 已正确启动,您应该能够使用以下命令本地连接到 PostgreSQL 节点:

sudo psql -U postgres

psql (15.4)
Type "help" for help.

postgres=#

当所有节点都启动并运行时,您可以使用以下命令检查集群状态:

sudo patronictl -c /etc/patroni/patroni.yml list

输出node1类似以下内容:

+ Cluster: cluster_1 --+---------+---------+----+-----------+
| Member | Host        | Role    | State   | TL | Lag in MB |
+--------+-------------+---------+---------+----+-----------+
| node-1 | 10.0.100.1  | Leader  | running |  1 |           |
+--------+-------------+---------+---------+----+-----------+

在其余节点上:

+ Cluster: cluster_1 --+---------+---------+----+-----------+
| Member | Host        | Role    | State   | TL | Lag in MB |
+--------+-------------+---------+---------+----+-----------+
| node-1 | 10.0.100.1  | Leader  | running |  1 |           |
| node-2 | 10.0.100.2  | Replica | running |  1 |         0 |
+--------+-------------+---------+---------+----+-----------+

配置 HAProxy

HAproxy 是负载均衡器,也是客户端应用程序进入 PostgreSQL 集群的单一入口点。客户端应用程序访问 HAPpoxy URL 并将其读/写请求发送到那里。在后台,HAProxy 以循环方式将写入请求路由到主节点,将读取请求路由到辅助节点,这样就不会不必要地加载辅助实例。要实现这一点,请在 HAProxy 配置文件中提供不同的端口。在此部署中,写入被路由到端口 5000,读取被路由到端口 5001

这样,客户端应用程序就不知道底层集群中的哪个节点是当前主节点。HAProxy 将连接发送到健康节点(只要至少有一个健康节点可用),并确保客户端应用程序请求永远不会被拒绝。

  1. 在节点上安装 HAProxy HAProxy-demo

sudo yum install percona-haproxy

HAProxy 配置文件路径为:/etc/haproxy/haproxy.cfg。在此文件中指定以下配置。

global
    maxconn 100

defaults
    log global
    mode tcp
    retries 2
    timeout client 30m
    timeout connect 4s
    timeout server 30m
    timeout check 5s

listen stats   #“listen”部分定义了一个完整的代理,其前端和后端部分合并在一个部分中。它通常适用于仅 TCP 流量。
    mode http
    bind *:7000
    stats enable
    stats uri /

listen primary
    bind *:5000
    option httpchk /primary   #后端,启用 HTTP 协议检查服务器健康状况,option httpchk <method> <uri> <version>,method是http中的方法,可省略;uri是http中的路径;
    http-check expect status 200   #后端,定义以下 http-check 规则的注释,如果失败则在日志中报告。使 HTTP 健康检查考虑响应内容或特定状态代码,配合上面使用
    default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions   #后端,更改后端服务器的默认选项,inter ”参数将两次连续健康检查之间的间隔设置为 <delay> 毫秒;fall ” 参数表示服务器在连续 <count> 次健康检查失败后将被视为已死亡。如果未指定,此值默认为 3;rise ”参数表示在连续 <count> 次成功健康检查后,服务器将被视为正常运行;on-marked-down修改服务器被标记为关闭时发生的情况。目前有一个操作可用:-shutdown-sessions:关闭对等会话。启用此设置后,服务器关闭时所有与服务器的连接都会立即终止。
    server node1 node1:5432 maxconn 100 check port 8008
    server node2 node2:5432 maxconn 100 check port 8008
    server node3 node3:5432 maxconn 100 check port 8008

listen standbys
    balance roundrobin
    bind *:5001
    option httpchk /replica 
    http-check expect status 200  #后端
    default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
    server node1 node1:5432 maxconn 100 check port 8008  #check此选项启用服务器上的运行状况检查: - 未设置时,不执行运行状况检查,并且始终认为服务器可用。可以使用“ addr ”更改目标地址,使用“ port ”更改端口。
    server node2 node2:5432 maxconn 100 check port 8008
    server node3 node3:5432 maxconn 100 check port 8008
  1. HAProxy 将使用 Patroni 托管的 REST API 来检查每个 PostgreSQL 节点的健康状态并适当地路由请求。

  2. 启用 SELinux 布尔值以允许 HAProxy 绑定到非标准端口:

sudo setsebool -P haproxy_connect_any on

重新启动 HAProxy:

sudo systemctl restart haproxy

检查 HAProxy 日志以查看是否有任何错误:

sudo journalctl -u haproxy.service -n 100 -f

 


 来源:    https://docs.percona.com/postgresql/15/solutions/high-availability.html

https://www.haproxy.com/documentation/haproxy-configuration-tutorials/alerts-and-monitoring/prometheus/

https://www.haproxy.com/documentation/haproxy-configuration-manual/2-6r1/#4-server