keepalived+LVS使用两台机器实现mysql双主负载均衡高可用实践

前段时间看了《大型网站系统与Java中间件实践》这本书,了解到大型网站构架的演进,大致上来说也是单机走向分布式集群来分摊访问压力。书中提到了很多常见的场景,比如数据库方面,当压力变大时可以实现读写分离、分库分表等,总体上是给出了通用解决方案和思路。

分享下《大型网站系统与Java中间件实践》PDF,网上找不到带目录的,简单的用Adobe自己添加了目录(百度云地址)

网上搜mysql集群高可用方案结果是一大堆,也没有说哪个方案是相对比较好。看了很多文章介绍初步了解到下面的几种情况:

  1. 数据库一主多从实现读写分离
    mysql-proxy:原理上应该是通过lua脚本解析sql,写主库读从库,还能配置负载均衡
    mycat:也是统一拦截sql,还支持分表分库、读写分离,负载均衡等,好像比较强大。
  2. 负载均衡,把请求分发以分摊服务器压力
    LVS:LVS(Linux Virtual Server)是一个虚拟的服务器集群系统,可以在UNIX/LINUX平台下实现负载均衡集群功能, LVS内核模块IPVS从2.4.24以后已经成为Linux官方标准内核的一部分。LVS相关参考这篇文章高并发场景 LVS 安装及高可用实现
    haproxynginx:也是可以用来做负载均衡
  3. 高可用则需要定时检测节点,出现故障时自动转移
    keepalived:具体参考这篇写的比较好的文章keepalived实现服务高可用,keepalived一开始就为LVS设计的,和LVS搭配比较简单。keepalived使用VRRP协议,限制的地方是使用arp的广播模式,只有同一网段才能使用,也就是局域网环境,然后还有脑裂的问题
    keepalived+LVS使用两台机器实现mysql双主负载均衡高可用实践_第1张图片
    《从Paxos到Zookeeper 分布式一致性原理与实践》中对脑裂的描述

MHA:比较成熟的高可用集群方案

看了一下好像都有点复杂,决定先简单的实践下使用keepalived+LVS实现mysql高可用。

keepalived+LVS使用两台机器实现mysql双主负载均衡高可用实践_第2张图片
draw.io

上图是我们需要实现的流程构架,只使用两台机器,将LVS与RealServer合并在同一台机器上。而且我们还是使用LVS的 DR模式。先介绍下LVS的 DR模式:

  1. 假设我们客户端(client:192.168.1.101)请求我们负载均衡服务器上的VIP地址(192.168.1.200),则发送的请求中源IP为(192.168.1.101),目标IP为(192.168.1.200
  2. 路由器通过arp解析到负载均衡服务器mac地址,则转发请求到负载均衡服务器上。
  3. DR模式下负载均衡服务器不修改该请求的目标IP,即目标IP还是(192.168.1.200),而是修改请求的mac地址再转发到RealServer(真正调用的服务器),RealServer不经过负载均衡服务器而直接返回响应信息给client。
    keepalived+LVS使用两台机器实现mysql双主负载均衡高可用实践_第3张图片
    DR模式参考网上的图

    LVSDR模式需要注意的点:
  • 默认情况下RealServer接收到目标IP是192.168.1.200的请求跟自己的IP地址不一致,则会直接抛弃这个请求导致无法访问。需要在RealServer的网卡lo接口上添加我们的VIP(192.168.1.200)来接收请求。lo是本地回环接口,当RealServer接收到目标IP是192.168.1.200的请求时,被当成本地程序服务处理,而不是直接丢弃。
  • 这个就是我们构建LVSDR模式时最普遍使用到的realserver脚本的其中一个作用

按照网上很多的教程都是至少需要4台机器,其中keepalived+LVS部署到两台机作为单纯的负载均衡服务器,而keepalived则保证LVS负载均衡服务器的高可用,同时还支持realserver的健康检测,当其中一个realserver节点服务挂了,就将其排除出去。
个人觉得两台机作为单纯负载均衡机会有点浪费,想直接将LVS和RealServer部署在同一台机器上,但是实际测试过程中确实是出现了请求的死循环,最后通过iptablesMARK标记请求的方式不触发LVS的slave机的负载均衡解决这个问题。

一:首先本地使用虚拟机搭建实验环境

因为keepalived使用要求是同一网段(局域网),这里使用了VirtualBox搭建CentOS-7环境。
主要遇到下面几点问题:

1. 网卡设置选择桥接模式

目标是实现主机与虚拟机相互能ping通,虚拟机能上网,这里连接方式选择桥接模式并且网卡选择当前主机正在使用的网卡。

桥接模式:像是局域网中的一台独立的主机,可以访问与局域网内任何一台机器相互访问
NAT模式:可以通过主机实现上网,但不能访问主机。也可以添加多一个网卡2选择Host-only来实现虚拟机访问主机。

keepalived+LVS使用两台机器实现mysql双主负载均衡高可用实践_第4张图片
桥接模式
2. 虚拟机固定IP

修改虚拟机为固定IP,目的是实验时IP地址不会变化而且容易记。

  • 主机本地cmd->ipconfig得到ipv4地址以及子网掩码和网关配置:
    keepalived+LVS使用两台机器实现mysql双主负载均衡高可用实践_第5张图片
    获取主机ip
  • ifconfig -a查看除了lo回环接口的外的接口名称,并执行ifup 网卡接口名称启用网卡,可以看到这里名称不是一般默认的eth0
    keepalived+LVS使用两台机器实现mysql双主负载均衡高可用实践_第6张图片
    查看网卡接口名称
  • 修改为固定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

1.之前有说到LVSDR模式中RealServer需要在lo本地回环接口配置VIP才能处理请求,而arp_ignorearp_announce作用是抑制ARP响应,局域网环境路由器通过ARP确定负载均衡服务器mac地址,而RealServer不应该回应arp请求
2.我们使用service keepalived start启动 keepalived会发现主负载均衡服务器已经在enp0s3接口上自动增加了VIP地址,所以当主机作为负载均衡服务器时不需要启动realserver脚本,只作为RealServer(lvs-slave)时才启用。

keepalived+LVS使用两台机器实现mysql双主负载均衡高可用实践_第7张图片
keepalived自动根据interface临时添加配置
三:测试LVS负载均衡和keepalived的故障自动漂移
1. 安装LVS管理工具ipvsadm
yum -y install ipvsadm
2. 查看LVS负载均衡配置详情ipvsadm -ln
keepalived+LVS使用两台机器实现mysql双主负载均衡高可用实践_第8张图片
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地址可以打开页面

keepalived+LVS使用两台机器实现mysql双主负载均衡高可用实践_第9张图片
纯页面貌似缓存情况严重,负载均衡效果一般

通过主动关闭Master机上的keepalived模拟故障,执行 ip a可以观察到slave机接口上VIP的变化
keepalived+LVS使用两台机器实现mysql双主负载均衡高可用实践_第10张图片
关闭lvs-Master的keepalived自动漂移到lvs-slave

4. 测试过程中容易发现cpu占用率飙升,请求死循环的情况

安装tcpdump用于监控请求

yum -y install tcpdump

两台机keepalived启动时执行tcpdump -n tcp port 80,然后访问多次192.168.1.200页面,会发现请求一直在滚动,这时不访问页面也不会停止。

keepalived+LVS使用两台机器实现mysql双主负载均衡高可用实践_第11张图片
请求死循环

这种情况就导致我很困惑,keepalived负载均衡备机也会对请求再次使用LVS的负载均衡规则,关掉keepalived的话能解决这个问题,但是不能保证高可用,又没有发现什么参数设置可以让keepalived处于slave角色时不使用LVS负载均衡。
直到我百度到这篇文章 在同一台服务器上部署LVS和WEB,以及很多copy这篇文章的博客文章,里边也是说会导致死循环,然后主要流程是使用到了iptables mark打标志,然后keepalived配置中virtual_server针对这个标签调度,但是这里用mac地址我感觉有点问题。
说实话看不懂

四:使用iptables mark使得slave备机负载均衡无效化
  1. 总体目标是:让处于slave状态的备机不启用LVS负载均衡功能,但是要保持keepalived启用状态维持高可用,当slave状态升级成Master时,又使得LVS负载均衡功能恢复可用,就是两种状态之间是可以切换的。
  2. 分析: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
  1. 总体思路:
    ①master负载均衡主机打标签,通过virtual_server配置mark标签的方式只处理目标IP为VIP的请求
    ②slave备机清除所有标签,因为virtual_server配置mark标签方式,所有的请求都不会满足要求,也就是所有请求都不会被负载均衡
    ③当主备切换时,只需要把标签给打上或者清除就可以了
  2. 相应的配置如下:
    直接添加整合到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
        }
    }
}

经过上面的修改,重新测试httpd会发现负载均衡效果好了很多(这里可能需要等5s,因为Keep-Alive: timeout=5, max=100),而且不会出现请求死循环的问题了,理论上以及我自己测试过多次是没有问题,具体稳定性以及有没有其他未发现的bug暂时不知道。

keepalived+LVS使用两台机器实现mysql双主负载均衡高可用实践_第12张图片
关于Keep-Alive特性from《图解HTTP》
五:配置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;获取FilePosition

keepalived+LVS使用两台机器实现mysql双主负载均衡高可用实践_第13张图片
ishow master status获取binlog信息

④登陆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;
keepalived+LVS使用两台机器实现mysql双主负载均衡高可用实践_第14张图片
同步成功

⑤反向操作一次主从则配置双主(互为主从)成功
⑥为了增强一致性,配置半同步复制:
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双主负载均衡高可用

你可能感兴趣的:(keepalived+LVS使用两台机器实现mysql双主负载均衡高可用实践)