标题应该没有违反广告法
本教程为全国最完整的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.配置高可用
安装步骤
1.分别安装Postgresql和Repmgr
两节点同时操作
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
2.SSH互信以及PGSQL互信
两台服务器同时操作
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
这样就可以看到集群中有一台主节点
3.备机克隆数据库
停止备用数据库
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/目录是否又有文件了
4.备机加入集群
从机启动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
查看是否两台都加入集群
5.主备切换
测试主备手动一键切换
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
到这里,集群的主备搭建就完成了,支持水平扩展多个子节点
6.keepalived安装
两台节点同时操作
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是否能继续访问,程序继续往数据库写入。
7.高可用的配置
两台节点执行
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&
保存退出,确保程序开机执行!
8.测试用例
以下测试过程保持测试工具一直数据插入和读取状体,保持不丢失数据,保持数据一致性。
初始条件 |
执行 |
影响范围 |
预期状态 |
其他 |
测试结果 |
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
9.总结
repmgr做主备搭建相对PGPOOL要容易一些,而且也不像PGPOOL那样有性能损耗。
用keepalived的目的是为了主备虚拟一个集群IP地址出来,避免单节点故障。
使用VIP进程守护的目的是为了避免VIP漂移到备用节点上。
不使用shell脚本处理VIP保持主节点的原因是不够灵活,逻辑不易理解,会给后续部署带来麻烦。
尽量避免使用编译安装的方式安装repmgr,会遇到很多问题。
为什么主节点挂了之后,主节点漂移,原来主节点需要手动添加到集群,当遇到这种情况的时候,建议人工介入检查一下,避免数据不一致。
本教程的每一个细节步骤都很重要。
10.其他命令
测试主备切换,仅仅在备机执行
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