keepalived+LVS使用两台机器实现mysql双主负载均
前段时间看了《大型网站系统与Java中间件实践》这本书,了解到大型网站构架的演进,大致上来说也是单机走向分布式集群来分摊访问压力。书中提到了很多常见的场景,比如数据库方面,当压力变大时可以实现读写分离、分库分表等,总体上是给出了通用解决方案和思路。
分享下《大型网站系统与Java中间件实践》PDF,网上找不到带目录的,简单的用Adobe自己添加了目录(百度云地址)
网上搜mysql集群高可用方案结果是一大堆,也没有说哪个方案是相对比较好。看了很多文章介绍初步了解到下面的几种情况:
- 数据库一主多从实现读写分离
mysql-proxy
:原理上应该是通过lua脚本解析sql,写主库读从库,还能配置负载均衡
mycat
:也是统一拦截sql,还支持分表分库、读写分离,负载均衡等,好像比较强大。 - 负载均衡,把请求分发以分摊服务器压力
LVS
:LVS(Linux Virtual Server)是一个虚拟的服务器集群系统,可以在UNIX/LINUX平台下实现负载均衡集群功能, LVS内核模块IPVS从2.4.24以后已经成为Linux官方标准内核的一部分。LVS相关参考这篇文章高并发场景 LVS 安装及高可用实现
haproxy
、nginx
:也是可以用来做负载均衡 - 高可用则需要定时检测节点,出现故障时自动转移
keepalived
:具体参考这篇写的比较好的文章keepalived实现服务高可用,keepalived
一开始就为LVS设计的,和LVS搭配比较简单。keepalived
使用VRRP协议,限制的地方是使用arp的广播模式,只有同一网段才能使用,也就是局域网环境,然后还有脑裂的问题
《从Paxos到Zookeeper 分布式一致性原理与实践》中对脑裂的描述
MHA
:比较成熟的高可用集群方案
看了一下好像都有点复杂,决定先简单的实践下使用keepalived+LVS
实现mysql高可用。
上图是我们需要实现的流程构架,只使用两台机器,将LVS与RealServer合并在同一台机器上。而且我们还是使用LVS的
DR
模式。先介绍下LVS的DR
模式:
- 假设我们客户端(client:
192.168.1.101
)请求我们负载均衡服务器上的VIP地址(192.168.1.200
),则发送的请求中源IP为(192.168.1.101
),目标IP为(192.168.1.200
) - 路由器通过arp解析到负载均衡服务器mac地址,则转发请求到负载均衡服务器上。
-
DR
模式下负载均衡服务器不修改该请求的目标IP,即目标IP还是(192.168.1.200
),而是修改请求的mac地址再转发到RealServer(真正调用的服务器),RealServer不经过负载均衡服务器而直接返回响应信息给client。
DR模式参考网上的图
LVSDR
模式需要注意的点:
- 默认情况下RealServer接收到目标IP是
192.168.1.200
的请求跟自己的IP地址不一致,则会直接抛弃这个请求导致无法访问。需要在RealServer的网卡lo
接口上添加我们的VIP(192.168.1.200
)来接收请求。lo
是本地回环接口,当RealServer接收到目标IP是192.168.1.200
的请求时,被当成本地程序服务处理,而不是直接丢弃。 - 这个就是我们构建LVS
DR
模式时最普遍使用到的realserver脚本的其中一个作用
按照网上很多的教程都是至少需要4台机器,其中
keepalived+LVS
部署到两台机作为单纯的负载均衡服务器,而keepalived
则保证LVS
负载均衡服务器的高可用,同时还支持realserver的健康检测,当其中一个realserver节点服务挂了,就将其排除出去。
个人觉得两台机作为单纯负载均衡机会有点浪费,想直接将LVS和RealServer部署在同一台机器上,但是实际测试过程中确实是出现了请求的死循环,最后通过iptables
的MARK
标记请求的方式不触发LVS的slave机的负载均衡解决这个问题。
一:首先本地使用虚拟机搭建实验环境
因为keepalived
使用要求是同一网段(局域网),这里使用了VirtualBox
搭建CentOS-7
环境。
主要遇到下面几点问题:
1. 网卡设置选择桥接模式
目标是实现主机与虚拟机相互能ping
通,虚拟机能上网,这里连接方式选择桥接模式并且网卡选择当前主机正在使用的网卡。
桥接模式桥接模式:像是局域网中的一台独立的主机,可以访问与局域网内任何一台机器相互访问
NAT模式:可以通过主机实现上网,但不能访问主机。也可以添加多一个网卡2选择Host-only
来实现虚拟机访问主机。
2. 虚拟机固定IP
修改虚拟机为固定IP,目的是实验时IP地址不会变化而且容易记。
- 主机本地cmd->
ipconfig
得到ipv4地址以及子网掩码和网关配置:
获取主机ip -
ifconfig -a
查看除了lo
回环接口的外的接口名称,并执行ifup 网卡接口名称
启用网卡,可以看到这里名称不是一般默认的eth0
查看网卡接口名称 - 修改为固定ip:
vi /etc/systemconfig/network-scripts/ifcfg-enp0s3
BOOTPROTO=static
IPADDR=192.168.1.110
NETMASK=255.255.255.0
GATEWAY=192.168.1.1
DNS1=192.168.1.1
DNS2=8.8.8.8
ONBOOT=yes
3. 关闭防火墙和SELinux,减少复杂度或额外干扰
#关闭防火墙
systemctl stop firewalld
#开机禁用
systemctl disable firewalld
#关闭SELinux-》设置SELINUX=disabled并重启
vi /etc/selinux/config
然后我们创建了两台虚拟机:
lvs-master
:192.168.1.110
lvs-slave
:192.168.1.111
二:安装keepalived实现服务的高可用以及配置LVS负载均衡
1.安装keepalived
yum -y install keepalived
2.修改lvs-master
配置文件:vi /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
router_id LVS_1 #lvs id,在一个网络内唯一
}
vrrp_instance VI_1 {
state MASTER #角色:MASTER主,BACKUP备
interface enp0s3 #网卡接口
virtual_router_id 51 #虚拟路由编号,主备一致
priority 100 #定义优先级,数字越大优先级越高,主优先级大于备
advert_int 1 #检查间隔,默认1s
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.1.200 #虚拟IP(VIP)地址
}
}
#定义LVS负载均衡的VIP port
virtual_server 192.168.1.200 80 {
delay_loop 6 #设置健康检查时间,单位秒
lb_algo wrr #设置负载调度算法
lb_kind DR #设置LVS实现负载的机制(NAT/TUN/DR)
nat_mask 255.255.255.0
persistence_timeout 0 #会话保持时间
protocol TCP
#定义real server的IP地址
real_server 192.168.1.110 80 {
weight 3 #配置节点权重,越大权重越高
TCP_CHECK {
connect_timeout 10
nb_get_retry 3
delay_before_retry 3
connect_port 80
}
}
#定义real server的IP地址
real_server 192.168.1.111 80 {
weight 3 #配置节点权重,越大权重越高
TCP_CHECK {
connect_timeout 10
nb_get_retry 3
delay_before_retry 3
connect_port 80
}
}
}
#定义LVS的VIP port
virtual_server 192.168.1.200 3306 {
delay_loop 6 #设置健康检查时间,单位秒
lb_algo wrr #设置负载调度算法
lb_kind DR #设置LVS实现负载的机制(NAT/TUN/DR)
nat_mask 255.255.255.0
persistence_timeout 0 #会话保持时间
protocol TCP
#定义real server的IP地址
real_server 192.168.1.110 3306 {
weight 3 #配置节点权重,越大权重越高
TCP_CHECK {
connect_timeout 10
nb_get_retry 3
delay_before_retry 3
connect_port 3306
}
}
#定义real server的IP地址
real_server 192.168.1.111 3306 {
weight 3 #配置节点权重,越大权重越高
TCP_CHECK {
connect_timeout 10
nb_get_retry 3
delay_before_retry 3
connect_port 3306
}
}
}
启动keepalived:service keepalived start
关闭命令:service keepalived stop
配置说明:
①这里两台机都需要安装keepalived作为Master主负载均衡服务器和Slave备机(salve机上的keepalived.conf
配置只需要修改:state为BACKUP,priority 99 优先级低于Master机,其他配置跟Master机一致就可以了)
②interface 这里可以配置机器上的主网卡接口(执行ifconfig
命令的第一个例如eth0)
③virtual_ipaddress 配置VIP,可配置多个
④virtual_server 配置LVS负载均衡,这里我配置多一个80端口先用来做测试
⑤lb_algo wrr是加权负载均衡,rr模式就是轮询负载均衡
3.配置realserver脚本文件:
①进入/etc/init.d/
目录cd /etc/init.d/
②创建并编辑脚本vi realserver
SNS_VIP=192.168.1.200 #自定义VIP
/etc/rc.d/init.d/functions
case "$1" in
start)
ifconfig lo:0 $SNS_VIP netmask 255.255.255.255 broadcast $SNS_VIP
/sbin/route add -host $SNS_VIP dev lo:0
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
sysctl -p >/dev/null 2>&1
echo "RealServer Start OK"
;;
stop)
ifconfig lo:0 down
route del $SNS_VIP >/dev/null 2>&1
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce
echo "RealServer Stoped"
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
esac
exit 0
③给与可执行权限chmod 755 realserver
④开启配置命令:service realserver start
关闭service realserver stop
keepalived自动根据interface临时添加配置1.之前有说到LVS
DR
模式中RealServer需要在lo
本地回环接口配置VIP才能处理请求,而arp_ignore
和arp_announce
作用是抑制ARP响应,局域网环境路由器通过ARP确定负载均衡服务器mac地址,而RealServer不应该回应arp请求
2.我们使用service keepalived start
启动keepalived
会发现主负载均衡服务器已经在enp0s3
接口上自动增加了VIP地址,所以当主机作为负载均衡服务器时不需要启动realserver脚本,只作为RealServer(lvs-slave)时才启用。
三:测试LVS负载均衡和keepalived的故障自动漂移
1. 安装LVS管理工具ipvsadm
yum -y install ipvsadm
2. 查看LVS负载均衡配置详情ipvsadm -ln
ipvsadm
3. 安装httpd测试LVS负载均衡
#安装Apache
yum -y install httpd
#添加测试首页
cd /var/www/html
touch index.html
#同理在lvs-slave机上安装并新增首页
echo "hello world! lvs-Master" > index.html
在本地使用浏览器访问VIP地址可以打开页面
通过主动关闭Master机上的keepalived模拟故障,执行
ip a
可以观察到slave机接口上VIP的变化关闭lvs-Master的keepalived自动漂移到lvs-slave
4. 测试过程中容易发现cpu占用率飙升,请求死循环的情况
安装tcpdump
用于监控请求
yum -y install tcpdump
两台机keepalived启动时执行tcpdump -n tcp port 80
,然后访问多次192.168.1.200
页面,会发现请求一直在滚动,这时不访问页面也不会停止。
这种情况就导致我很困惑,keepalived负载均衡备机也会对请求再次使用LVS的负载均衡规则,关掉keepalived的话能解决这个问题,但是不能保证高可用,又没有发现什么参数设置可以让keepalived处于slave角色时不使用LVS负载均衡。
直到我百度到这篇文章在同一台服务器上部署LVS和WEB,以及很多copy这篇文章的博客文章,里边也是说会导致死循环,然后主要流程是使用到了iptables mark打标志,然后keepalived配置中virtual_server针对这个标签调度,但是这里用mac地址我感觉有点问题。
说实话看不懂
四:使用iptables mark使得slave备机负载均衡无效化
- 总体目标是:让处于slave状态的备机不启用LVS负载均衡功能,但是要保持keepalived启用状态维持高可用,当slave状态升级成Master时,又使得LVS负载均衡功能恢复可用,就是两种状态之间是可以切换的。
- 分析:Master和Slave之间最大差别应该就是第一个网络接口
enp0s3
(eth0
)上会临时设置VIP(192.168.1.200
),如果keepalived有能识别到这个接口上没有这个VIP就不启用LVS负载均衡功能的参数配置就可以做到这点,但是没有。在上面那篇文章中,可以看到virtual_server参数配置负载均衡还有使用mark标签的方式,我们可以通过设置标签,使得LVS只处理打了标签的请求。
//查看标签
iptables -t mangle -L -n
//给目标IP为VIP的输入请求打标签
iptables -t mangle -A PREROUTING -d 192.168.1.200 -p tcp --dport 80 -j MARK --set-mark 1
iptables -t mangle -A PREROUTING -d 192.168.1.200 -p tcp --dport 3306 -j MARK --set-mark 2
//清空所有的标签
iptables -F -t mangle
- 总体思路:
①master负载均衡主机打标签,通过virtual_server配置mark标签的方式只处理目标IP为VIP的请求
②slave备机清除所有标签,因为virtual_server配置mark标签方式,所有的请求都不会满足要求,也就是所有请求都不会被负载均衡
③当主备切换时,只需要把标签给打上或者清除就可以了 - 相应的配置如下:
直接添加整合到realserver脚本
SNS_VIP=192.168.1.200
/etc/rc.d/init.d/functions
case "$1" in
start)
ifconfig lo:0 $SNS_VIP netmask 255.255.255.255 broadcast $SNS_VIP
/sbin/route add -host $SNS_VIP dev lo:0
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
sysctl -p >/dev/null 2>&1
#清空mark标记,使得LVS负载均衡对所有请求不生效
iptables -F -t mangle
echo "RealServer Start OK"
;;
stop)
ifconfig lo:0 down
route del $SNS_VIP >/dev/null 2>&1
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce
echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce
#LVS负载均衡只对mark标记的请求有效
iptables -F -t mangle
iptables -t mangle -A PREROUTING -d $SNS_VIP -p tcp --dport 80 -j MARK --set-mark 1
iptables -t mangle -A PREROUTING -d $SNS_VIP -p tcp --dport 3306 -j MARK --set-mark 2
echo "RealServer Stoped"
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
esac
exit 0
keepalived.conf主要是主备切换时调用realserver脚本以及使用mark:下master配置slave类似
! Configuration File for keepalived
global_defs {
router_id LVS_1 #lvs id,在一个网络内唯一
}
vrrp_instance VI_1 {
state MASTER #角色:MASTER主,BACKUP备
interface enp0s3 #网卡接口
virtual_router_id 51 #虚拟路由编号,主备一致
priority 100 #定义优先级,数字越大优先级越高,主优先级大于备
advert_int 1 #检查间隔,默认1s
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.1.200 #虚拟IP(VIP)地址
}
notify_master "/etc/init.d/realserver stop" #切到主 后执行的脚本
notify_backup "/etc/init.d/realserver start" #切到备 后执行的脚本
}
#定义LVS的VIP port
virtual_server fwmark 1{
delay_loop 6 #设置健康检查时间,单位秒
lb_algo wrr #设置负载调度算法
lb_kind DR #设置LVS实现负载的机制(NAT/TUN/DR)
nat_mask 255.255.255.0
persistence_timeout 0 #会话保持时间
protocol TCP
#定义real server的IP地址
real_server 192.168.1.110 80 {
weight 3 #配置节点权重,越大权重越高
TCP_CHECK {
connect_timeout 10
nb_get_retry 3
delay_before_retry 3
connect_port 80
}
}
real_server 192.168.1.111 80 {
weight 3 #配置节点权重,越大权重越高
TCP_CHECK {
connect_timeout 10
nb_get_retry 3
delay_before_retry 3
connect_port 80
}
}
}
#定义LVS的VIP port
virtual_server fwmark 2{
delay_loop 6 #设置健康检查时间,单位秒
lb_algo wrr #设置负载调度算法
lb_kind DR #设置LVS实现负载的机制(NAT/TUN/DR)
nat_mask 255.255.255.0
persistence_timeout 0 #会话保持时间
protocol TCP
#定义real server的IP地址
real_server 192.168.1.110 3306 {
weight 3 #配置节点权重,越大权重越高
TCP_CHECK {
connect_timeout 10
nb_get_retry 5
delay_before_retry 6
connect_port 3306
}
}
real_server 192.168.1.111 3306 {
weight 3 #配置节点权重,越大权重越高
TCP_CHECK {
connect_timeout 10
nb_get_retry 5
delay_before_retry 6
connect_port 3306
}
}
}
关于Keep-Alive特性from《图解HTTP》经过上面的修改,重新测试httpd会发现负载均衡效果好了很多(这里可能需要等5s,因为Keep-Alive: timeout=5, max=100),而且不会出现请求死循环的问题了,理论上以及我自己测试过多次是没有问题,具体稳定性以及有没有其他未发现的bug暂时不知道。
五:配置mysql双主(互为主从)
上面我们搭建好了keepalived+LVS环境,也添加了3306mysql端口的配置,且验证过80端口是可行的,然后我们只需要添加mysql并配置主从就可以完成搭建。
1.安装mysql
直接到mysql官网获取下载方式https://dev.mysql.com/downloads/repo/yum/
这里我使用的是centos7对应mysql80-community-release-el7-1.noarch.rpm
noarch:没有限制,一般不包含二进制程序,常为shell script软件
①查询是否已安装旧版本:rpm -qa | grep mysql 或 yum list installed | grep mysql
②下载对应rpm安装包:wget dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm
③安装rpm包:rpm -Uvh mysql80-community-release-el7-1.noarch.rpm 或者 yum install -y mysql80-community-release-el7-1.noarch.rpm
④安装完成后/etc/yum.repos.d/下会多出几个mysql的yum源的配置,安装mysql:yum install -y mysql-community-server
⑤启动mysql:service mysqld start
⑥获取临时密码grep 'temporary password' /var/log/mysqld.log
⑦登陆mysql:mysql -uroot -p
然后粘贴临时密码登陆
⑧修改密码:alter user 'root'@'localhost' identified by 'your-password';
⑨如果密码简单报错,则修改密码级别:set global validate_password.policy=0;
⑩navicat连接MYSQL报错:提示Host is not allowed to connect to this MySQL server
原因:root帐号不允许从远程登陆,只能在localhost,修改localhost为%
use mysql;
然后执行 update user set host = '%' where user = 'root';
*报错:Client does not support authentication protocol requested by server:
则ALTER user 'root'@'%' IDENTIFIED WITH mysql_native_password BY '复杂点的密码';
2.mysql配置主从
mysql主从复制和二进制日志文件系统可以看这两篇文章:
MySQL Replication 主从复制全方位解决方案
MySQL的存储引擎与日志说明
①修改配置文件vi /etc/my.cnf
,主要添加下面两个配置
#server id 不能相同
server-id = 1
# 跳过域名解析参数
skip_name_resolve = 0
②创建一个用户给予复制权限用于主从复制
#先创建一个用户repl
create user 'repl'@'192.168.1.%' identified by 'your-password';
#再授权
grant replication slave on *.* to 'repl'@'192.168.1.%' with grant option;
③登陆mysql-master,show master status;
获取File
和Position
④登陆mysql-slave,并执行change master,如果两个都是yes则为成功,如果还是connecting则需要检查网络是否连通,权限是否配置之类的。
change master to master_host='192.168.1.110',master_user='repl',master_password='your-password',master_log_file='binlog.000014',master_log_pos=155;
同步成功
⑤反向操作一次主从则配置双主(互为主从)成功
⑥为了增强一致性,配置半同步复制:
MySQL复制默认是异步复制,半同步复制则是当master在将自己binlog发给slave的时候,要确保slave已经接受到了这个二进制日志以后,才会返回数据给客户端。
//两个库都安装插件
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
//查看是否成功
show plugins;
//两个库都执行两条语句开启semisync master和slave
SET GLOBAL rpl_semi_sync_master_enabled = 1;
SET GLOBAL rpl_semi_sync_slave_enabled = 1;
//重启SLAVE IO线程
STOP SLAVE IO_THREAD;
START SLAVE IO_THREAD;
//semi_sync_slave_status和semi_sync_master_status都是on则配置成功
show status like '%semi%';
直接在之前已经搭建好的keepalived+LVS的lvs-master和lvs-slave两台机器上安装mysql并通过mysql二进制文件复制的方式配置了双主模式就可以实现mysql双主负载均衡高可用