背景:
当前 MySQL 的结构是一主两从,除了主从复制关系,各自独立。现将其做成一个主备高可用架构。
从库高可用 KeepAlived
如果不做从库的高可用,则应用只能通过从库 IP 去连接从库,当所连从库出问题时,手动修改连接另外一个从库。
这儿我们通过 KeepAlived 来实现两台 Slave 的高可用。将两台 Slave 主机对外呈现一个 VIP,这样应用就可以去连接 VIP。
VIP 平时出现在某台 slave 主机上,当该台 Slave 主机出现问题时,VIP 迁移到另外一台 Slave 主机上。这样就保证了应用的正常连接。
1、在两台 slave 节点上安装 keepalived:
yum install keepalived
2、修改 keepalived 配置文件(两个 slave 完全一样):
! Configuration File for keepalived
global_defs {
notification_email {
xuwang10086@qq.com
}
notification_email_from xuwang10086@qq.com
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id LVS_CY
# 表示keepalived服务器的一个标识,是发邮件时显示在邮件主题中的信息
}
vrrp_instance VI_MYSQL_READ {
state BACKUP
#如果定义为MASTER,主起来后无论如何都会抢占,如果不想抢占,这儿都设置为BACKUP
interface eth0
#监控eth0号端口
virtual_router_id 1
#虚拟路由id号为1,id号唯一,这个id决定了多播的MAC地址
priority 100
#设置本节点的优先级,如果是MASTER-BACKUP,master的优先级要比backup的优先级别高,数值要大
advert_int 1
#检查间隔,默认为1秒
nopreempt
#当高优先级节点活动来后,是否抢占,为避免脑裂,不建议抢占,对于MySQL,活过来的Slave可能已经不同步,建议手工处理,不抢占
authentication {
auth_type PASS
auth_pass 3307
}
virtual_ipaddress {
10.16.10.247/22
#设置虚拟的ip, 这个ip是以后对外提供服务的ip。如果有多个VIP,继续换行填写
}
}
3、测试:
将两个 slave 的 keepalived 服务启动起来,然后通过 service keepalived restart 启停 keepalived 来测试 VIP 的漂移。可以发现 VIP 正常漂移且不抢占。
4、自动健康检测:
但是注意,上面我们是通过手动停止 keepalived 服务来让 VIP 快速切换的,实际中我们更关心的是当 MySQL 出现问题时就应该切换。我们可以通过检查脚本来检查 MySQL 的状态,当 MySQL 出现问题时,就停止 keepalived 以切换 VIP。
在 keepalived 的配置文件中增加如下检查脚本:
vrrp_script check_mysqld {
script "/root/keepalived_check_mysql.sh"
interval 5 # 5秒
}
vrrp_instance VI_MYSQL_READ {
……
检查脚本内容如下:
# cat /root/keepalived_check_mysql.sh
#!/bin/sh
MYSQL=/usr/bin/mysql
MYSQL_USER=root
MYSQL_PASSWORD=xuwang
MYSQL_SOCKET=/data/app/cymysql/mysql.sock
CHECK_TIME=3
MYSQL_OK=1 # 1 for working, 0 for not working
function check_mysql_helth(){
$MYSQL -u $MYSQL_USER -p${MYSQL_PASSWORD} -S ${MYSQL_SOCKET} -e "show status;" >/dev/null 2>&1
if [ $? = 0 ] ;then
MYSQL_OK=1
else
MYSQL_OK=0
fi
return $MYSQL_OK
}
while [ $CHECK_TIME -ne 0 ]
do
let "CHECK_TIME -= 1"
check_mysql_helth
if [ $MYSQL_OK = 1 ] ; then
CHECK_TIME=0
exit 0
fi
if [ $MYSQL_OK -eq 0 ] && [ $CHECK_TIME -eq 0 ]; then
/etc/init.d/keepalived stop
exit 1
fi
sleep 1
done
# chmod +x keepalived_check_mysql.sh
5、测试
确保数据库处于开启状态,然后重启 keepalived 服务(注意一定要先启数据库,再启keepalived)。然后停止 VIP 所在主机的数据库服务,查看 keepalived 服务是否会自动停止及 VIP 是否发生迁移。
从库负载均衡 LVS
我们可以通过 LVS 来实现负载均衡。
Linux 2.6 以上内核版本都已自带对 LVS 的支持:
# modprobe -l | grep ipvs
# lsmod | grep ip_vs
以前是通过 lvsadm 命令(红帽自带,在HA目录中)来配置 LVS 规则,指定 VIP 和真实服务器及转发规则。现在更多的是通过keepalived 可以实现 lvsadm 规则的自动配置。
我们的拓扑结构如下:
10.16.10.237(KeepAlived + LVS)
|
|
-----------------
| |
| |
MySQL(RealServer 152) MySQL(RealServer 153)
1、在 KeepAlived + LVS 节点上配置 KeepAlived
#yum install keepalived
# cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
notification_email {
xuwang10086@qq.com
}
notification_email_from xuwang10086@qq.com
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id LVS_CY
# 表示keepalived服务器的一个标识,是发邮件时显示在邮件主题中的信息
}
vrrp_instance VI_MYSQL_READ {
state MASTER
#如果定义为MASTER,主起来后无论都会抢占,如果不想抢占,这儿都设置为BACKUP
interface eth0
#监控eth0号端口
virtual_router_id 1
#虚拟路由id号为58,id号唯一,这个id决定了多播的MAC地址
priority 100
#设置本节点的优先级,master的优先级要比backup的优先级别高,数值要大
advert_int 1
#检查间隔,默认为1秒
nopreempt
#当主节点活动来后,是否抢占
authentication {
auth_type PASS
auth_pass 3307
}
virtual_ipaddress {
10.16.10.247/22
#设置虚拟的ip, 这个ip是以后对外提供服务的ip。如果有多个VIP,继续换行填写
}
}
virtual_server 10.16.10.247 3307 {
delay_loop 6
lb_algo rr
lb_kind DR
#nat_mask 255.255.252.0
#persistence_timeout 5
protocol TCP
real_server 10.16.10.152 3307 {
weight 2
TCP_CHECK {
connect_timeout 5
nb_get_retry 3
delay_before_retry 3
connect_port 3307
}
}
real_server 10.16.10.153 3307 {
weight 2
TCP_CHECK {
connect_timeout 5
nb_get_retry 3
delay_before_retry 3
connect_port 3307
}
}
}
# service keepalived restart
做样一来,KeepAlived 就自动把 LVS 规则配置好了。使用 ip addr 可以看 VIP 了。如果感兴趣,可以安装 ipvsadm 软件后使用 ipvsadm -ln 查看。
# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.16.10.247:3307 rr
-> 10.16.10.152:3307 Route 2 0 0
-> 10.16.10.153:3307 Route 2 0 0
在上面的配置文件中,lb_algo rr 表示 LVS scheduler,共有 rr|wrr|lc|wlc|lblc|sh|dh 几种(网上有人说8种)。
lb_kind 表示 LVS forwarding method,共有NAT|DR|TUN。
DR模式表示直接路由模式,对于数据库(及大多数应用场景)来说,个人认为DR模式应为首选。请求发起方向LVS发起请求,LVS转派给后端服务器,那结果如何返回给请求发起方呢?针对这个问题,LVS提供了两套响应方案:一套是由各Slave节点响应请求,并直接将数据返回给请求发起方,LVS相当于纯工作转派,这种就是DR模式;第二套方案也得Slave节点响应请求,不过在返回数据时,Slave不会将数据直接返回给请求的发起方,而是反馈给LVS,由LVS统一返回到请求发起方,这种即是NAT模式,相当于LVS是统一入口,出入都得从它走。
2、在RealServer上配置(两台一样)
创建一个如下的脚本:
# cat lvs_dr.sh
#!/bin/bash
iface="lo:0"
vip=10.16.10.247
mask=255.255.255.255
#ifconfig ${iface} $vip broadcast $vip netmask 255.255.255.255 up
case $1 in
start)
ifconfig $iface $vip netmask $mask broadcast $vip up
route add -host $vip dev $iface
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
;;
stop)
ifconfig $iface down
route del $vip >/dev/null 2>&1
echo 0 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 0 > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo 0 > /proc/sys/net/ipv4/conf/all/arp_announce
echo 0 > /proc/sys/net/ipv4/conf/lo/arp_announce
;;
*)
echo "Usage:$(basename $0) start|stop"
exit 1
;;
esac
执行脚本的启动功能:
# sh lvs_dr.sh start
下面解释一下脚本的作用:
我们前面说过,LVS只负责转发请求,具体响应请求及返回数据,是由 RealServer 来做。这个流程大家应该理解了,不过这个逻辑在具体执行时,不知道大家有没有想过,服务端会怎么处理这种情况呢?要知道,LVS 和 RealServer 可是不同的服务器,客户端的请求是发给 LVS 的,而 LVS 顺手就扔给 RealServer 了,它扔的倒是轻巧,不过 RealServer 收到请求可能就糊涂了,这个不是找它的呀。此外,就算 RealServer 是个热心肠,甭管是不是来找它的,它都帮着响应,那客户端可能就会觉着奇怪了,为啥前后不是一个人呢。
解决办法:
客户端请求的不就是 IP 嘛,我们只要让 LVS 节点及 RealServer 节点,都持有客户端请求的那个 IP 地址不就好了嘛。当然,这又引来一个新的疑问,这样设定,岂不是会发生 IP 地址冲突。这是个好问题,我们前面曾经提到过,其实老板还是只有一位,秘书们的老板称号只是名义上的,而不是实际的。LVS 作为老板的地位是不可动摇的,在我们这个示例中,它必然持有 192.168.30.242 这一IP地址,所有访问这一 IP 的请求,都将由它来响应。那么,女秘书们的名义地位怎么体现呢?这不得不又提到网卡别名,我们确实需要为 RealServer 们绑定 192.168.30.242 这一IP地址,但不是在标准网卡设置,而是将之绑定到 lo环回接口上面就好了。
切换到 RealServer 节点上,执行如下命令:
# /sbin/ifconfig lo:242 192.168.30.242 broadcast 192.168.30.242 netmask 255.255.255.255
地址绑定无误,此外,为了避免有人呼叫 192.168.30.242 时, RealServer 四川瞎嚷嚷,我们要禁用 lo 环回接口中的 ARP 广播,执行操作如下:
# echo "1">/proc/sys/net/ipvd/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/ipe4/conf/all/arp_announce
注意,上述操作需要在每个RealServer节点上执行。
3、测试负载均衡
在客户端访问LVS,注意,不能在 RealServer 上通过 mysql 对 VIP 发起访问,除非访问 VIP 时被 LVS 分发到发出请求的 RealServer 自己:
# mysql -h 10.16.10.247 -P 3307 -uroot –pxuwang
mysql> show variables like 'server_id'; <--- 返回33307
# mysql -h 10.16.10.247 -P 3307 -uroot –pxuwang
mysql> show variables like 'server_id'; <--- 返回23307
可见已经轮循了。
现在关闭一台数据库服务器,会发现 LVS 会自动将故障的R ealSever 排除在派发主机之外(健康检查),这个一来,通过 VIP 就始终连接的是剩下的那台正常服务器了。当主机恢复正常后,又正常轮循。另外一台服务器故障也是同样的道理。这也就有了高可用的特性。
从库高可用+负载均衡
在上面的拓扑中,keepalived 是一个单点,我们通过为其配置一个 backup keepalived 来保证 keepalived 的高可用。
拓扑结构如下:
这个很简单的,只需要将10.16.10.237上面的keepalived.conf文件复制到10.16.10.238上,修改如下内容,然后启动keepalived服务即可:
state BACKUP
priority 90
需要注意的是不能把LVS和RealServer放在同一台主机上,即上图中,不能把左边的两台和右边的两台主机分别合二为一。如果想这样做,需要下面的特殊处理。
LVS与RealServer部署在同一台主机的特殊考虑
在237的keepalived.conf文件复制到152上,将238的keepalived.conf文件复制到153上,启动keepalived服务后,通过VIP:3307来访问数据库,前3次正常,第4次卡住,如此循环……
使用ipvsadm -lcn查看的时候发现如下,两台服务器在“打皮球”.
# ipvsadm -lcn
IPVS connection entries
pro expire state source virtual destination
TCP 00:55 FIN_WAIT 10.16.10.238:15745 10.16.10.247:3307 10.16.10.153:3307
TCP 00:28 FIN_WAIT 10.16.10.238:15744 10.16.10.247:3307 10.16.10.152:3307
TCP 01:00 SYN_RECV 10.16.10.238:15754 10.16.10.247:3307 10.16.10.153:3307
TCP 01:55 FIN_WAIT 10.16.10.238:15748 10.16.10.247:3307 10.16.10.152:3307
# ipvsadm -lcn
IPVS connection entries
pro expire state source virtual destination
TCP 00:59 SYN_RECV 10.16.10.238:15754 10.16.10.247:3307 10.16.10.152:3307
查看两台LVS的转发规则:
# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.16.10.247:3307 rr
-> 10.16.10.152:3307 Local 2 0 0
-> 10.16.10.153:3307 Route 2 0 0
# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.16.10.247:3307 rr
-> 10.16.10.152:3307 Route 2 0 0
-> 10.16.10.153:3307 Local 2 0 0
在这种架构中,只要停止我的备份的 keepalived ,就能正常的工作,不然一定有其中一台数据库连接到最后就中断停止在那个地方不动了。通过抓包,大量包被不断的转发来转发去。
我们来分析一下原因:
客户端第一次请求VIP:3307(即LVS master 152),master将请求分发至真实服务器152,正常。
客户端第二次请求VIP:3307(即LVS master 152),master将请求分发至真实服务器153(因为rr)。注意153同时也是LVS backup,转发到153的数据包将不能直接地被MySQL所接收,数据包会首先先经过 ip_vs()。尽管其没有VIP,但是其转发规则仍然生效的。所以LVS backup 会根据其转发规则将数据分发至真实服务器153。
客户端第三次请求VIP:3307(即LVS master 152),master将请求分发至真实服务器152(因为rr),正常。
客户端第四次请求VIP:3307(即LVS master 152),master将请求分发至真实服务器153(因为rr)。注意153同时也是LVS backup,转发到153的数据包将不能直接地被MySQL所接收,数据包会首先先经过 ip_vs()。尽管其没有VIP,但是其转发规则仍然生效的。所以LVS backup 会根据其转发规则将数据分发至152。
这样一来,152将数据丢给153,153又将数据丢回了152,形成死循环。随着时间的推移,不但不能正常的处理连接,您的服务器也会崩溃,在他们中间或后端不断的反复连接。
解决方案:对不是来自其它LVS服务器的数据包打标记,在LVS中配置只对打了标记的包进行转发。
在152上(主LVS上):
iptables -t mangle -I PREROUTING -d 10.16.10.247 -p tcp -m tcp --dport 3307 -m mac ! --mac-source 00:50:56:82:51:7f -j MARK --set-mark 0x3
MAC地址是153(从LVS)的MAC地址,表示只要不是153过来的对VIP:3307的请求数据都打上0x3标记。
在153上(从LVS上):
iptables -t mangle -I PREROUTING -d 10.16.10.247 -p tcp -m tcp --dport 3307 -m mac ! --mac-source 00:50:56:82:0c:92 -j MARK --set-mark 0x4
MAC地址是152(主LVS)的MAC地址,表示只要不是152过来的对VIP:3307的请求数据都打上0x4标记。
修改主LVS的keepalived.conf文件:
将原来的
virtual_server 10.16.10.247 3307 {
}
修改为:
virtual_server fwmark 3 {
……
}
修改从LVS的keepalived.conf文件:
将原来的
virtual_server 10.16.10.247 3307 {
}
修改为:
virtual_server fwmark 4 {
……
}