目录
上一篇我们使用 Heartbeat 的 HA 功能,实现 MySQL 主从复制的自动故障切换。它的工作原理是:当 Heartbeat 启动时,会将 VIP 绑定到 haresources 文件中指定的主机上。自定义 mysql_check 检查脚本,定期(例子中为三秒钟)检查本机 MySQL 的服务器状态,如果 MySQL 不可用,则停止本机的 Heartbeat 服务。Heartbeat 每秒钟(由 keepalive 参数指定)会检查一次本机心跳,如果不存在,则将 VIP 绑定到另一台机器上,同时调用 remove_slave 脚本执行从库切换为主库的操作。
本篇我们将做另一个实验,利用 haproxy 实现 MySQL 双主复制的读写负载均衡与 MySQL 的高可用,同时用 Heartbeat 保证两台负载均衡器的高可用性。
一、中间件简述
1. Heartbeat 简介
参见“https://blog.youkuaiyun.com/wzy0623/article/details/81188814#一、Heartbeat简介”。
2. haproxy 简介
haproxy 是一个开源的高性能的反向代理或者说是负载均衡服务软件,它支持双机热备、虚拟主机、基于 TCP 和 HTTP 应用代理等功能。其配置简单,而且拥有很好的对服务器节点的健康检查功能(相当于 keepalived 健康检查),当其代理的后端服务器出现故障时,haproxy 会自动将该故障服务器摘除,当服务器的故障恢复后 haproxy 还会自动重新添加回服务器主机。
haproxy 实现了一种事件驱动、单一进程模型,此模型支持非常大的并发连接数。多进程或多线程模型受内存、系统调度器以及无处不在的锁限制,很少能处理数千并发连接。事件驱动模型因为在有更好的资源和时间管理的用户空间(User-Space)实现所有这些任务,所以没有这些问题。此模型的弊端是,在多核系统上,这些程序通常扩展性较差,这就是为什么必须对其进行优化以使每个 CPU 时间片(Cycle)做更多的工作。
haproxy 特别适用于那些负载特大的 web 站点,这些站点通常又需要会话保持或七层处理。haproxy 运行在当前的硬件上,完全可以支持数以万计的并发连接。并且它的运行模式使得它可以很简单安全地整合进当前架构中,同时可以保护 web 服务器不被暴露到网络上。
haproxy 软件引入了 frontend、backend 的功能,frontend(acl 规则匹配)可以根据任意 HTTP 请求头做规则匹配,然后把请求定向到相关的 backend(server pools,等待前端把请求转过来的服务器组)。通过 frontend 和 backend,我们可以很容易的实现 haproxy 的七层代理功能。
二、安装配置
1. 基本环境
OS:CentOS Linux release 7.2.1511 (Core)
172.16.1.126:Heartbeat + haproxy + MySQL Replication Master
172.16.1.127:Heartbeat + haproxy + MySQL Replication Master
172.16.1.100:VIP
整体架构图如图1 所示。
2. 配置 MySQL 双主复制
所谓 MySQL 双主复制,就是两个 MySQL 服务器都能读能写,数据记录通过二进制传达给对方从而保持数据的一致性。就本例简而言之,172.16.1.126 到 172.16.1.127 的主从复制 + 172.16.1.127 到 172.16.1.126 的主从复制 = 172.16.1.126、172.16.1.127的双主复制。双主复制的详细配置步骤可以参考这篇文章:MySQL主从复制与主主复制 - 那一叶随风 - 博客园,这里从略。
与主从复制相比,双主复制需要注意以下三个参数的设置:
- log_slave_updates:要设置为 true,将复制事件写入本机 binlog。一台服务器既做主库又做从库时此选项必须要开启。
- auto_increment_offset 和 auto_increment_increment:为避免自增列冲突,需要设置这两个参数,例如在双主复制中,可以配置如下:
# masterA自增长ID auto_increment_offset = 1 auto_increment_increment = 2 #奇数ID # masterB自增加ID auto_increment_offset = 2 auto_increment_increment = 2 #偶数ID
3. 安装配置 haproxy
以下步骤用 root 用户在 172.16.1.126、172.16.1.127 两个主机上执行。
(1)从以下地址下载 haproxy 源码
https://www.haproxy.org/download/1.8/src/haproxy-1.8.12.tar.gz
(2)创建 haproxy 运行账户和组
groupadd haproxy #添加haproxy组
useradd -g haproxy haproxy -s /bin/false #创建账户haproxy并加入到haproxy组
(3)解压
tar zxvf haproxy-1.8.12.tar.gz
cd haproxy-1.8.12
(4)查看操作系统版本信息
[root@hdp3~/haproxy-1.8.12]#uname -r
3.10.0-327.el7.x86_64
[root@hdp3~/haproxy-1.8.12]#
(5)编译与安装
make TARGET=linux3100 CPU=x86_64 PREFIX=/usr/local/haprpxy
make install PREFIX=/usr/local/haproxy
参数说明:
- TARGET:使用 uname -r 查看内核,如:2.6.18-371.el5,此时该参数就为 linux26。kernel 等于 2.6.28 的用:TARGET=linux2628,等等。
- CPU:使用 uname -r 查看系统信息,如 x86_64 GNU/Linux,此时该参数就为 x86_64。
- PREFIX:指定 haprpxy 安装路径。
(6)设置 haproxy
mkdir -p /usr/local/haproxy/conf #创建配置文件目录
mkdir -p /etc/haproxy #创建配置文件目录
touch /usr/local/haproxy/conf/haproxy.cfg #创建配置文件
ln -s /usr/local/haproxy/conf/haproxy.cfg /etc/haproxy/haproxy.cfg #添加配置文件软连接
cp -r /root/haproxy-1.8.12/examples/errorfiles /usr/local/haproxy/errorfiles #拷贝错误页面
ln -s /usr/local/haproxy/errorfiles /etc/haproxy/errorfiles #添加软连接
mkdir -p /usr/local/haproxy/log #创建日志文件目录
touch /usr/local/haproxy/log/haproxy.log #创建日志文件
ln -s /usr/local/haproxy/log/haproxy.log /var/log/haproxy.log #添加软连接
cp /root/haproxy-1.8.12/examples/haproxy.init /etc/rc.d/init.d/haproxy #拷贝开机启动文件
chmod +x /etc/rc.d/init.d/haproxy #添加脚本执行权限
chkconfig haproxy on #设置开机启动
ln -s /usr/local/haproxy/sbin/haproxy /usr/sbin #添加软连接
(7)配置 haproxy 参数
编辑 /usr/local/haproxy/conf/haproxy.cfg,内容如下:
global
log 127.0.0.1 local2 # 日志定义级别(err | warning | info | debug)
chroot /usr/local/haproxy # 当前工作目录
pidfile /var/run/haproxy.pid # 进程id
maxconn 4000 # 最大连接数
user haproxy # 运行改程序的用户
group haproxy
daemon # 后台形式运行
stats socket /usr/local/haproxy/stats
defaults
mode tcp # haproxy运行模式(http | tcp | health)
log global # 采用全局定义的日志
option dontlognull # 不记录健康检查的日志信息
option redispatch # serverId对应的服务器挂掉后,强制定向到其它健康的服务器
retries 3 # 三次连接失败则服务器不用
timeout http-request 10s
timeout queue 1m
timeout connect 10s # 连接超时
timeout client 1m # 客户端超时
timeout server 1m # 服务器超时
timeout http-keep-alive 10s
timeout check 10s # 心跳检测
maxconn 600 # 最大连接数
listen stats # 配置haproxy状态页(用来查看的页面)
mode http
bind :8888
stats enable
stats hide-version # 隐藏haproxy版本号
stats uri /haproxyadmin?stats # 一会用于打开状态页的uri
stats realm Haproxy\ Statistics # 输入账户密码时的提示文字
stats auth admin:admin # 用户名:密码
frontend main
bind 0.0.0.0:6603
# 使用6603端口。监听前端端口(表示任何ip访问6603端口都会将数据轮番转发到mysql服务器群组中)
default_backend mysql # 后端服务器组名
backend mysql
balance roundrobin # 使用轮询方式调度
server mysql1 172.16.1.126:3306 check port 3306 maxconn 300
server mysql2 172.16.1.127:3306 check port 3306 maxconn 300
(8)启动日志
编辑 /etc/rsyslog.conf 文件,去掉以下两行的注释,并在其后添加一行:
# Provides TCP syslog reception
$ModLoad imtcp # 去掉注释
$InputTCPServerRun 514 # 去掉注释
local2.* /var/log/haproxy.log # 添加此行
(9)启动 haproxy
systemctl start haproxy
systemctl status haproxy -l
systemctl stop haproxy
systemctl restart haproxy
此时打开浏览器输入 172.16.1.126:8888/haproxyadmin?stats,用 admin/admin 登陆后如图2 所示,表明安装 haproxy 成功。
4. 安装配置 Heartbeat
Heartbeat 的源码编译安装需要依次安装 Cluster Glue、Resource Agents 和 Heartbeat 三个软件,并且三个软件要安装在相同目录下。详细安装配置步骤参考:https://blog.youkuaiyun.com/wzy0623/article/details/81188814#二、安装Heartbeat。
这里需要注意的是 haresources 的设置。在上一篇中,我们建立了一个名为 mysql 的脚本,它调用 remove_slave.sh。当 Heartbeat 主机获得资源时,将自动把 MySQL 主从复制中的 slave 置为 master。这次我们用 /usr/local/heartbeat/etc/ha.d/haresources 来启动 haproxy,文件内容只有如下一行:
hdp3 172.16.1.100 haproxy
而 /usr/local/heartbeat/etc/ha.d/resource.d/haproxy 文件中也只有如下一行:
/etc/init.d/haproxy restart
资源只有 VIP,我们用这种配置保证 haproxy 的高可用性。当初始启动 Heartbeat 后,VIP 绑定在 172.16.1.126 上。
5. 创建 MySQL 服务检测脚本
与上一篇的实验类似,这里保留自定义 mysql_check 脚本用于检查本机 MySQL 的服务器状态。脚本内容与部署参见https://blog.youkuaiyun.com/wzy0623/article/details/81188814#4.%20创建MySQL服务检测脚本。
三、功能测试
1. 验证 haproxy 的负载均衡轮询策略
用客户端连接 VIP,并多次执行 MySQL 查询,可以看到查询请求依次被发送到在两个主机上执行。
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 126 |
+---------------+-------+
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 127 |
+---------------+-------+
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 126 |
+---------------+-------+
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 127 |
+---------------+-------+
C:\WINDOWS\system32>
2. 验证 MySQL 的高可用性
在 172.16.1.126 上杀掉 MySQL:
pkill -9 mysqld
用客户端连接 VIP,并多次执行 MySQL 查询,可以看到查询请求都被发送到 172.16.1.127。一台 MySQL 宕机不影响应用的正常使用,保证了 MySQL 服务的高可用性。
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 127 |
+---------------+-------+
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 127 |
+---------------+-------+
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 127 |
+---------------+-------+
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"\
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 127 |
+---------------+-------+
C:\WINDOWS\system32>
3. 验证宕机重新上线后自动添加到 haproxy 中
在 172.16.1.126 再次启动 MySQL,并重启 haproxy 服务:
service mysql start
systemctl restart haproxy
用客户端连接 VIP,并多次执行 MySQL 查询,可以看到查询请求依次被发送到在两个主机上执行。
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"\
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 127 |
+---------------+-------+
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"\
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 126 |
+---------------+-------+
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"\
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 127 |
+---------------+-------+
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"\
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 126 |
+---------------+-------+
C:\WINDOWS\system32>
4. 验证 haproxy 的高可用性
初始 VIP 在 126,126 的 haproxy 启动,127 的 haproxy 停止。客户端访问:
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"\
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 127 |
+---------------+-------+
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"\
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 126 |
+---------------+-------+
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"\
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 127 |
+---------------+-------+
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"\
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 126 |
+---------------+-------+
C:\WINDOWS\system32>
停止 172.16.1.126 上的 MySQL 服务:
pkill -9 mysqld
mysql_check 会检测到本机 MySQL 服务宕机,触发停止 heartbeat 服务。VIP 漂移到 172.16.1.127,同时 172.16.1.127 上的 haproxy 被拉起:
[root@hdp4~]#ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
link/ether 00:50:56:a5:49:7f brd ff:ff:ff:ff:ff:ff
inet 172.16.1.127/24 brd 172.16.1.255 scope global ens160
valid_lft forever preferred_lft forever
inet 172.16.1.100/24 brd 172.16.1.255 scope global secondary ens160:0
valid_lft forever preferred_lft forever
inet6 fe80::250:56ff:fea5:497f/64 scope link
valid_lft forever preferred_lft forever
[root@hdp4~]#
[root@hdp4~]#
[root@hdp4~]#/etc/init.d/haproxy status
● haproxy.service - SYSV: HA-Proxy is a TCP/HTTP reverse proxy which is particularly suited for high availability environments.
Loaded: loaded (/etc/rc.d/init.d/haproxy)
Active: active (running) since Thu 2018-07-26 11:25:47 CST; 26s ago
Docs: man:systemd-sysv-generator(8)
Process: 532199 ExecStop=/etc/rc.d/init.d/haproxy stop (code=exited, status=0/SUCCESS)
Process: 532205 ExecStart=/etc/rc.d/init.d/haproxy start (code=exited, status=0/SUCCESS)
Main PID: 532210 (haproxy)
CGroup: /system.slice/haproxy.service
└─532210 /usr/sbin/haproxy -D -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid
Jul 26 11:25:47 hdp4 systemd[1]: Starting SYSV: HA-Proxy is a TCP/HTTP reverse proxy which is particularly suite...ts....
Jul 26 11:25:47 hdp4 haproxy[532205]: Starting haproxy: [ OK ]
Jul 26 11:25:47 hdp4 systemd[1]: Started SYSV: HA-Proxy is a TCP/HTTP reverse proxy which is particularly suited...ents..
Hint: Some lines were ellipsized, use -l to show in full.
[root@hdp4~]#
客户端访问不受影响:
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"\
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 126 |
+---------------+-------+
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"\
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 127 |
+---------------+-------+
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"\
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 126 |
+---------------+-------+
C:\WINDOWS\system32>mysql -utest -p123456 -P6603 -h172.16.1.100 -e "show variables like 'server_id'"\
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 127 |
+---------------+-------+
C:\WINDOWS\system32>
在 172.16.1.126 上再次启动 heartbeat,VIP 并不发生漂移,仍然停留在 172.16.1.127 上。
[root@hdp3~]#systemctl start heartbeat
[root@hdp3~]#
[root@hdp3~]#
[root@hdp3~]#ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:50:56:a5:0f:77 brd ff:ff:ff:ff:ff:ff
inet 172.16.1.126/24 brd 172.16.1.255 scope global ens32
valid_lft forever preferred_lft forever
inet6 fe80::250:56ff:fea5:f77/64 scope link
valid_lft forever preferred_lft forever
[root@hdp3~]#
四、总结
- 之所以要使用 MySQL 双主复制而不是主从复制,是因为本方案中并没有涉及读写分离,而是在两个等价的 MySQL 服务器之间做读写负载均衡。
- Heartbeat 实现了 haproxy 的 HA,避免了 haproxy 的单点故障,出现故障时可以自动切换到正常的节点。
- haproxy 服务器提供了负载均衡的作用,将用户请求分发到多个 backend。同时,一台 MySQL 故障并不会影响整个集群,因为 haproxy 会检测 backend 的状态,并据此自动添加或删除集群中的 MySQL 服务。
- 如本例的配置,需要考虑单台 MySQL 服务器的负载最好不要超过 50%,否则一旦某台 MySQL 服务器故障,可能出现另一台正常 MySQL 不堪重负的情况。
- 只采用本例的配置无法处理“脑裂”问题。例如,当心跳线闪断,slave 获得 VIP,而此时 master 除了与 slave 失去联系,本身并无任何问题,也绑定同一 VIP。当心跳恢复正常时,就会出现 VIP 的冲突等问题。脑裂是一个比较复杂的话题,通常需要引入其它插件,并且“多管齐下”才能彻底解决。