Mysql高可用之lvs+keepalived+mysql多实例+双master

一.前言

在mysql的实际生产环境中,配置了mysql双机多实例+主从复制,并且准备设置为双机多实例+双master,以增加mysql实例的高可用性。经过研究,仅keepalived无法实现多实例上的高可用,于是采用lvs+keepalived以实现mysql多实例+双master上的高可用.

LVS和Keepalived介绍:
LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统。采用IP负载均衡技术和基于内容请求分发技术。调度器具有很好的吞吐率,将请求均衡地转移到不同的服务器上执行,且调度器自动屏蔽掉服务器的故障,从而将一组服务器构成一个高性能的、高可用的虚拟服务器。整个服务器集群的结构对客户是透明的,而且无需修改客户端和服务器端的程序.
keepalived是一个类似于layer3, 4 & 5交换机制的软件,也就是我们平时说的第3层、第4层和第5层交换。Keepalived的作用是检测web服务器的状态,如果有一台web服务器死机,或工作出现故障,Keepalived将检测到,并将有故障的web服务器从系统中剔除,当web服务器工作正常后Keepalived自动将web服务器加入到服务器群中,这些工作全部自动完成,不需要人工干涉,需要人工做的只是修复故障的web服务器。

简单来说
LVS主要提供负载均衡.
Keepalived在这里主要用作RealServer的健康状态检查以及LoadBalance主机和BackUP主机之间failover的实现.
借张图展示一下:
Mysql高可用之lvs+keepalived+mysql多实例+双master

 二.环境描述

现有环境:

mysql服务器A:
192.168.67.253:3306
192.168.67.253:3308

mysql服务器B:
192.168.67.254:3306
192.168.67.254:3308

两台服务器上的3306端口互为master,3308端口互为master

实现后的架构:

负载均衡器: MASTER:
接口ip:192.168.66.6
VIP:192.168.66.251
所需软件:ipvsadm,keepalived

BACKUP:
接口ip:192.168.66.12
VIP:192.168.66.251
所需软件:ipvsadm,keepalived 真实服务器: mysql服务器A:
接口ip及端口:
192.168.67.253:3306
192.168.67.253:3308
VIP:192.168.66.251
所需软件:lvs客户端配置脚本

mysql服务器B:
192.168.67.254:3306
192.168.67.254:3308
VIP:192.168.66.251
所需软件:lvs客户端配置脚本

 三.部署过程

 1.安装ipvs及keepalived

在两台准备做负载均衡器的服务器上分别安装ipvs和keepalived,详细安装过程可参照SA team中相关文档,在此不再赘述。

 1.1 安装lvs客户端

Lvs的客户端指负载均衡其/转发器(director)后面提供服务的真实机器。也即是我们的mysql服务器。在mysql服务器上增加如下配置文件

[root@localhost ~]# more /usr/local/bin/lvs_real   
#!/bin/bash 
#description : start realserver VIP=192.168.66.251 /etc/rc.d/init.d/functions 
 
case "$1" in 
start) 
echo " start LVS of REALServer" 
/sbin/ifconfig lo:0 $VIP broadcast $VIP netmask 255.255.255.255 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 
;; 
stop) 
/sbin/ifconfig lo:0 down 
echo "close LVS Directorserver" 
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 "Usage: $0 {start|stop}" 
exit 1 
esac

这里对配置文件里重要的一些项进行说明:

1、 vip(virtual ip)。直接路由模式的vip必须跟服务器对外提供服务的ip地址在同一个网段, 并且lvs 负载均衡器和其他所有提供相同功能的服务器都使用这个vip.

2、 vip被绑定在环回接口lo0:0 上,其广播地址是其本身,子网掩码是255.255.255.255。这 与标准的网络地址设置有很大的不同。采用这种可变长掩码方式把网段划分成只含一个 主机地址的目的是避免ip地址冲突。

3、 echo “1”,echo “2” 这段的作用是抑制arp广播。如果不做arp抑制,将会有众多的机器 向其他宣称:“嗨!我是奥巴马,我在这里呢!”,这样就乱套了。

 1.2 lvs客户端验证

执行命令 /usr/local/bin/lvs_real start ,接着我们来检查网络的状态:

[root@localhost ~]# ip addr
1: lo:  mtu 16436 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 inet 192.168.66.251/32 brd 192.168.66.251 scope global lo:0 inet6 ::1/128 scope host 
      valid_lft forever preferred_lft forever
2: eth0:  mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000
   link/ether 54:04:a6:b7:9c:4c brd ff:ff:ff:ff:ff:ff
   inet 192.168.66.253/24 brd 192.168.66.255 scope global eth0
   inet6 fe80::5604:a6ff:feb7:9c4c/64 scope link 
      valid_lft forever preferred_lft forever

发现在lo接口上绑定VIP成功。可以通过/usr/local/bin/lvs_real stop卸载VIP

 1.3 keepalived.conf

请注意,在安装ipvs及keepalived过程中,只有keepalived.conf这个配置文件是与SA team相关文档中不同

MASTER的配置文件/etc/keepalived/keepalived.conf

root># cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
  router_id lvs50
}
vrrp_sync_group VG1 {
 group {
     INT
 }
}
vrrp_instance INT {
  state MASTER              #备份服务器上将MASTER改为BACKUP
  interface eth0
  lvs_sync_daemon_interface eth0
  virtual_router_id 200     #相同的VRID为一个组,他将决定多播的MAC地址;在局域网应唯一(即假如一个局域网内有多套LVS,则这里的设置不冲突即可)
  priority 100              #备份服务上将100改为比100小,最好主备的值差大于50
  advert_int 1
  authentication {
      auth_type PASS
      auth_pass lvs50test 
  }
  virtual_ipaddress {
     192.168.66.251          #设置Vip
  }
}
virtual_server 192.168.66.251 3306 {
  delay_loop 20
  lb_algo wrr       #lb_algo rr|wrr|lc|wlc|lblc|sh|dh      #LVS调度算法
  lb_kind DR       #lb_kind NAT|DR|TUN                     #LVS模式
  persistence_timeout 120
  protocol TCP
  #virtualhost www.no300.com                      #检查客户端的域名,这里用TCP去检测,可不用设置
  sorry_server 192.168.66.254 3306                #在real_server失败的时候跳转到的ip和port
  real_server 192.168.66.253 3306 {                 #真实服务器IP和Port
      weight 1
     TCP_CHECK {                                 #用TCP协议检测
          connect_timeout 5
          nb_get_retry 3
          delay_before_retry 3
          connect_port 3306 
      }
  }    
 }
virtual_server 192.168.66.251 3308 {
  delay_loop 20
  lb_algo wrr       #lb_algo rr|wrr|lc|wlc|lblc|sh|dh      #LVS调度算法
  lb_kind DR       #lb_kind NAT|DR|TUN                     #LVS模式
  persistence_timeout 120
  protocol TCP
  #virtualhost www.no300.com                      #检查客户端的域名,这里用TCP去检测,可不用设置
  sorry_server 192.168.66.254 3308                #在real_server失败的时候跳转到的ip和port
  real_server 192.168.66.253 3308 {                 #真实服务器IP和Port
      weight 1
     TCP_CHECK {                                 #用TCP协议检测
          connect_timeout 5
          nb_get_retry 3
          delay_before_retry 3
          connect_port 3308
      }
  }
 }

BACKUP配置文件/etc/keepalived/keepalived.conf

root># cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
  router_id lvs50
}
vrrp_sync_group VG1 {
 group { 
     INT
 }
}
vrrp_instance INT {
  state BACKUP      #备份服务器上将MASTER改为BACKUP
  interface eth0
  lvs_sync_daemon_interface eth0
  virtual_router_id 200     #相同的VRID为一个组,他将决定多播的MAC地址;在局域网应唯一
  priority 90      # 备份服务上将100改为99
  advert_int 1
  authentication {
      auth_type PASS
      auth_pass lvs50test 
  }
  virtual_ipaddress {
     192.168.66.251
  }
}
virtual_server 192.168.66.251 3306 {
  delay_loop 20
  lb_algo wrr       #lb_algo rr|wrr|lc|wlc|lblc|sh|dh                        #LVS调度算法
  lb_kind DR       #lb_kind NAT|DR|TUN                                      #LVS模式
  persistence_timeout 120
  protocol TCP
  #virtualhost www.no300.com
  sorry_server 192.168.66.254 3306
  real_server 192.168.66.253 3306 {
      weight 1 
     TCP_CHECK {
           connect_timeout 5
           nb_get_retry 3
           delay_before_retry 3
           connect_port 3306
      }
  }
  }
virtual_server 192.168.66.251 3308 {
  delay_loop 20
  lb_algo wrr       #lb_algo rr|wrr|lc|wlc|lblc|sh|dh                        #LVS调度算法
  lb_kind DR       #lb_kind NAT|DR|TUN                                      #LVS模式
  persistence_timeout 120
  protocol TCP
  #virtualhost www.no300.com
  sorry_server 192.168.66.254 3308
  real_server 192.168.66.253 3308 {
      weight 1
     TCP_CHECK {
           connect_timeout 5
           nb_get_retry 3
           delay_before_retry 3
           connect_port 3308
      }
  }
  }

keepalived realserver列表只配置一台mysql主机,sorry server配置另一台主机。

这样在realserver正常情况下,sorry server不会被切换,只充当备机,只有realserver出故障时才切换到sorry server,keepalived的sorry server天生具有这个特性。

 2.lvs+keepalived服务的启用和验证

 2.1 启动lvs客户端

在两台mysql服务器上分别执行/usr/local/bin/lvs_real start,然后执行命令ip addr检验是否启动成功

 2.2 启动keepalived

在两台负载均衡服务器上分别执行命令service keepalived start ,然后我们查看系统进程,看是否是3个keepalived进程

root># ps -ef |grep keepalived
root     15595     1  0 14:50 ?        00:00:00 keepalived -D
root     15596 15595  0 14:50 ?        00:00:00 keepalived -D
root     15597 15595  0 14:50 ?        00:00:00 keepalived -D
root     15600 15597  0 14:50 ?        00:00:00 [keepalived] 
root     15603 15537  0 14:50 pts/0    00:00:00 grep --color=auto keepalived

查看/var/log/messages中keepalived段的输出,看是否有异常。

执行命令ip addr,看VIP是否已正确绑定到MASTER

MASTER的输出:

root># ip addr
1: lo:  mtu 16436 qdisc noqueue 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
2: eth0:  mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 50:59:00:e2:b7:8a brd ff:ff:ff:ff:ff:ff
    inet 192.168.66.6/24 brd 192.168.66.255 scope global eth0 inet 192.168.66.251/32 scope global eth0 

BACKUP的输出:

root># ip addr
1: lo:  mtu 16436 qdisc noqueue 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
2: eth0:  mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 50:56:00:e2:b7:8a brd ff:ff:ff:ff:ff:ff
    inet 192.168.66.12/23 brd 192.168.67.255 scope global eth0

 四.功能测试

 4.1 测试前准备

保证所有服务器的网络服务正常。ping所有ip及vip来检查

 4.2 转发功能测试

1.在本地机器执行命令 telnet 192.168.66.251 3306,检查访问是否正常。然后测试3308端口

2.登录主负载均衡器,察看转发情况。

root># ipvsadm
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.66.251:mysql wrr persistent 120
  -> 192.168.66.253:mysql         Route   1      0          0         
TCP  192.168.66.251:tns-server wrr persistent 120
  -> 192.168.66.253:tns-server    Route   1      0          0

 4.3 健康检查功能(故障隔离)测试

通过手工的方法,使真实服务器提供的服务失效,然后再从客户端发起访问请求,检验lvs提供的服务是否可用。

两种方式:1.停止某个真实服务器的服务 2.关闭网络服务。

4.3.1 关闭真实服务器192.168.66.253:3306上的mysql

在本地telnet 192.168.66.253 3306,不能访问为正常。

telnet 192.168.66.251 3306.如访问正常,再检查keepalived日志,发现主负载均衡器上有如下输出,BACKUP无日志输出

Jul 19 15:47:22 6 Keepalived_healthcheckers: TCP connection to [192.168.66.253]:3306 failed !!!
Jul 19 15:47:22 6 Keepalived_healthcheckers: Removing service [192.168.66.253]:3306 from VS [192.168.66.251]:3306
Jul 19 15:47:22 6 Keepalived_healthcheckers: Lost quorum 1-0=1 > 0 for VS [192.168.66.251]:3306
Jul 19 15:47:22 6 Keepalived_healthcheckers: Adding sorry server [192.168.66.254]:3306 to VS [192.168.66.251]:3306
Jul 19 15:47:22 6 Keepalived_healthcheckers: Removing alive servers from the pool for VS [192.168.66.251]:3306

telnet 192.168.66.254 3306,发现访问正常,证明lvs已将请求转发至sorr_server指定的192.168.66.254:3306.

4.3.2 关闭lvs 的客户端配置脚本/usr/local/bin/lvs_real

这个测试不会成功,关闭真实机器的vip以后,负载均衡器依然会把用户请求转发过来,但tcp连接不成功,用户的访问会失败。因此在部署和运维lvs环境时,要特别注意这一点

关闭192.168.66.253上的lvs_real脚本,此时telnet 192.168.66.253 3306访问正常,telnet 192.168.66.251 3306访问失败。

在keepalived检测时间超时后,依然未看到有错误日志输出。因此要特别注意此脚本

 五.失败切换(FailOver)测试

关闭主负载均衡器(MASTER)的keepalived进程,然后从客户端访问vip地址及真实服务器地址

正常情况下,主负载均衡器(MASTER)失效时,备份负载均衡器(BACKUP)能立即接替转发任务(接替时间由keepalived.conf文件的advert_int 指定)。

在确认主负载均衡器(MASTER)的keepalived进程关闭后,我们来看看备份负载均衡器的运行情况。这里我们观察两个地方:ipvsadm 的输出及系统日志的输出。

 5.1 ipvsadm 输出变化

MASTER上keepalived未关闭时的输出:

root># ipvsadm
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.66.251:mysql wrr persistent 120
  -> 192.168.66.253:mysql         Route   1      0          0         
TCP  192.168.66.251:tns-server wrr persistent 120
  -> 192.168.66.253:tns-server    Route   1      0          0  

MASTER上keepalived关闭后的输出:

root># ipvsadm
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn

BACKUP上ipvsadm输出未变化,原因尚不清楚

 5.2 系统日志输出变化

MASTER:

Jul 19 16:58:14 6 Keepalived: Terminating on signal
Jul 19 16:58:14 6 Keepalived: Stopping Keepalived v1.2.2 (04/12,2012) 
Jul 19 16:58:14 6 Keepalived_vrrp: Terminating VRRP child process on signal
Jul 19 16:58:14 6 Keepalived_healthcheckers: Terminating Healthchecker child process on signal
Jul 19 16:58:14 6 Keepalived_vrrp: VRRP_Instance(INT) removing protocol VIPs.
Jul 19 16:58:14 6 kernel: IPVS: stopping sync thread 15679 ...
Jul 19 16:58:15 6 kernel: IPVS: sync thread stopped!

BACKUP:

Jul 19 16:58:19 12 Keepalived_vrrp: VRRP_Instance(INT) Transition to MASTER STATE
Jul 19 16:58:19 12 Keepalived_vrrp: VRRP_Group(VG1) Syncing instances to MASTER state
Jul 19 16:58:20 12 Keepalived_vrrp: VRRP_Instance(INT) Entering MASTER STATE
Jul 19 16:58:20 12 Keepalived_vrrp: VRRP_Instance(INT) setting protocol VIPs.
Jul 19 16:58:20 12 Keepalived_vrrp: VRRP_Instance(INT) Sending gratuitous ARPs on eth0 for 192.168.66.251
Jul 19 16:58:20 12 kernel: IPVS: stopping sync thread 23765 ...
Jul 19 16:58:20 12 Keepalived_healthcheckers: Netlink reflector reports IP 192.168.66.251 added
Jul 19 16:58:20 12 kernel: IPVS: sync thread stopped!
Jul 19 16:58:20 12 Keepalived_vrrp: Netlink reflector reports IP 192.168.66.251 added
Jul 19 16:58:20 12 kernel: IPVS: sync thread started: state = MASTER, mcast_ifn = eth0, syncid = 200
Jul 19 16:58:25 12 Keepalived_vrrp: VRRP_Instance(INT) Sending gratuitous ARPs on eth0 for 192.168.66.251

可以看到备份负载均衡器已接管服务

现在再回来启动MASTER上的keepalived服务,接着观察BACKUP上的系统日志

BACKUP:

Jul 19 17:14:35 12 Keepalived_vrrp: VRRP_Instance(INT) Received higher prio advert
Jul 19 17:14:35 12 kernel: IPVS: stopping sync thread 23772 ...
Jul 19 17:14:35 12 kernel: IPVS: sync thread stopped!
Jul 19 17:14:35 12 Keepalived_vrrp: VRRP_Instance(INT) Entering BACKUP STATE
Jul 19 17:14:35 12 Keepalived_vrrp: VRRP_Instance(INT) removing protocol VIPs.
Jul 19 17:14:35 12 Keepalived_healthcheckers: Netlink reflector reports IP 192.168.66.251 removed
Jul 19 17:14:35 12 Keepalived_vrrp: VRRP_Group(VG1) Syncing instances to BACKUP state
Jul 19 17:14:35 12 Keepalived_vrrp: Netlink reflector reports IP 192.168.66.251 removed
Jul 19 17:14:35 12 kernel: IPVS: sync thread started: state = BACKUP, mcast_ifn = eth0, syncid = 200

输出显示BACKUP已把控制权移交给MASTER

 六.模拟生产环境测试

 6.1 环境准备

请开发人员设计一个java程序,每隔1秒往mysql的表中插入一行数据,模拟mysql实例失败及keepalived失败切换的场景。

mysql环境:

模拟两读一写的模式,即只往192.168.66.253:3306写数据,253和254两台机器的3306实例均可读取数据。待253:3306失败后才写254:3306.

表结构:

CREATE TABLE `vip_wealth_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `account` varchar(40) NOT NULL,
  `cert_no` varchar(40) DEFAULT NULL COMMENT '用户身份证号',
  `name` varchar(40) DEFAULT NULL COMMENT '用户姓名',
  PRIMARY KEY (`id`),
  KEY `vip_user_wealth_index` (`cert_no`,`name`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8 COMMENT='记录会员的财富值流水信息' 

 6.2 模拟mysql实例失败

 6.2.1 测试Innodb存储引擎

启动java程序,开始往mysql中插入数据,查看数据写入情况,192.168.66.253:3306停止前查询到的结果如下:

mysql> select * from vip_wealth_log;
+----+---------+---------+------+
| id | account | cert_no | name |
+----+---------+---------+------+
|  2 | test    | demo    | test |
|  4 | test    | demo    | test |
|  6 | test    | demo    | test |
|  8 | test    | demo    | test |
| 10 | test    | demo    | test |
| 12 | test    | demo    | test |
| 14 | test    | demo    | test |
| 16 | test    | demo    | test |
| 18 | test    | demo    | test |
+----+---------+---------+------+
9 rows in set (0.00 sec)

可以看到mysql已依照设定值,将自增段做了奇偶处理。

停止192.168.66.253:3306,java程序抛出异常,连接中止。查询192.168.66.254:3306的数据结果如下:

mysql> select * from vip_wealth_log;
+----+---------+---------+------+
| id | account | cert_no | name |
+----+---------+---------+------+
|  2 | test    | demo    | test |
|  4 | test    | demo    | test |
|  6 | test    | demo    | test |
|  8 | test    | demo    | test |
| 10 | test    | demo    | test |
| 12 | test    | demo    | test |
| 14 | test    | demo    | test |
| 16 | test    | demo    | test |
| 18 | test    | demo    | test |
| 20 | test    | demo    | test |
| 22 | test    | demo    | test |
| 24 | test    | demo    | test |
| 26 | test    | demo    | test |
| 28 | test    | demo    | test |
+----+---------+---------+------+
14 rows in set (0.00 sec)

重新启动192.168.66.253:3306服务后查询结果也一样。

在192.168.66.253:3306关闭的情况下,重新启动java程序,此时服务已切换至192.168.66.254:3306,查看自增列为单数,验证双master架构成功。不再截图。

继续切换,恢复192.168.66.253:3306服务,此时查看keepalived日志,服务切换回192.168.66.253:3306,java程序抛出异常,并停止往192.168.66.254:3306写数据。

但因192.168.66.254:3306服务并未中止,所以java程序的连接仍在,过了大概5-10分钟连接才消失。

 6.2.2 测试MyISAM存储引擎

更改表存储引擎为MyISAM,同上述步骤重新测试,得到测试结果同上,不再赘述。

但因5.1版本的mysql同步仍为异步同步,不排除192.168.66.253:3306实例崩溃时丢失数据,以及崩溃时数据未及时和192.168.66.254:3306同步而导致两台机器数据不一致的可能(可能性极大)。

 6.3 崩溃测试

执行命令ps -ef |grep mysql,kill掉3306或3308端口任意实例,观察对其他实例是否有影响。

kill掉某个实例后,在两台mysql服务器上执行命令mysqld_multi report,发现均未影响到其他实例的运行,检查日志也未抛出异常。测试成功

 七.测试总结

1.在停止192.168.66.253:3306实例后再打开,发现192.168.66.254:3306并未马上连接至192.168.66.2533:3306.

修改master_connect_retry=1;修正此问题。

2.由于mysql5.1版本复制为异步复制,存在一定延迟,在两台master切换或某实例崩溃,特别是某进程执行更新MyISAM表中大批数据崩溃的情况下,会造成两台master数据不一致的情况。

因此建议使用双master架构的应用应允许小部分数据量不一致,或者表存储引擎为Innodb且有自增主键的mysql服务器采用此架构。

 八.状态监控

使用zabbix监控以下服务的运行:

1.负载均衡器及真实服务器的存活检查。只有这些服务器运行正常,才可能有其他依赖服务。

2.Vip的存活检查。特别是lvs客户端上lo端口绑定VIP的检查。一般情况下,启用了lvs环境后,是可以用ping的方式检查vip的。

3.真实服务器服务状态检查。

4.Vip对应的服务状态检查。一般通过check_tcp加端口号的形式实现。

 九.参考文献

1.SA team中lvs及keepalived相关文档

2.《互联网运营智慧》 第6章 负载均衡及服务器集群(LVS) 作者:田逸

上一篇:关于mysql cluster适用场景的分析


下一篇:/tmp/mysql.sock无故丢失的故障分析