标题应该没有违反广告法
本教程为全国最完整的PGSQL高可用集群方案,以下的每一个步骤都事关重要。
Centos7.8
Posgresql10
Repmgr10
两台服务器:
192.168.126.143
192.168.126.144
服务器名称:
hostnamectl set-hostname post1
hostnamectl set-hostname post2
1.先两台机器都安装Posgresql
2.两台机器都安装Repmgr
3.两台机器都设置postgres用户的相互信任
4.其中一台机器设置好postgresql的配置文件,并且初始化数据库
5.另一台机器克隆数据库
6.修改repmgr的配置
7.将两台机器加入集群节点
8.测试两台都关机
9.测试关机一台
10.安装keepalived配置VIP
11.配置高可用
两节点同时操作 yum install -y gcc bison gcc-c++ readline readline-devel zlib zlib-devel perl perl-devel telnet openssl openssl-devel systemctl stop firewalld 安装PGSQL 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 postgresql10-server sudo /usr/pgsql-10/bin/postgresql-10-setup initdb sudo systemctl enable postgresql-10 sudo systemctl start postgresql-10 安装REPMGR curl https://dl.2ndquadrant.com/default/release/get/10/rpm | sudo bash sudo yum repolist yum search repmgr yum --showduplicates list repmgr10 yum install repmgr10-4.4-1.el7 su postgres ln -s /usr/pgsql-11/bin/repmgr /usr/local/sbin/ exit 安装完成后的路径如下: 可执行目录/usr/pgsql-10/bin/ 数据实例的目录/var/lib/pgsql/10/data/ 修改主节点的postgesql.conf 别的地方不改,默认改这几个地方 listen_addresses = '*' # what IP address(es) to listen on; # comma-separated list of addresses; # defaults to 'localhost'; use '' for all # (change requires restart) port = 5432 # (change requires restart) max_connections = 1000 # (change requires restart) wal_log_hints=on archive_mode=on archive_command='test ! -f /pgarch/%f && cp %p /pgarch/%f' shared_preload_libraries='repmgr' 修改主节点的pg_hba.conf 注意IP地址根据自己情况来配置 local all all peer # IPv4 local connections: host all all 127.0.0.1/32 trust host all all 172.16.108.54/32 md5 host all all 192.168.126.144/32 trust host all all 192.168.126.143/32 trust host all all 0.0.0.0/0 md5 # IPv6 local connections: host all all ::1/128 ident # Allow replication connections from localhost, by a user with the # replication privilege. local replication repmgr trust local repmgr repmgr trust host replication repmgr 127.0.0.1/32 trust host replication repmgr 192.168.126.143/32 trust host replication repmgr 192.168.126.144/32 trust host repmgr repmgr 127.0.0.1/32 trust host repmgr repmgr 192.168.126.143/32 trust host repmgr repmgr 192.168.126.144/32 trust 修改主节点的vim /etc/repmgr/10/repmgr.conf node_id=143 #唯一值,用于标识本服务器 node_name=post1 #连接到本机的信息 conninfo='host=192.168.126.143 user=repmgr dbname=repmgr connect_timeout=2' ##postgresql的data路径 data_directory='/var/lib/pgsql/10/data' connection_check_type=ping failover='automatic' promote_command='repmgr standby promote -f /etc/repmgr/10/repmgr.conf --log-to-file' follow_command='repmgr standby follow -f /etc/repmgr/10/repmgr.conf --log-to-file --upstream-node-id=%n' log_file='/var/lib/pgsql/10/repmgrd.log' monitoring_history=true # (启用监控参数) monitor_interval_secs=1 #(定义监视数据间隔写入时间参数) reconnect_attempts=2 #(故障转移之前,尝试重新连接主库次数(默认为6)参数) reconnect_interval=2 #(每间隔5s尝试重新连接一次参数) 修改备节点的vim /etc/repmgr/10/repmgr.conf node_id=144 #唯一值,用于标识本服务器 node_name=post2 ##连接到本机的信息 conninfo='host=192.168.126.144 user=repmgr dbname=repmgr connect_timeout=2' ##postgresql的data路径 data_directory='/var/lib/pgsql/10/data' connection_check_type=ping failover='automatic' promote_command='repmgr standby promote -f /etc/repmgr/10/repmgr.conf --log-to-file' follow_command='repmgr standby follow -f /etc/repmgr/10/repmgr.conf --log-to-file --upstream-node-id=%n' log_file='/var/lib/pgsql/10/repmgrd.log' monitoring_history=true # (启用监控参数) monitor_interval_secs=1 #(定义监视数据间隔写入时间参数) reconnect_attempts=2 #(故障转移之前,尝试重新连接主库次数(默认为6)参数) reconnect_interval=2 #(每间隔5s尝试重新连接一次参数) 修改完成后重启PGSQL
重启数据库服务器有两个方法 方法1 service postgresql-10 restart 方法2 su postgres cd /usr/pgsql-10/bin/ ./pg_ctl -D /var/lib/pgsql/10/data restart
两台服务器同时操作 su postgres psql ALTER USER postgres WITH PASSWORD '你的数据库密码'; \q exit ssh-keygen -t rsa ssh-copy-id -i /var/lib/pgsql/.ssh/id_rsa.pub postgres@192.168.126.144 restorecon -r -vv /var/lib/pgsql/.ssh ssh 'postgres@192.168.126.144' 如果不用输入密码就可以进入,则正常 另一台机器也相同的操作,IP不一样,要测试一下 不输入密码才是对的 ssh-keygen -t rsa ssh-copy-id -i /var/lib/pgsql/.ssh/id_rsa.pub postgres@192.168.126.143 restorecon -r -vv /var/lib/pgsql/.ssh ssh 'postgres@192.168.126.143' 主操作 su postgres createuser -s repmgr createdb repmgr -O repmgr #进入psql psql #在psql中设置repmgr依次默认查找repmgr、同用户名、public三个schema ALTER USER repmgr SET search_path TO repmgr, "$user", public; #退出psql \q 在备机测试是否能连接主机 su postgres psql 'host=192.168.126.14 user=repmgr dbname=repmgr connect_timeout=2' 重启主服务器并且将主服务器注册为主节点 service postgresql-10 restart su postgres repmgr -f /etc/repmgr/10/repmgr.conf primary register repmgr cluster show 这样就可以看到集群中有一台主节点
停止备用数据库 su postgres cd /usr/pgsql-10/bin/ ./pg_ctl -D /var/lib/pgsql/10/data stop 删除备用数据库的数据目录 rm -rf /var/lib/pgsql/10/data/* 从机测试克隆 repmgr -h 192.168.126.143 -U repmgr -d repmgr -f /etc/repmgr/10/repmgr.conf standby clone --dry-run 如果没报错就继续 从机执行克隆 repmgr -h 192.168.126.143 -U repmgr -d repmgr -f /etc/repmgr/10/repmgr.conf standby clone 克隆完成后,检查/var/lib/pgsql/10/data/目录是否又有文件了
从机启动PGSQL su postgres cd /usr/pgsql-10/bin/ ./pg_ctl -D /var/lib/pgsql/10/data start 从机注册子节点 repmgr -f /etc/repmgr/10/repmgr.conf standby register repmgr cluster show 查看是否两台都加入集群
测试主备手动一键切换 su postgres repmgr -f /etc/repmgr/10/repmgr.conf standby switchover -U repmgr --verbose --dry-run 没有报错就执行切换,仅仅在子节点执行切换命令 repmgr -f /etc/repmgr/10/repmgr.conf standby switchover -U repmgr --verbose 执行后,检查主备是否交换 repmgr cluster show 两台节点都启动repmgrd repmgrd –f /etc/repmgr/10/repmgr.conf --pid-file /tmp/repmgrd.pid 查看进程是否存在 cat/tmp/repmgrd.pid 将repmgrd设置为开机启动 新建文件/rep.sh 内容如下: sleep 8s su postgres -c "/usr/pgsql-10/bin/repmgrd –f /etc/repmgr/10/repmgr.conf --pid-file /tmp/repmgrd.pid" shift+z+z保存 给文件授权 chmod +x rep.sh 编辑文件 vim /etc/rc.local #!/bin/bash touch /var/lock/subsys/local ./rep.sh
到这里,集群的主备搭建就完成了,支持水平扩展多个子节点
两台节点同时操作 yum install -y keepalived cat /etc/keepalived/keepalived.conf ifconfig查看网卡名称为ens33 vim /etc/keepalived/keepalived.conf [root@post1 /]# cat /etc/keepalived/keepalived.conf ! Configuration File for keepalived vrrp_instance VI_1 { state BACKUP #网卡名字 interface ens33 #局域网不能有冲突 virtual_router_id 55 #设置为不抢占 nopreempt priority 99 #4秒检查一次 advert_int 4 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { #集群VIP 192.168.126.150 } } 设置开机启动与服务启动 systemctl start keepalived.service systemctl enable keepalived.service 检查VIP被谁拿走了 ip a
安装完成后,测试集群VIP是否生效,然后关机测试VIP是否能继续访问,程序继续往数据库写入。
两台节点执行
sudo rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm sudo yum install dotnet-sdk-5.0 cd /root mkdir vip cd vip dotnet new console dotnet add package Npgsql --version 5.0.7 vim Program.cs编辑如下代码
using Npgsql; using System; using System.Diagnostics; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Threading; /**** * 本代码的作用是确保keepalived的VIP是数据库的主节点。本代码仅仅只需要修改VIP和数据库链接密码 * ***/ namespace vip { class Program { static void Main(string[] args) { while (true) { try { //两个服务器的数据库链接字符串,VIP地址,主=False,True是备 bool zb =true; zb = Exec("Host=127.0.0.1;Port=5432;Username=postgres;Password=数据库的密码;Database=postgres");//请修改数据库密码 string vip = "192.168.126.150";//请修改这里的IP //Console.WriteLine("主备情况=>True是备,Flase是主----》"+zb); //获取IP地址是否包含VIP if (GetVIP(vip) && !zb) { //正常情况 VIP在本机,并且本机是F主 //什么也不做 } else if (!GetVIP(vip) && zb) { //VIP不在本机,本机不是主,正常 //什么也不做 } else if (GetVIP(vip) && zb) { Console.WriteLine("本机不是主,但是有VIP,所以重启KEEPALIVED丢弃VIP"); //VIP在本机,但是本机不是主,则重启keepalived Bash("service keepalived stop"); Thread.Sleep(4000);//睡4秒的作用是确保VIP已经漂移 Bash("service keepalived start"); } else if (!GetVIP(vip) && !zb ) { //VIP不在本机,本机是主,又另外的从机来执行重启keepalived. //什么也不做 } } catch (Exception ex) { Console.WriteLine("异常:" + ex.Message); continue; } Thread.Sleep(100);//防止占用太多资源100ms检查一次 } } /// <summary> /// 检查本机是否包含VIP /// </summary> /// <param name="vip"></param> /// <returns></returns> static bool GetVIP(string vip) { string ipa=Bash("ip a"); // Console.WriteLine(ipa); if (ipa.Contains(vip)) return true; else return false; } /// <summary> /// 执行BASH命令 /// </summary> /// <param name="vip"></param> /// <returns></returns> public static string Bash(string escapedArgs) { var process = new Process() { StartInfo = new ProcessStartInfo { FileName = "/bin/bash", Arguments = $"-c \"{escapedArgs}\"", RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true, } }; process.Start(); string result = process.StandardOutput.ReadToEnd(); process.WaitForExit(); return result; } /// <summary> /// 数据库相关操作,读取PGSQL的pg_is_in_recovery表 /// </summary> /// <param name="vip"></param> /// <returns></returns> public static bool Exec(string connStr) { bool ct = true; try { using (var conn = new NpgsqlConnection(connStr)) { conn.Open(); using (NpgsqlCommand cmd = new NpgsqlCommand("select pg_is_in_recovery from pg_is_in_recovery()", conn)) { NpgsqlDataReader rd = cmd.ExecuteReader(); while (rd.Read()) { ct = Convert.ToBoolean(rd["pg_is_in_recovery"]); return ct; } conn.Close(); return ct; } } } catch (Exception ex) { Console.WriteLine("SQL异常:" + ex.Message); return ct; } } } }
dotnet build
执行测试一下功能
dotnet run
将本程序设置为两个节点开机启动
vim rep.sh
sleep 8s
su postgres -c "/usr/pgsql-10/bin/repmgrd –f /etc/repmgr/10/repmgr.conf --pid-file /tmp/repmgrd.pid"
nohup dotnet /root/vip/bin/Debug/net5.0/vip.dll&
保存退出,确保程序开机执行!
以下测试过程保持测试工具一直数据插入和读取状体,保持不丢失数据,保持数据一致性。
初始条件 | 执行 | 影响范围 | 预期状态 | 其他 | 测试结果 |
A主,B备 | 关闭B | 不影响业务 | 一切正常 | B再开机也正常 | 通过 |
A主,B备 | 关闭A | 业务10秒内恢复 | B为备,VIP在B机 | 稳妥起见,A机要人工介入检查,执行/rejoin.sh | 通过 |
A主,B备 | 同时关闭AB | 影响业务 | 链接失败 | 重新开机业务自动恢复正常 | 通过 |
A主,B备 | 主备人工切换 | 业务10秒内恢复 | B主,A从 | 数据库主备在3秒左右切换 vip在7秒左右切换 | 通过 |
A主,B备 | 停止B的PGSQL | 无影响 | 无影响 | B在启动也无影响 | 通过 |
A主,B备 | 停止A的PGSQL | 业务10秒内恢复 | B会变成主 | repmgrd会自动切换B为主 VIP守护进程链接不上本机数据库,丢弃VIP A机要人工介入检查,执行/rejoin.sh | 通过 |
A主,B备 | 手动关闭A的keepalived | 业务异常,不可读写 | B有VIP,但是B发现自己的数据库是备机,会重启丢弃VIP,导致大家都没有VIP | VIP守护发现本机有VIP,但是不是主节点 会循环重启keepalived,直到A机恢复正常。 | 通过 |
A主,B备 | 手动关闭B的keepalived | 业务正常 | 一切正常 | 再启动keepalived也正常 | 通过 |
A主,B备 | 断开两个节点链接 | 业务异常 | ip地址冲突 | 网内无法访问 | 通过 |
测试工具以及代码
https://github.com/cagy520/MYSQL-CLUSTER-TEST.git
repmgr做主备搭建相对PGPOOL要容易一些,而且也不像PGPOOL那样有性能损耗。
用keepalived的目的是为了主备虚拟一个集群IP地址出来,避免单节点故障。
使用VIP进程守护的目的是为了避免VIP漂移到备用节点上。
不使用shell脚本处理VIP保持主节点的原因是不够灵活,逻辑不易理解,会给后续部署带来麻烦。
尽量避免使用编译安装的方式安装repmgr,会遇到很多问题。
为什么主节点挂了之后,主节点漂移,原来主节点需要手动添加到集群,当遇到这种情况的时候,建议人工介入检查一下,避免数据不一致。
本教程的每一个细节步骤都很重要。
测试主备切换,仅仅在备机执行
repmgr -f /etc/repmgr/10/repmgr.conf standby switchover -U repmgr --verbose --dry-run
repmgr -f /etc/repmgr/10/repmgr.conf standby switchover -U repmgr --verbose
原来故障的主节点从新作为备节点加入集群,也就是/rejoin.sh
repmgr node rejoin -d 'host=192.168.126.143 dbname=repmgr user=repmgr' --force-rewind --config-files=postgresql.conf,postgresql.auto.conf --verbose --dry-run
repmgr node rejoin -d 'host=192.168.126.143 dbname=repmgr user=repmgr' --force-rewind --config-files=postgresql.conf,postgresql.auto.conf --verbose
从集群中剔除节点
repmgr primary unregister --force --node-id post1