暂无图片
暂无图片
4
暂无图片
暂无图片
暂无图片

postgresql+patroni+etcd高可用安装

原创 necessary 2025-03-28
647

简介

Patroni 是一个用于管理 PostgreSQL 高可用性的开源工具。它基于 etcd、ZooKeeper 或 Consul 作为分布式一致性存储,以提供自动故障转移和集群管理功能。

Patroni 的核心功能

自动故障转移:当主节点失效时,Patroni 会自动选择一个新的主节点,并重新配置集群。

健康检查:持续监测 PostgreSQL 节点的健康状态,并在必要时执行恢复操作。

Leader 选举:通过分布式一致性存储(如 etcd、Consul、ZooKeeper)进行主节点选举。

简化的配置管理:提供灵活的 YAML 配置文件,支持热更新,无需重启服务。

负载均衡支持:与 HAProxy 或其他负载均衡器结合,提供读写分离能力。

API 控制:提供 RESTful API 供外部管理和监控集群状态。

Patroni 工作原理

集群初始化:

启动多个 PostgreSQL 节点,每个节点运行 Patroni 进程。Patroni 进程与 etcd/Consul/ZooKeeper 交互,以存储和检索集群状态信息。

主节点选举:

Patroni 通过分布式一致性存储(DCS)决定主节点。只有成功获得租约的节点才能成为 leader(主节点)。

健康检查:

Patroni 定期检查 PostgreSQL 实例的运行状态。如果主节点失效,会触发自动故障转移,将一个从节点提升为新主节点。

复制管理:

其他 PostgreSQL 节点作为从节点(standby),同步主节点数据。当 Patroni 发现主节点故障时,它会选出最新的从节点作为新的主节点,并重新配置集群。

外部交互:

通过 REST API 与 HAProxy、应用程序或其他管理工具交互,提供集群信息和控制操作。

集群规划

ip hostname 组件
10.0.0.131 vm131 postgresql+etcd
10.0.0.132 vm132 postgresql+etcd
10.0.0.133 vm133 postgresql+etcd

安装

基础操作所有节点关闭防火墙、postgres用户组添加、selinux临时关闭、hostname配置、IP映射文件hosts配置本文不再赘述。

IP映射

cat > /etc/hosts << EOF
10.0.0.131 vm131
10.0.0.132 vm132
10.0.0.133 vm133
EOF

时钟同步

生产使用时钟工具,这里只做同步服务器时区处理
sudo timedatectl set-timezone Asia/Shanghai

etcd安装

yum -y install etcd yamllint

vm131执行以下

cat > /etc/etcd/etcd.conf << EOF ETCD_DATA_DIR="/var/lib/etcd/etcd01.etcd" ETCD_LISTEN_PEER_URLS="http://10.0.0.131:2380" ETCD_LISTEN_CLIENT_URLS="http://10.0.0.131:2379,http://127.0.0.1:2379" ETCD_NAME="etcd01" ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.0.0.131:2380" ETCD_ADVERTISE_CLIENT_URLS="http://10.0.0.131:2379" ETCD_INITIAL_CLUSTER="etcd01=http://10.0.0.131:2380,etcd02=http://10.0.0.132:2380,etcd03=http://10.0.0.133:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" ETCD_INITIAL_CLUSTER_STATE="new" EOF

vm132执行以下

cat > /etc/etcd/etcd.conf << EOF ETCD_DATA_DIR="/var/lib/etcd/etcd02.etcd" ETCD_LISTEN_PEER_URLS="http://10.0.0.132:2380" ETCD_LISTEN_CLIENT_URLS="http://10.0.0.132:2379,http://127.0.0.1:2379" ETCD_NAME="etcd02" ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.0.0.132:2380" ETCD_ADVERTISE_CLIENT_URLS="http://10.0.0.132:2379" ETCD_INITIAL_CLUSTER="etcd01=http://10.0.0.131:2380,etcd02=http://10.0.0.132:2380,etcd03=http://10.0.0.133:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" ETCD_INITIAL_CLUSTER_STATE="new" EOF

vm133执行以下

cat > /etc/etcd/etcd.conf << EOF ETCD_DATA_DIR="/var/lib/etcd/etcd03.etcd" ETCD_LISTEN_PEER_URLS="http://10.0.0.133:2380" ETCD_LISTEN_CLIENT_URLS="http://10.0.0.133:2379,http://127.0.0.1:2379" ETCD_NAME="etcd03" ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.0.0.133:2380" ETCD_ADVERTISE_CLIENT_URLS="http://10.0.0.133:2379" ETCD_INITIAL_CLUSTER="etcd01=http://10.0.0.131:2380,etcd02=http://10.0.0.132:2380,etcd03=http://10.0.0.133:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" ETCD_INITIAL_CLUSTER_STATE="new" EOF
参数解释:
 ETCD_DATA_DIR="/var/lib/etcd/etcd03.etcd"  ##指定 etcd 数据存储的目录
 ETCD_LISTEN_PEER_URLS="http://10.0.0.133:2380" ##指定 etcd 集群内部通信(Peer 通信) 的监听地址,即该节点用于与其他 etcd 节点交互的地址。
 ETCD_LISTEN_CLIENT_URLS="http://10.0.0.133:2379,http://127.0.0.1:2379" ##指定 etcd 对外提供服务的监听地址(即 Client 通信)。
 ETCD_NAME="etcd03" ##设置当前 etcd 节点的名称,需保证在集群中唯一。
 ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.0.0.133:2380" ##指定 当前 etcd 节点 向 其他 etcd 节点 广播的 Peer 通信地址。必须与 ETCD_LISTEN_PEER_URLS 保持一致。
 ETCD_ADVERTISE_CLIENT_URLS="http://10.0.0.133:2379" ##指定 当前 etcd 节点 向 外部客户端 广播的访问地址。
 ETCD_INITIAL_CLUSTER="etcd01=http://10.0.0.131:2380,etcd02=http://10.0.0.132:2380,etcd03=http://10.0.0.133:2380" ## 定义 etcd 集群的所有成员
 ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" ##指定 etcd 集群的唯一标识符,用于防止多个 etcd 集群相互干扰。
 ETCD_INITIAL_CLUSTER_STATE="new" ##指定 etcd 集群的初始化状态:"new":表示正在创建新的 etcd 集群。"existing":用于加入一个已经存在的 etcd 集群(避免错误地重新初始化)

yamlint 对etcd.conf的yaml的格式进行检查
yamllint /etc/etcd/etcd.conf
如果当前的repo没有此包,可以执行以下指令对其进行更新
bash <(curl -sSL https://linuxmirrors.cn/main.sh)
在这里插入图片描述
在这里插入图片描述

## 警告需要在开头增加--- 符号 非error提示可以忽略 ### vm131 第九行第33列结尾有多余空格,需要删掉 1:1 warning missing document start "---" (document-start) 9:33 error trailing spaces (trailing-spaces)

启动etcd

systemctl start etcd systemctl status etcd ## 检查以下etcd启动正常的情况开启自启动 systemctl enable etcd

检查etcd健康状态

etcdctl --endpoints=http://10.0.0.131:2379,http://10.0.0.132:2379,http://10.0.0.133:2379 cluster-health etcdctl cluster-health etcdctl member list

在这里插入图片描述

postgresql 安装

sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm sudo yum install -y postgresql15-server

##以下只需要在主节点初始化数据库即可

sudo /usr/pgsql-15/bin/postgresql-15-setup initdb sudo systemctl start postgresql-15 sudo systemctl status postgresql-15

在这里插入图片描述
最佳配置参数

echo -e "host all all all scram-sha-256\nhost replication all all scram-sha-256" | sudo tee -a /var/lib/pgsql/15/data/pg_hba.conf cat /var/lib/pgsql/15/data/pg_hba.conf |tail -n 5 echo -e "listen_addresses='*'\nwal_keep_size = 1024" | sudo tee -a /var/lib/pgsql/15/data/postgresql.conf cat /var/lib/pgsql/15/data/postgresql.conf |tail -n 5 systemctl restart postgresql-15.service systemctl status postgresql-15.service

在这里插入图片描述
创建流复制用户

su - postgres psql ALTER USER postgres WITH ENCRYPTED PASSWORD 'postgres'; CREATE USER repluser REPLICATION PASSWORD 'repluser123';

在这里插入图片描述

patroni安装

回到root用户下所有节点执行以下步骤,确保网络正常python为3.6以上版本,指令依次执行。

yum install -y python3 ##以下3.6选择和python版本一致的主版本号 curl https://bootstrap.pypa.io/pip/3.6/get-pip.py -o get-pip.py python3 get-pip.py pip install psycopg2-binary -i https://mirrors.aliyun.com/pypi/simple/ pip install patroni[etcd] -i https://mirrors.aliyun.com/pypi/simple/ patroni --version pip list

在这里插入图片描述

patroni配置

– vm131

cat > ~postgres/patroni.yaml << EOF --- scope: pg_patroni namespace: /patroni_metadata/ name: patroni1 restapi: listen: 10.0.0.131:8008 connect_address: 10.0.0.131:8008 etcd: host: 10.0.0.131:2379 bootstrap: dcs: ttl: 30 loop_wait: 10 retry_timeout: 10 initialize: false # 存入 DCS 的 initdb 参数 initdb: - encoding: UTF8 - data-checksums # 存入 DCS 的 PostgreSQL 配置 postgresql: use_slots: true use_pg_rewind: true # 存入 DCS 的 pg_hba 配置 pg_hba: - local all all trust - host all all all scram-sha-256 - host replication all all scram-sha-256 # 存入 DCS 的 postgresql 参数 parameters: listen_addresses: "*" wal_level: logical hot_standby: on max_connections: 100 max_wal_senders: 10 max_replication_slots: 10 max_prepared_transactions: 0 max_locks_per_transaction: 64 wal_log_hints: on track_commit_timestamp: off postgresql: listen: '*' connect_address: 10.0.0.131:5432 data_dir: /var/lib/pgsql/15/data bin_dir: /usr/pgsql-15/bin authentication: replication: username: repluser password: repluser123 superuser: username: postgres password: postgres parameters: listen_addresses: '*' port: 5432 max_connections: 200 superuser_reserved_connections: 10 shared_buffers: 512MB wal_level: logical hot_standby: on max_wal_senders: 10 max_replication_slots: 10 wal_log_hints: on replica: primary_slot_name: patroni1 tags: nofailover: false noloadbalance: false clonefrom: false nosync: false callbacks: on_start: /var/lib/pgsql/vip_manager.sh on_stop: /var/lib/pgsql/vip_manager.sh on_role_change: /var/lib/pgsql/vip_manager.sh EOF

– vm132

cat > ~postgres/patroni.yaml << EOF --- scope: pg_patroni namespace: /patroni_metadata/ name: patroni2 restapi: listen: 10.0.0.132:8008 connect_address: 10.0.0.132:8008 etcd: host: 10.0.0.132:2379 bootstrap: dcs: ttl: 30 loop_wait: 10 retry_timeout: 10 initialize: false # 存入 DCS 的 initdb 参数 initdb: - encoding: UTF8 - data-checksums # 存入 DCS 的 PostgreSQL 配置 postgresql: use_slots: true use_pg_rewind: true # 存入 DCS 的 pg_hba 配置 pg_hba: - local all all trust - host all all all scram-sha-256 - host replication all all scram-sha-256 # 存入 DCS 的 postgresql 参数 parameters: listen_addresses: "*" wal_level: logical hot_standby: on max_connections: 100 max_wal_senders: 10 max_replication_slots: 10 max_prepared_transactions: 0 max_locks_per_transaction: 64 wal_log_hints: on track_commit_timestamp: off postgresql: listen: '*' connect_address: 10.0.0.132:5432 data_dir: /var/lib/pgsql/15/data bin_dir: /usr/pgsql-15/bin authentication: replication: username: repluser password: repluser123 superuser: username: postgres password: postgres parameters: listen_addresses: '*' port: 5432 max_connections: 200 superuser_reserved_connections: 10 shared_buffers: 512MB wal_level: logical hot_standby: on max_wal_senders: 10 max_replication_slots: 10 wal_log_hints: on replica: primary_slot_name: patroni2 tags: nofailover: false noloadbalance: false clonefrom: false nosync: false callbacks: on_start: /var/lib/pgsql/vip_manager.sh on_stop: /var/lib/pgsql/vip_manager.sh on_role_change: /var/lib/pgsql/vip_manager.sh EOF

– vm133

cat > ~postgres/patroni.yaml << EOF --- scope: pg_patroni namespace: /patroni_metadata/ name: patroni3 restapi: listen: 10.0.0.133:8008 connect_address: 10.0.0.133:8008 etcd: host: 10.0.0.133:2379 bootstrap: dcs: ttl: 30 loop_wait: 10 retry_timeout: 10 initialize: false # 存入 DCS 的 initdb 参数 initdb: - encoding: UTF8 - data-checksums # 存入 DCS 的 PostgreSQL 配置 postgresql: use_slots: true use_pg_rewind: true # 存入 DCS 的 pg_hba 配置 pg_hba: - local all all trust - host all all all scram-sha-256 - host replication all all scram-sha-256 # 存入 DCS 的 postgresql 参数 parameters: listen_addresses: "*" wal_level: logical hot_standby: on max_connections: 100 max_wal_senders: 10 max_replication_slots: 10 max_prepared_transactions: 0 max_locks_per_transaction: 64 wal_log_hints: on track_commit_timestamp: off postgresql: listen: '*' connect_address: 10.0.0.133:5432 data_dir: /var/lib/pgsql/15/data bin_dir: /usr/pgsql-15/bin authentication: replication: username: repluser password: repluser123 superuser: username: postgres password: postgres parameters: listen_addresses: '*' port: 5432 max_connections: 200 superuser_reserved_connections: 10 shared_buffers: 512MB wal_level: logical hot_standby: on max_wal_senders: 10 max_replication_slots: 10 wal_log_hints: on replica: primary_slot_name: patroni3 tags: nofailover: false noloadbalance: false clonefrom: false nosync: true EOF

yaml格式效验

yamllint ~postgres/patroni.yaml ## 调整为不再有error即可

在这里插入图片描述

守护基础配置

cat > /usr/lib/systemd/system/patroni.service << EOF [Unit] Description=patroni - a high-availability PostgreSQL Documentation=https://patroni.readthedocs.io/en/latest/index.html After=syslog.target network.target etcd.target Wants=network-online.target [Service] Type=simple User=postgres Group=postgres PermissionsStartOnly=true ExecStart=/usr/local/bin/patroni /var/lib/pgsql/patroni.yaml ExecReload=/bin/kill -HUP $MAINPID LimitNOFILE=65536 KillMode=process KillSignal=SIGINT Restart=on-abnormal RestartSec=30s TimeoutSec=0 [Install] WantedBy=multi-user.target EOF

检查启动状态,先拉起主节点patroni,停留几秒,状态正常。再依次拉起其他节点

systemctl daemon-reload systemctl start patroni systemctl enable patroni systemctl status patroni patronictl -c /var/lib/pgsql/patroni.yaml list
常见错误

出现以下错误,说明dcs已经对该节点存储过一次数据库的信息

journalctl -u patroni --no-pager -n 50 CRITICAL: system ID mismatch, node patroni1 belongs to a different cluster: 7486515195984684289 != 7486517408449177422

使用以下指令清理DCS中的元数据

etcdctl rm --recursive /patroni_metadata/ etcdctl del --prefix /patroni_metadata/ patronictl -c /var/lib/pgsql/patroni.yaml remove pg_patroni
命令 适用范围 作用
etcdctl rm --recursive /patroni_metadata/ etcd v2 递归删除 /patroni_metadata/ 及其所有子键值对
etcdctl del --prefix /patroni_metadata/ etcd v3 删除 /patroni_metadata/ 及其所有前缀匹配的键值对
patronictl -c /var/lib/pgsql/patroni.yaml remove pg_patroni Patroni 从 Patroni 集群中移除 pg_patroni 节点

/patroni_metadata/ 是 ~postgres/patroni.yaml 文件中namespace:参数的值

在这里插入图片描述

创建callback脚本

cat > ~postgres/vip_manager.sh << EOF #!/bin/bash VIP=10.0.0.134 VIPBRD=10.0.0.255 VIPNETMASK=255.255.255.0 VIPNETMASKBIT=24 VIPDEV=ens33 VIPLABEL=1 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/sbin:/usr/sbin function usage() { echo "Usage: -bash <on_start|on_stop|on_role_change> <role> <scope>" exit 1 } function addvip(){ echo "$(date '+%Y-%m-%d %H:%M:%S') INFO: Adding VIP $VIP to $VIPDEV" sudo /sbin/ip addr add "$VIP/$VIPNETMASKBIT" brd "$VIPBRD" dev "$VIPDEV" label "$VIPDEV:$VIPLABEL" || echo "Failed to add VIP" sudo /usr/sbin/arping -q -A -c 1 -I "$VIPDEV" "$VIP" || echo "Failed to send ARP" } function delvip(){ echo "$(date '+%Y-%m-%d %H:%M:%S') INFO: Deleting VIP $VIP from $VIPDEV" sudo /sbin/ip addr del "$VIP/$VIPNETMASKBIT" dev "$VIPDEV" label "$VIPDEV:$VIPLABEL" || echo "Failed to delete VIP" sudo /usr/sbin/arping -q -A -c 1 -I "$VIPDEV" "$VIP" || echo "Failed to send ARP" } echo "$(date '+%Y-%m-%d %H:%M:%S') WARNING: patroni callback" case "$1" in on_stop) delvip ;; on_start) ;; on_role_change) if [[ "$2" == 'master' ]]; then addvip elif [[ "$2" == 'slave' ]] || [[ "$2" == 'replica' ]] || [[ "$2" == 'logical' ]]; then delvip fi ;; *) usage ;; esac EOF ##对以上脚本授予执行权限 ##授予 postgres sudo权限 chmod +x ~postgres/vip_manager.sh

给postgres用户授权ip 指令权限

echo "postgres ALL=(ALL) NOPASSWD: $(which ip), $(which arping)" | sudo tee -a /etc/sudoers > /dev/null cat /etc/sudoers

检查集群状态

patronictl -c patroni.yaml list

在这里插入图片描述
patroni 主从切换

patronictl -c patroni.yaml switchover pg_patroni --leader patroni1 --candidate patroni2 --force

在这里插入图片描述

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论