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

PostgreSQL HAProxy ha & load balance 代理

digoal 2019-11-01
1502

作者

digoal

日期

2019-11-01

标签

PostgreSQL , haproxy , master listen , slave(s) listen , ha , stream replication , session loadbalance , xinetd


背景

HAProxy是一个非常流行的4层、7层(http)负载均衡与会话路由软件,效率也非常高。

虽然他没有办法直接实现pg的读写分离,但是可以实现比较简单的几类PG接入点(监听点)以及会话failover管理:

1、一个或多个主实例。(多个主实例通常是指如下架构:pg-xl的coordinator, citus的coordinator。或者完全对等的multi-master节点。)

pic

当会话对应的后端数据库检测到不符合预期状态时,主动断开会话,重联。(failover 会话)

rw点切换后,主动断开这个节点的会话,迁移到新的rw节点

pic

2、一个或多个只读实例。(例如一主多从多结构)

当后端有多个ro节点时,可以load balance(会话级)

pic

切换后,主动断开这个节点的会话,迁移到新的ro节点

pic

实现例子

参考 https://www.percona.com/blog/2019/10/31/postgresql-application-connection-failover-using-haproxy-with-xinetd/

环境: CentOS 7, PostgreSQL , 主从 多台 (主从切换不需要haproxy管,haproxy只负责根据后端数据库的状态切换会话)

准备几个东西

  • A simple shell script to check the status of the PostgreSQL instance running on the local machine.
  • A xinetd service daemonizer.
  • HAProxy: Which maintains the routing mechanism.

数据库服务器部署:check 后端数据库的脚本

这个脚本配置在数据库服务器上,用来获取数据库的三种状态:

1、恢复中(从库),返回206

2、主库,返回200

3、无法确认(数据库连接不上或其他问题),返回503

脚本如下,不同状态返回不同的内容:

```

!/bin/bash

This script checks if a postgres server is healthy running on localhost. It will return:

"HTTP/1.x 200 OK\r" (if postgres is running smoothly)

- OR -

"HTTP/1.x 500 Internal Server Error\r" (else)

The purpose of this script is make haproxy capable of monitoring postgres properly

It is recommended that a low-privileged postgres user is created to be used by this script.

For eg. create user healthchkusr login password 'hc321';

PGBIN=/usr/pgsql-10/bin
PGSQL_HOST="localhost"
PGSQL_PORT="5432"
PGSQL_DATABASE="postgres"
PGSQL_USERNAME="postgres"
export PGPASSWORD="passwd"
TMP_FILE="/tmp/pgsqlchk.out"
ERR_FILE="/tmp/pgsqlchk.err"

We perform a simple query that should return a few results

调用如下脚本,看返回结果pg_is_in_recovery看是不是在恢复中,t表示从库。f表示主库。

VALUE=/opt/bigsql/pg96/bin/psql -t -h localhost -U postgres -p 5432 -c "select pg_is_in_recovery()" 2> /dev/null

Check the output. If it is not empty then everything is fine and we return something. Else, we just do not return anything.

if [ $VALUE == "t" ]
then
/bin/echo -e "HTTP/1.1 206 OK\r\n"
/bin/echo -e "Content-Type: Content-Type: text/plain\r\n"
/bin/echo -e "\r\n"
/bin/echo "Standby"
/bin/echo -e "\r\n"
elif [ $VALUE == "f" ]
then
/bin/echo -e "HTTP/1.1 200 OK\r\n"
/bin/echo -e "Content-Type: Content-Type: text/plain\r\n"
/bin/echo -e "\r\n"
/bin/echo "Primary"
/bin/echo -e "\r\n"
else
/bin/echo -e "HTTP/1.1 503 Service Unavailable\r\n"
/bin/echo -e "Content-Type: Content-Type: text/plain\r\n"
/bin/echo -e "\r\n"
/bin/echo "DB Down"
/bin/echo -e "\r\n"
fi
```

脚本位置/opt/pgsqlchk

sudo chmod 755 /opt/pgsqlchk

数据库服务器部署:配置 xinetd 服务(check后端)

将/opt/pgsqlchk 检查脚本配置为xinetd服务,通过某个监听端口调用(本例为23267)

```
yum install -y xinetd telnet

vi /etc/xinetd.d/pgsqlchk

service pgsqlchk
{
flags = REUSE
socket_type = stream
port = 23267
wait = no
user = nobody
server = /opt/pgsqlchk
log_on_failure += USERID
disable = no
only_from = 0.0.0.0/0
per_source = UNLIMITED
}
```

添加xinetd服务

bash -c 'echo "pgsqlchk 23267/tcp # pgsqlchk" >> /etc/services'

启动xinetd服务

systemctl start xinetd

HAproxy节点部署

假设以上有两台数据库服务器pg0, pg1 (hostname, or dns配置).

另一台服务器,用于部署haproxy,如下。

yum install -y haproxy

配置haproxy.cfg

监听两个端口,5000对应check返回200状态的数据库节点(pg_is_in_recovery=f),为rw节点

5001对应check返回206状态的数据库节点(pg_is_in_recovery=t),为ro节点

```
vi /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
mode http
bind *:7000
stats enable
stats uri /

listen ReadWrite
bind *:5000
option httpchk
http-check expect status 200
default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
server pg0 pg0:5432 maxconn 100 check port 23267
server pg1 pg1:5432 maxconn 100 check port 23267

listen ReadOnly
bind *:5001
option httpchk
http-check expect status 206
default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
server pg0 pg0:5432 maxconn 100 check port 23267
server pg1 pg1:5432 maxconn 100 check port 23267
```

配置解读

  • HAProxy is configured to use TCP mode
  • HAProxy service will start listening to port 5000 and 5001
  • Port 5000 is for Read-Write connections and 5001 is for Read-Only connections
  • Status check is done using http-check feature on port 23267
  • Both server pg0 and pg1 are candidates for both Read-write and Read-only connections
  • Based on the http-check and the status returned, it decides the current role

如果只读节点有多台,配置到server里面即可。

启动 haproxy服务

systemctl start haproxy

测试

连接haproxy 5000端口,读写节点。

```
$ psql -h localhost -p 5000 -U postgres
Password for user postgres:
psql (9.6.5)
Type "help" for help.

postgres=# select pg_is_in_recovery();
pg_is_in_recovery


f
(1 row)
```

连接haproxy 5001端口,只读节点。

```
$ psql -h localhost -p 5001 -U postgres
Password for user postgres:
psql (9.6.5)
Type "help" for help.

postgres=# select pg_is_in_recovery();
pg_is_in_recovery


t
(1 row)
```

haproxy配置介绍

https://www.cnblogs.com/MacoLee/p/5853413.html

https://www.jianshu.com/p/8af373981cfe

```

#####全局配置#########

global
  log 127.0.0.1 local0 #[日志输出配置,所有日志都记录在本机,通过local0输出]
  log 127.0.0.1 local1 notice #定义haproxy 日志级别[error warringinfo debug]
  daemon #以后台形式运行harpoxy
  nbproc 1 #设置进程数量
  maxconn 4096 #默认最大连接数,需考虑ulimit-n限制
  #user haproxy #运行haproxy的用户
  #group haproxy #运行haproxy的用户所在的组
  #pidfile /var/run/haproxy.pid #haproxy 进程PID文件
  #ulimit-n 819200 #ulimit 的数量限制
  #chroot /usr/share/haproxy #chroot运行路径
  #debug #haproxy 调试级别,建议只在开启单进程的时候调试
  #quiet

##默认配置############

defaults
  log global
  mode http #默认的模式mode { tcp|http|health },tcp是4层,http是7层,health只会返回OK
  option httplog #日志类别,采用httplog
  option dontlognull #不记录健康检查日志信息
  retries 2 #两次连接失败就认为是服务器不可用,也可以通过后面设置
  #option forwardfor #如果后端服务器需要获得客户端真实ip需要配置的参数,可以从Http Header中获得客户端ip
  option httpclose #每次请求完毕后主动关闭http通道,haproxy不支持keep-alive,只能模拟这种模式的实现
  #option redispatch #当serverId对应的服务器挂掉后,强制定向到其他健康的服务器,以后将不支持
  option abortonclose #当服务器负载很高的时候,自动结束掉当前队列处理比较久的链接
  maxconn 4096 #默认的最大连接数
  timeout connect 5000ms #连接超时
  timeout client 30000ms #客户端超时
  timeout server 30000ms #服务器超时
  #timeout check 2000 #心跳检测超时
  #timeout http-keep-alive10s #默认持久连接超时时间
  #timeout http-request 10s #默认http请求超时时间
  #timeout queue 1m #默认队列超时时间
  balance roundrobin #设置默认负载均衡方式,轮询方式
  #balance source #设置默认负载均衡方式,类似于nginx的ip_hash
  #balnace leastconn #设置默认负载均衡方式,最小连接数

##统计页面配置########

listen stats
  bind 0.0.0.0:1080 #设置Frontend和Backend的组合体,监控组的名称,按需要自定义名称
  mode http #http的7层模式
  option httplog #采用http日志格式
  #log 127.0.0.1 local0 err #错误日志记录
  maxconn 10 #默认的最大连接数
  stats refresh 30s #统计页面自动刷新时间
  stats uri /stats #统计页面url
  stats realm XingCloud\ Haproxy #统计页面密码框上提示文本
  stats auth admin:admin #设置监控页面的用户和密码:admin,可以设置多个用户名
  stats auth Frank:Frank #设置监控页面的用户和密码:Frank
  stats hide-version #隐藏统计页面上HAProxy的版本信息
  stats admin if TRUE #设置手工启动/禁用,后端服务器(haproxy-1.4.9以后版本)

##设置haproxy 错误页面#####

errorfile 403 /home/haproxy/haproxy/errorfiles/403.http

errorfile 500 /home/haproxy/haproxy/errorfiles/500.http

errorfile 502 /home/haproxy/haproxy/errorfiles/502.http

errorfile 503 /home/haproxy/haproxy/errorfiles/503.http

errorfile 504 /home/haproxy/haproxy/errorfiles/504.http

##frontend前端配置##############

frontend main
  bind :80 #这里建议使用bind :80的方式,要不然做集群高可用的时候有问题,vip切换到其他机器就不能访问了。
  acl web hdr(host) -i www.abc.com #acl后面是规则名称,-i为忽略大小写,后面跟的是要访问的域名,如果访问www.abc.com这个域名,就触发web规则,。
  acl img hdr(host) -i img.abc.com #如果访问img.abc.com这个域名,就触发img规则。
  use_backend webserver if web #如果上面定义的web规则被触发,即访问www.abc.com,就将请求分发到webserver这个作用域。
  use_backend imgserver if img #如果上面定义的img规则被触发,即访问img.abc.com,就将请求分发到imgserver这个作用域。
  default_backend dynamic #不满足则响应backend的默认页面

##backend后端配置##############

backend webserver #webserver作用域
  mode http
  balance roundrobin #balance roundrobin 负载轮询,balance source 保存session值,支持static-rr,leastconn,first,uri等参数
  option httpchk /index.html HTTP/1.0 #健康检查, 检测文件,如果分发到后台index.html访问不到就不再分发给它
  server web1 10.16.0.9:8085 cookie 1 weight 5 check inter 2000 rise 2 fall 3
  server web2 10.16.0.10:8085 cookie 2 weight 3 check inter 2000 rise 2 fall 3
  #cookie 1表示serverid为1,check inter 1500 是检测心跳频率
  #rise 2是2次正确认为服务器可用,fall 3是3次失败认为服务器不可用,weight代表权重

backend imgserver
  mode http
  option httpchk /index.php
  balance roundrobin
  server img01 192.168.137.101:80 check inter 2000 fall 3
  server img02 192.168.137.102:80 check inter 2000 fall 3

backend dynamic
  balance roundrobin
  server test1 192.168.1.23:80 check maxconn 2000
  server test2 192.168.1.24:80 check maxconn 2000

listen tcptest
  bind 0.0.0.0:5222
  mode tcp
  option tcplog #采用tcp日志格式
  balance source
  #log 127.0.0.1 local0 debug
  server s1 192.168.100.204:7222 weight 1
  server s2 192.168.100.208:7222 weight 1
```

参考

https://www.percona.com/blog/2019/10/31/postgresql-application-connection-failover-using-haproxy-with-xinetd/

https://www.jianshu.com/p/8af373981cfe

https://www.cnblogs.com/MacoLee/p/5853413.html

PostgreSQL 许愿链接

您的愿望将传达给PG kernel hacker、数据库厂商等, 帮助提高数据库产品质量和功能, 说不定下一个PG版本就有您提出的功能点. 针对非常好的提议,奖励限量版PG文化衫、纪念品、贴纸、PG热门书籍等,奖品丰富,快来许愿。开不开森.

9.9元购买3个月阿里云RDS PostgreSQL实例

PostgreSQL 解决方案集合

德哥 / digoal's github - 公益是一辈子的事.

digoal's wechat

文章转载自digoal,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论