目录
上一篇我们使用 Keepalived 的 HA 功能,实现 MySQL 主从复制的自动故障切换。它的工作原理是:初始将 MySQL 的主从两个主机赋予不同的优先级别,当 Keepalived 启动时,会将 VIP 绑定到高优先级的主库上。在 Keepalived 中调用自定义脚本 check_run,每分钟检查一次本机 MySQL 的服务器状态,如果 MySQL 不可用,则杀掉本机的 keepalived 进程。Keepalived 每秒钟会检查一次本机的 keepalived 进程,如果进程不存在,则将 VIP 绑定到另一台机器上,如果这台机器原来是从库,则同时调用 master.sh 脚本执行从库切换为主库的操作。
本篇我们将做另一个实验,利用 Keepalived 的 IPVS 功能,调用 LVS 实现 MySQL 双主复制的读写负载均衡,同时保证负载均衡器和 MySQL 的高可用性。实验环境如图1 所示。
图1
一、Keepalived 和 LVS 简介
1. Keepalived 简介
参见“使用 Keepalived 实现 MySQL 主从高可用”。
2. LVS 简介
LVS(Linux Virtual Server)是一个高可用、虚拟的服务器集群系统。本项目在 1998 年 5 月由章文嵩博士成立,是中国国内最早出现的自由软件项目之一。LVS 主要用于多服务器的负载均衡,作用于网络层。LVS 构建的服务器集群系统中,前端的负载均衡层被称为 Director Server,后端提供服务的服务器组层被称为 Real Server。通过下图可以大致了解 LVS 的基础架构。
图2
LVS 由 ipvs 和 ipvsadm 两部分组成:
- ipvs:ipvs 是工作在内核空间 netfilter 的 input 链上的框架,通过用户空间工具进行管理,其中是真正生效实现调度的代码。
- ipvsadm:ipvsadm 负责为 ipvs 内核框架编写规则,是管理配置内核中 ipvs 程序的用户空间的管理工具。
LVS 是工作在 Linux 内核空间的 tcp/ip 栈的应用程序,其程序名称为 ipvs。ipvs 会监听 input 链上的请求,一旦请求的是集群服务的话,ipvs 钩子函数会将请求拉出并进行报文修改,强制转发到 postrouting 处理,关系如图3 所示。
图3
在客户端看来,LVS 就是一个真实的应用服务器。客户端向 LVS 发送请求信息,LVS 接收数据报文至内核空间,工作在 input 链上的 ipvs 模块会判断用户请求是不是定义的后端服务器,如果用户请求的就是定义的后端集群服务,数据报文传送到 input 链上时,input 链会强行将数据报文转发给 postrouting,postrouting 将数据报文传送给后端真实服务器。LVS 的特点在于超强的分流功能,但它只能负责调度流量的去向,没有办法实现在业务层分流负载。
LVS 可以独立使用,但更普遍的做法是与 Keepalived 一起使用。LVS 提供负载均衡,Keepalived 提供健康检查,故障转移,提高系统的可用性。Keepalived 中的 LVS 配置包括虚拟主机组(Virtual Server Group)和虚拟主机(Virtual Server)。这些配置会传递给 ipvsadm 作为参数。采用这样的架构以后,很容易对现有系统进行扩展,在后端添加或者减少 realserver 后,只需要更改 Keepalived 配置文件中的 LVS 部分即可。
二、安装配置
环境:
172.16.1.124:Keepalived + LVS Master
172.16.1.125:Keepalived + LVS Slave
172.16.1.126:MySQL Replication Master
172.16.1.127:MySQL Replication Master
172.16.1.100:VIP
在本环境中的 RealServer 就是两台 MySQL 服务器,LVS 和 RealServer 分别使用两台主机。当 LVS 架构中的 Director Server 和 RealServer 工作在同一台机器上时,需要注意 SYN_RECV 问题,即会出现两台 director 无限循环转发请求的情况。对该问题的讨论参见“怎么样让LVS和realserver工作在同一台机器上”。
在 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
1. 下载安装 LVS
在 172.16.1.124 和 172.16.1.125 上用 root 用户执行以下命令:
yum -y install ipvsadm
2. 下载安装 Keepalived
在 172.16.1.124 和 172.16.1.125 上安装 Keepalived,详细步骤参见“使用 Keepalived 实现 MySQL 主从高可用”。
3. Keepalived 配置
172.16.1.124 初始为 keepalived 的 master,其上的 keepalived 配置文件如下:
[root@hdp1~]#more /etc/keepalived/keepalived.conf
global_defs {
router_id LVS_DEVEL
}
vrrp_sync_group VG1 {
group {
VI_1
}
}
vrrp_instance VI_1 {
state BACKUP
interface ens32
virtual_router_id 51
priority 100
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass 1234
}
virtual_ipaddress {
172.16.1.100
}
}
virtual_server 172.16.1.100 3306 { # 定义虚拟服务器,地址与上面的virtual_ipaddress相同
delay_loop 3 # 健康检查时间间隔,3秒
lb_algo rr # 负载均衡调度算法:rr|wrr|lc|wlc|sh|dh|lblc
lb_kind DR # 负载均衡转发规则:NAT|DR|TUN
# persistence_timeout 5 # 会话保持时间5秒,动态服务建议开启
protocol TCP # 转发协议protocol,一般有tcp和udp两种
#后端真实服务器,有几台就设置几个
real_server 172.16.1.126 3306 {
weight 1 # 权重越大负载分越大,0表示失效
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
connect_port 3306
}
}
real_server 172.16.1.127 3306 {
weight 1
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
connect_port 3306
}
}
}
[root@hdp1~]#
172.16.1.125 初始为 keepalived 的 slave,其上的 keepalived 配置文件如下:
[root@hdp2~]#more /etc/keepalived/keepalived.conf
global_defs {
router_id LVS_DEVEL
}
vrrp_sync_group VG1 {
group {
VI_1
}
}
vrrp_instance VI_1 {
state BACKUP
interface ens32
virtual_router_id 51
priority 90
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass 1234
}
virtual_ipaddress {
172.16.1.100
}
}
virtual_server 172.16.1.100 3306 { # 定义虚拟服务器,地址与上面的virtual_ipaddress相同
delay_loop 3 # 健康检查时间间隔,3秒
lb_algo rr # 负载均衡调度算法:rr|wrr|lc|wlc|sh|dh|lblc
lb_kind DR # 负载均衡转发规则:NAT|DR|TUN
# persistence_timeout 5 # 会话保持时间5秒,动态服务建议开启
protocol TCP # 转发协议protocol,一般有tcp和udp两种
#后端真实服务器,有几台就设置几个
real_server 172.16.1.126 3306 {
weight 1 # 权重越大负载分越大,0表示失效
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
connect_port 3306
}
}
real_server 172.16.1.127 3306 {
weight 1
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
connect_port 3306
}
}
}
[root@hdp2~]#
master 与 slave 的 keepalived 配置文件中只有 priority 设置不同,master 为 100,slave 为 90,其它全一样。配置文件是以块形式组织的,每个块都在 {} 包围的范围内,# 和 ! 开头的行都是注释。global_defs、vrrp_sync_group、vrrp_instance 部分的配置说明参见“使用 Keepalived 实现 MySQL 主从高可用”。
本例中没有配置 virtual_server_group。该配置段是可选的,目的是为了让一台 RealServer 的某个 Service 可以属于多个 Virtual Server,并且只做一次健康检查。下面重点说明 virtual server 段的配置。
- virtual_server:设置一个虚拟服务器,指定其虚拟 IP 和虚拟端口。
- delay_loop:指定服务轮询的时间间隔,单位是秒。
- lb_algo:指定 LVS 的调度算法,本例指定为 rr,即轮询。关于 LVS 所支持调度算法的说明,参见“LVS NAT,DR,TUN三种负载原理 - 孤岛鱼夫 - 博客园”。
- lb_kind:指定 LVS 转发模式,本例设置为 DR,这也是大多数生产环境的配置。关于 LVS 所支持转发模式的说明,参见“LVS NAT,DR,TUN三种负载原理 - 孤岛鱼夫 - 博客园”。
- persistence_timeout:指定以秒为单位的会话保持时间,这里做了注释,意为不保持,目的是为了方便查看后面测试 rr 负载均衡算法的效果。生产环境建议开启该参数。
- protocol:指定转发协议使用 TCP 还是 UDP。
- real_server:每台 RealServer 都需要一个 real_server 配置项,指定真实服务器的 IP 和端口。本例指定两个 MySQL 库的服务器地址和端口。
- weight:指定 RealServer 权重,默认为 1,0 为失效。
- TCP_CHECK:指定健康检查方式为 TCP。支持 HTTP_GET、SSL_GET、TCP_CHECK、SMTP_CHECK、MISC_CHECK、HTTP_GET、SSL_GET 等检查方式,每种方式包含自己的参数。
- connect_timeout:定义连接超时时间,单位是秒。
- nb_get_retry:指定重连次数。
- delay_before_retry:指定重连的时间间隔,单位是秒。
- connect_port:指定健康检查的端口。
4. 编写 RealServer 的网络配置脚本
在 172.16.1.126 和 172.16.1.127 上建立 /etc/init.d/realserver 文件,内容如下:
#!/bin/sh
VIP=172.16.1.100
. /etc/rc.d/init.d/functions
case "$1" in
# 禁用本地的ARP请求、绑定本地回环地址
start)
/sbin/ifconfig lo down
/sbin/ifconfig lo up
echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
/sbin/sysctl -p >/dev/null 2>&1
/sbin/ifconfig lo:0 $VIP netmask 255.255.255.255 up # 在回环地址上绑定VIP,设定掩码,与Direct Server上自身的IP保持通信
/sbin/route add -host $VIP dev lo:0
echo "LVS-DR real server starts successfully.\n"
;;
stop)
/sbin/ifconfig lo:0 down
/sbin/route del $VIP >/dev/null 2>&1
echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
echo "LVS-DR real server stopped.\n"
;;
status)
isLoOn=`/sbin/ifconfig lo:0 | grep "$VIP"`
isRoOn=`/bin/netstat -rn | grep "$VIP"`
if [ "$isLoON" == "" -a "$isRoOn" == "" ]; then
echo "LVS-DR real server has run yet."
else
echo "LVS-DR real server is running."
fi
exit 3
;;
*)
echo "Usage: $0 {start|stop|status}"
exit 1
esac
exit 0
执行下面的命令将该脚本加入开机自启动:
chmod +x /etc/init.d/realserver
echo "/etc/init.d/realserver" >> /etc/rc.d/rc.local
执行以下命令配置 realserver:
service realserver start
命令执行后 172.16.1.126 和 172.16.1.127 上的 IP 地址分别如图4、5 所示。
图4
图5
三、测试
1. 分别在 master 和 slave 上启动 keepalived 进程。
在 172.16.1.124 和 172.16.1.125 上执行以下命令:
/etc/init.d/keepalived start
2. 查看 master 和 slave 上的 VIP。
结果分别如图6、7 所示,可以看到 VIP 已经成功绑定到 172.16.1.124。
图6
图7
此时查看 LVS 集群状态,可以看到集群下有两个 RealServer,调度算法,权重等信息。ActiveConn 代表当前 RealServer 的活跃连接数。
[root@hdp1~]#ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.1.100:3306 rr
-> 172.16.1.126:3306 Route 1 0 0
-> 172.16.1.127:3306 Route 1 0 0
[root@hdp1~]#
3. 验证 LVS 负载均衡转发策略
MySQL 客户端使用 VIP 连接数据库,并查看所连接的数据库服务器 ID。可以看到,每次执行依次连接到 172.16.1.126 和 172.16.1.127 的 MySQL,证明是轮询策略产生的结果。
C:\WINDOWS\system32>mysql -uwxy -p123456 -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 -uwxy -p123456 -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 -uwxy -p123456 -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 -uwxy -p123456 -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 |
+---------------+-------+
4. 模拟 LVS 的 master 失效
停止 master 上的 keepalived 服务,在 172.16.1.124 上执行以下命令:
/etc/init.d/keepalived stop
再次查看 172.16.1.124、172.16.1.125 绑定的 VIP 分别如图8、9 所示。可以看到 VIP 已经漂移到 172.16.1.125 上,它成为了新的 master。
图8
图9
此时连接 MySQL,负载均衡不受影响。
C:\WINDOWS\system32>mysql -uwxy -p123456 -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 -uwxy -p123456 -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 -uwxy -p123456 -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 -uwxy -p123456 -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 |
+---------------+-------+
此时再次启动 172.16.1.124 上 keepalived 服务,它已经变为 slave,而且并不会去抢占 master,这是由 nopreempt 参数决定的。
5. 模拟 mysqld crash,在 172.16.1.126 上执行以下命令。
pkill -9 mysqld
此时,LVS 检测到了 172.16.1.126 上的 MySQL Server 宕机,集群自动剔除了故障节点。此时集群中只有一个 RealServer 的地址,即 172.16.1.127:3306。
[root@hdp1~]#ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.1.100:3306 rr
-> 172.16.1.127:3306 Route 1 0 0
[root@hdp1~]#
此时连接 MySQL,可以看到应用不受影响,但只连接到一台 MySQL 服务器。
C:\WINDOWS\system32>mysql -uwxy -p123456 -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 -uwxy -p123456 -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 -uwxy -p123456 -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 -uwxy -p123456 -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 |
+---------------+-------+
重新启动 172.16.1.126 上的 MySQL 后,LVS 自动将故障节点自动加入集群。
[root@hdp1~]#ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.1.100:3306 rr
-> 172.16.1.126:3306 Route 1 0 0
-> 172.16.1.127:3306 Route 1 0 0
[root@hdp1~]#
四、总结
- 之所以要使用 MySQL 双主复制而不是主从复制,是因为本方案中并没有涉及读写分离,而是在两个等价的 MySQL 服务器之间做读写负载均衡。
- 本例中除了简单配置外,没有任何自定义的脚本。
- Keepalived 利用 VRRP 实现了 LVS 的 HA,避免了 LVS 服务器的单点故障,出现故障时可以自动切换到正常的节点。
- LVS 服务器提供了负载均衡的作用,将用户请求分发到多个 RealServer。同时,一台 RealServer 故障并不会影响整个集群,因为 LVS 会检测 RealServer 的状态,并据此自动添加或删除集群中的 RealServer 服务。
- 如本例的配置,需要考虑单台 MySQL 服务器的负载最好不要超过 50%,否则一旦某台 MySQL 服务器故障,可能出现另一台正常 MySQL 不堪重负的情况。