
前言
如果你直接按照网上的HAProxy和PG的配置来进行相关实验,可能要费很多时间来去重现。它们往往到具体的步骤的时候,给出的脚本要么是错误的,要么是无法执行的,需要你有一定的shell脚本判断能力,得自己动手去反复debug和纠正。互联网上的信息,我们不能要求太高,可以理解。
我以以前写过一篇由浅入深的文章:由浅入深高可用(HA)之: HAProxy
当时,只是试图解释一个HAProxy如何让Tomcat Web容器,具备负载均衡的基本功能。发给HAProxy节点的请求,会自动分发到后端的符合预期的Web节点。
而本文的目的与之类似,发往HAProxy节点的请求,如何有效的动态分发到后端符合预期的PostgreSQL节点上。比如,我只想发到read-write节点上;我只想发到read-only (standby)节点上。
至于主备节点的自动切换、自动故障转移,并不在本文的讨论范围之内。
实验过程逐步解析
1、预期效果
先说说预期访问效果:

客户端的请求,由HAProxy自动分发到master或者standby节点上。它们看到的只是HAProxy以及HAProxy上提供RW或者提供R(ead)服务的不同端口,仅此而已。
2、实验过程
2.1、PG一主一从两节点环境准备
就以PG14为例(无关乎pg15, p15, pg17)。先准备一主一从两个节点。
由于主从流复制的环境搭建,非常基础,这里就不介绍具体的搭建过程了。这里主节点是:
sean-rh1: 10.180.3.255: 5555
从节点是:
sean-rh2: 10.180.3.109: 5555
对应的环境变量配置是:
pgenv14.sh
export PGROOT=/usr/pgsql-14
export PGHOME=/var/lib/pgsql/14
export PGPORT=5555
export PGDATA=$PGHOME/data
export PATH=$PGROOT/bin:$PATH
export LD_LIBRARY_PATH=$PGROOT/lib:$LD_LIBRARY_PATH
免密配置文件:$HOME/.pgpass (权限600)
*:5555:postgres:postgres:<password of postgres>
127.0.0.1:*:postgres:postgres:<password of postgres>
以上是大致的PG主从节点准备过程,实际使用过程,你可以准备多个从节点。
2.2 、HAProxy准备
这里建议直接下载最新的HAProxy版本,当然也不反对直接安装的比较低的版本。使用Yum或Apt,针对不同的linux发行版,都可以达到目的。
直接从haproxy官网下载源码:haproxy-3.0.4.tar.gz:
wget https://www.haproxy.org/download/3.0/src/haproxy-3.0.4.tar.gz
make [USE_OPENSSL=1 USE_ZLIB=1] TARGET=linux-glibc
# []部分内容是可选,视实际情况。
# 安装:
sudo su -c "make install"
这样,haproxy会默认安装到: usr/local/sbin/haproxy下边。
走到这一步,系统所需要的软件包都基本具备了。
为使用方便,我们可以把它安装到一台独立的带有PG环境的机器上,这里直接安装到Sean-rh1那台机器上,它有一个外部IP地址:10.177.43.138
2.3、配置HAProxy
为简化相关操作,没有创建单独的haproxy用户,直接使用root用户。
在root用户的 $HOME目录下边,将前边的.pgpass文件复制到该目录下边,避免输入密码。另外上边的pgenv14.sh作为环境变量,haproxy需要用到。假定它的路径位于:/var/lib/pgsql/pgenv14.sh。
这里的HAProxy使用external check方式进行周期性检测。
配置文件:/etc/haproxy/haproxy.cfg, 其内容如下:
global
external-check
# insecure-fork-wanted
defaults
log global
mode tcp
option tcplog
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
listen stats
bind 0.0.0.0:8080
mode http
stats enable
stats uri
stats realm Haproxy\ Statistics
stats auth admin:admin
listen postgres_master
bind 0.0.0.0:5000
mode tcp
balance leastconn
option external-check
external-check command etc/haproxy/health_check.sh
server sean-rh2 10.180.3.109:5555 check
server sean-rh1 10.180.3.255:5555 check
listen postgres_standby
bind 0.0.0.0:5001
mode tcp
balance leastconn
option external-check
external-check command etc/haproxy/health_check_standby.sh
server sean-rh2 10.180.3.109:5555 check
server sean-rh1 10.180.3.255:5555 check
配置文件内容解释:
Global: 全局采用external check方式。是 HAProxy 配置中的一个高级功能。它允许你通过执行自定义的外部命令或脚本来执行健康检查。这种方法提供了比标准的 TCP 或 HTTP 健康检查更高的灵活性,因为它可以根据应用程序的特定需求和逻辑来检测后端服务器的健康状况。
stats: 8080端口,给出系统的统计状态的页面
postgres_master:5000, 允许通过5000端口访问HAproxy,最终访问到master节点。如何决定哪个节点是master? 通过脚本: etc/haproxy/health_check.sh的周期性执行。
Postgres_standby: 5001, 允许通过5001端口访问HAproxy, 最终访问到系统的动态分派的standby节点。如何知道哪些或哪个是standby节点,通过脚本:/etc/haproxy/health_check_standby.sh的周期性执行。
明白了配置文件的基本含义,就可以准备这两个检测脚本了。
2.3.1、先看看health_check.sh
#!/bin/bash
. var/lib/pgsql/pgenv14.sh
DB_USER="postgres"
DB_NAME="postgres"
# 从环境变量中获取 IP 和端口
DB_HOST="$HAPROXY_SERVER_ADDR"
DB_PORT="$HAPROXY_SERVER_PORT"
result='f'
check_result(){
result=`psql -U postgres -h $DB_HOST postgres -t -c "select pg_is_in_recovery();"`
echo $result
}
#
check_result
#根据上面 query 操作的结果退出,如果成功则退出码为 0,否则为 1
echo "HOST: $DB_HOST">>a.out
echo "check SQL: result is: $result" >> a.out
if test $result = "f"; then
echo "is in not recovery, it's master node: result is: 0" >> a.out
exit 0
else
echo "current node is standby node." >> a.out
exit 1
fi
解释:在上边的check_result函数当中,直接使用psql的结果,得到HOME/.pgpass的主要原因。
如果返回值为"f", 则意味着是master node。符合预期。
2.3.2、health_check_standby.sh
它与health_check.sh完全类似,只是判断条件完全相反。其内容如下:
#!/bin/bash
. var/lib/pgsql/pgenv14.sh
DB_USER="postgres"
DB_NAME="postgres"
# 从环境变量中获取 IP 和端口
DB_HOST="$HAPROXY_SERVER_ADDR"
DB_PORT="$HAPROXY_SERVER_PORT"
result='f'
check_result(){
result=`psql -U postgres -h $DB_HOST postgres -t -c "select pg_is_in_recovery();"`
echo $result
}
# 调用检查函数
check_result
#根据上面 query 操作的结果退出,如果成功则退出码为 0,否则为 1
echo "HOST: $DB_HOST">>a.out
echo "check SQL: result is: $result" >> a.out
if test $result = "f"; then
echo "is in not recovery, it's master node: result is: 1" >> a.out
exit 1
else
echo "current node is standby node." >> a.out
exit 0
fi
上述脚本中的输出到a.out,纯粹是为了debug。实际使用中,可以自行调整。
2.4、启动HAProxy
配置完了HAProxy之后,可以使用root用户去启动它。
sudo -s
nohup usr/local/sbin/haproxy -f etc/haproxy/haproxy.cfg &
tail -f a.out
tail -f a.out
is in not recovery, it's master node: result is: 0
HOST: 10.180.3.109
check SQL: result is: t
current node is standby node.
HOST: 10.180.3.255
check SQL: result is: f
is in not recovery, it's master node: result is: 0
HOST: 10.180.3.109
check SQL: result is: t
.....
启动完之后,很容易看到上边的诊断信息。
2.5、验证最后结果
2.5.1、统计信息
HAProxy上边的统计信息,我们可以访问http://10.177.43.138:8080, 得到的效果如下图:

上边可以很清楚的看到sean-rh1是主节点,可以提供读写操作,sean-rh2是备节点,可以提供读操作。如果我们有自动切换机制,当这两个节点切换以后,这个显示就会发生变化。
看到这里,我们就可以结合一些具备故障自动切换转移的高可用方案了,比如repmgr,patroni。
2.5.2、统一的数据库访问入口
访问主节点,可提供读写, 这里访问端口5000
03:14:57-root@sean-rh1:/var/lib/pgsql]$ psql -h 10.177.43.138 -p 5000 -U postgres mydb
Password for user postgres:
psql (14.8)
Type "help" for help.
mydb=# create table t123(id) as values(1), (2), (3);
SELECT 3
mydb=# select * from t123;
id
----
1
2
3
(3 rows)
访问只读节点, 这里访问的是5001端口,都访问的是HAProxy那台机器。我们能看到最后,它只能读取数据。并且确实是standby节点。
[03:16:52-root@sean-rh1:/var/lib/pgsql]$ psql -h 10.177.43.138 -p 5001 -U postgres mydb
Password for user postgres:
psql (14.8, server 14.10)
Type "help" for help.
mydb=# select * from t123;
id
----
1
2
3
(3 rows)
mydb=# insert into t123 values(4);
ERROR: cannot execute INSERT in a read-only transaction
mydb=#
mydb=# select pg_is_in_recovery();
pg_is_in_recovery
-------------------
t
(1 row)
总结
在你配置完HAProxy之后,你的应用程序只需要关心HAProxy提供的端口以及它对外暴露的IP地址即可。而应用代码不用做任何修改。使用起来非常方便。
比如我如果只想访问master节点,我只需要访问{HAProxy_IP}:{HAProxy_Port}即可。
理解了这个原理,再结合其他HA方案,就可以将自动故障转移的功能合并起来。形成强大的高可用解决方案。
感兴趣或有疑问的朋友,欢迎在文后留言交流。谢谢。
我是【Sean】, 微信: _iihero, 欢迎大家长按关注并加星公众号:数据库杂记。有好资源相送,同时为你提供及时更新。已关注的朋友,发送0、1到7,都有好资源相送。

往期导读:
1. PostgreSQL中配置单双向SSL连接详解
2. 提升PSQL使用技巧:PostgreSQL中PSQL使用技巧汇集(1)
3. 提升PSQL使用技巧:PostgreSQL中PSQL使用技巧汇集(2)
4. PostgreSQL SQL的基础使用及技巧
5. PostgreSQL开发技术基础:过程与函数
6. PostgreSQL中vacuum 物理文件truncate发生的条件
7. PostgreSQL中表的年龄与Vacuum的实验探索:Vacuum有大用
8. PostgreSQL利用分区表来弥补AutoVacuum的不足
9. 也聊聊PostgreSQL中的空间膨胀与AutoVacuum
10. 正确理解SAP BTP中hyperscaler PG中的IOPS (AWS篇)
11. 由浅入深高可用(HA)之: HAProxy




