Loading... **目录** [一、问题提出](https://wxy0327.blog.csdn.net/article/details/80916567#t0) [二、方案选择](https://wxy0327.blog.csdn.net/article/details/80916567#t1) [三、Keepalived简介](https://wxy0327.blog.csdn.net/article/details/80916567#t2) [1. VRRP](https://wxy0327.blog.csdn.net/article/details/80916567#t3) [1.1 VRRP协议](https://wxy0327.blog.csdn.net/article/details/80916567#t4) [1.2 工作机制](https://wxy0327.blog.csdn.net/article/details/80916567#t5) [2. Keepalived设计与实现](https://wxy0327.blog.csdn.net/article/details/80916567#t6) [1.1 多进程模式](https://wxy0327.blog.csdn.net/article/details/80916567#t7) [1.2 控制面板](https://wxy0327.blog.csdn.net/article/details/80916567#t8) [1.3 看门狗](https://wxy0327.blog.csdn.net/article/details/80916567#t9) [1.4 IPVS封装](https://wxy0327.blog.csdn.net/article/details/80916567#t10) [四、安装配置](https://wxy0327.blog.csdn.net/article/details/80916567#t11) [1. 安装keepalived软件](https://wxy0327.blog.csdn.net/article/details/80916567#t12) [2. 主从的配置文件修改](https://wxy0327.blog.csdn.net/article/details/80916567#t13) [五、测试](https://wxy0327.blog.csdn.net/article/details/80916567#t14) [参考:](https://wxy0327.blog.csdn.net/article/details/80916567#t15) --- # 一、问题提出 由于公司服务器硬件已临界损耗期限,最近数据库主机频繁出现故障,不得不手工执行主从库切换操作。这种方式的问题是全程需要人工干预,宕机时间长,严重影响线上业务。于是开始调研MySQL 高可用解决方案,要达到两个主要目标:主库出现问题时快速自动切换到从库;对应用透明。 # 二、方案选择 MySQL的高可用方案有很多,比如Cluster、MMM、MHA、DRBD,以及Oracle官方推出的Fabric等,这些方案各有优劣,但都比较复杂,安装配置有一定难度,对线上库实施动静太大。就我们的具体情况而言,并不需要这么复杂的环境,实施简单、对现有架构影响最小、能迅速解决问题的方案才是最适合的。比如我们现在只是配置了MySQL Replication,加上如[Keepalived](https://so.csdn.net/so/search?q=Keepalived&spm=1001.2101.3001.7020)这样的高可用软件,就能实现我们的需求。 MySQL架构为Master/Slave,当Master故障时,虚IP漂移到Slave上提供服务,简单环境如图1所示。在这种架构中,故障自动切换以后,需要采取手动操作的方式与新的Master进行复制。当然也可以设置为Active-Passive模式下的双Master复制。 ![](https://img-blog.csdn.net/20180704180218694?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d6eTA2MjM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 图1 # 三、Keepalived简介 Keepalived的作用是检测服务器的状态,如果有一台服务器宕机,或工作出现故障,Keepalived将检测到,并将有故障的服务器从系统中剔除,同时使用其它服务器代替该服务器的工作,当服务器工作正常后Keepalived自动将服务器加入到服务器群中,这些工作全部自动完成,不需要人工干涉,需要人工做的只是修复故障的服务器。 ## 1. VRRP Keepalived是对VRRP的一个实现,因此在理解Keepalived的工作原理之前,有必要先了解VRRP的原理。 ### 1.1 VRRP协议 在现实的网络环境中,两台需要通信的主机(end-host)大多数情况下并没有直接地物理连接。对于这样的情况,它们之间的路由怎么选择?通常有两种方法解决如何选定到达目的主机的下一跳路由问题: * 使用动态路由协议,如RIP、OSPF等。 * 配置静态路由。 很明显,在主机上配置动态路由,因为管理、维护成本以及是否支持等诸多问题是不切实际的。那么配置静态路由就变得很流行。实际上,这种方式一直沿用至今。但是,路由器,或者说默认网关(default gateway)却经常成为单故障点。就算配置了多个静态路由,却因为必须重启网络才能生效而变得不实用。 VRRP(虚拟路由冗余协议,Virtual Router Redundancy Protocol)的目的就是为了解决静态路由单点故障问题。它通过一种竞选(election)协议动态地将路由任务交给LAN中虚拟路由器中的某台VRRP路由器。这里有两个关键名词:VRRP路由器和虚拟路由器。 * VRRP路由器:VRRP路由器就是一台路由器,只不过上面运行了VRRPD程序来实现VRRP协议而已,是物理的路由器。一台VRRP路由器可以位于多个虚拟路由器中。 * 虚拟路由器:所谓虚拟,就是说并不是实际存在的,是一个逻辑而不是物理的路由器。虚拟路由器通常由多台VRRP路由器通过某种方式组成,就好像将这些物理路由器都丢到一个池里面去,整个池对外看起来就像是一台路由器,但其实内部有多台。虚拟路由器的标识称为VRID。 在一个VRRP虚拟路由中,有多台物理的VRRP路由器,但是这多台物理路由并不同时工作,而是由一台称为master的负责路由工作,其它的都是backup。master并非一成不变,VRRP协议让每个VRRP路由器参与竞选,最终获胜的就是master。master有一些特权,比如拥有虚拟路由器的IP地址,我们的主机就是用这个IP地址作为静态路由的。拥有特权的master要负责转发发送给网关地址的包和响应ARP请求。 ### 1.2 工作机制 VRRP通过竞选协议来实现虚拟路由器的功能,所有的协议报文都是通过IP多播(multicast)包形式发送的,多播地址为224.0.0.18。虚拟路由器由VRID(范围0-255)和一组IP地址组成,对外表现为一个众所周知的MAC地址:00-00-5E-00-01-{VRID}。所以,在一个虚拟路由器中,不管谁是master,对外都是相同的MAC和IP(称之为VIP)。客户端主机并不需要因为master的改变而修改自己的路由配置,对它们来说,这种主从的切换是透明的。 在一个虚拟路由器中,只有作为master的VRRP路由器会一直发送VRRP广告包(VRRP Advertisement Message),backup不会抢占master,除非它的优先级更高。当master不可用时,backup收不到广告包,多台backup中优先级最高的这台会抢占为master。这种抢占是非常快速的(<1秒),以保证服务的连续性。出于安全性考虑,VRRP包使用了加密协议进行加密。 ## 2. Keepalived设计与实现 Keepalived是一个高度模块化设计的软件,从源代码结构似乎也很容易看出这一点。Keepalived 1.2.13源代码中只有如下目录:check core etc include libipvs-2.4 libipvs-2.6 vrrp * check:Keepalived的healthchecker子进程的目录,包括了所有的健康检查方式以及对应配置的解析,LVS的配置解析也在这个里面。 * core:Keepalived的核心程序,如全局配置的解析,进程启动等等。 * etc:包含Keepalived的配置模板和启动脚本等文件。 * include:头文件目录。 * libipvs*:LVS使用的库文件。 * vrrp:Keepalived的vrrpd子进程以及相关的代码。 Keepalived架构如图2所示。 ![](https://img-blog.csdn.net/20180704180512230?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d6eTA2MjM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 图2 ### 1.1 多进程模式 Keepalived采用了多进程的设计模式,每个进程负责不同的功能,我们在使用LVS的机器上通常可以看到三个Keepalived进程: 111 keepalived < 父进程,负责内存管理、监控子进程等 112 \_ keepalived < VRRP子进程 113 \_ keepalived < healthchecker子进程 ### 1.2 控制面板 这里所谓的控制面板就是对配置文件的编译和解析。Keepalived的配置文件解析比较另类,并不是一次解析所有配置,而是只在用到某模块时才解析相应的配置。在源文件里面可以看到类似XXX_parser.c的文件,就是做这个用的。 ### 1.3 看门狗 WatchDog框架提供了对VRRP和healthchecker子进程的监控。 ### 1.4 IPVS封装 Keepalived里面所有对LVS的相关操作并不直接使用ipvsadm客户端程序,而是使用IPVS提供的函数进行操作,这些代码都在check/ipwrapper.c中。 # 四、安装配置 环境: MySQL Master 172.16.1.126 MySQL Slave 172.16.1.127 VIP 172.16.1.100 MySQL主从复制配置从略。 ## 1. 安装keepalived软件 用root执行下面的命令,主从操作一样。 ```groovy wget -q http://www.keepalived.org/software/keepalived-1.2.13.tar.gz tar -zxvf keepalived-1.2.13.tar.gz cd keepalived-1.2.13 ./configure && make && make install cp /usr/local/etc/rc.d/init.d/keepalived /etc/rc.d/init.d/ cp /usr/local/etc/sysconfig/keepalived /etc/sysconfig/ mkdir /etc/keepalived cp /usr/local/etc/keepalived/keepalived.conf /etc/keepalived/ cp /usr/local/sbin/keepalived /usr/sbin/ chkconfig --add keepalived chkconfig --level 345 keepalived on ``` ## 2. 主从的配置文件修改 master的keepalived配置文件如下: ```puppet [root@hdp3~]#more /etc/keepalived/keepalived.conf global_defs { router_id MySQL-HA } vrrp_script check_run { script "/home/mysql/mysql_check.sh" interval 60 } 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 } track_script { check_run } notify_master /home/mysql/master.sh notify_stop /home/mysql/stop.sh virtual_ipaddress { 172.16.1.100 } } [root@hdp3~]# ``` slave的keepalived配置文件如下: ```puppet [root@hdp4~]#more /etc/keepalived/keepalived.conf global_defs { router_id MySQL-HA } vrrp_script check_run { script "/home/mysql/mysql_check.sh" interval 60 } 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 } track_script { check_run } notify_master /home/mysql/master.sh notify_stop /home/mysql/stop.sh virtual_ipaddress { 172.16.1.100 } } [root@hdp4~]# ``` master与slave的keepalived配置文件中只有priority设置不同,master为100,slave为90,其它全一样。配置文件是以块形式组织的,每个块都在{}包围的范围内,#和!开头的行都是注释。 global_defs为全局定义,对整个Keepalived起作用,而不管是否使用LVS。 * router_id:运行Keepalived的机器的一个标识。 vrrp_script配置业务进程监控脚本。 * script:设置脚本文件名。 * interval:设置脚本执行的时间间隔,这里为每60秒执行一次。 /home/mysql/mysql_check.sh文件用以检测MySQL服务是否正常,当发现连接不上mysql,自动把keepalived进程杀掉,让VIP进行漂移。文件内容如下。 ```bash [root@hdp3~]#more /home/mysql/mysql_check.sh #!/bin/bash . /home/mysql/.bashrc count=1 while true do mysql -uroot -S /data/mysql.sock -e "show status;" > /dev/null 2>&1 i=$? ps aux | grep mysqld | grep -v grep > /dev/null 2>&1 j=$? if [ $i = 0 ] && [ $j = 0 ] then exit 0 else if [ $i = 1 ] && [ $j = 0 ] then exit 0 else if [ $count -gt 5 ] then break fi let count++ continue fi fi done /etc/init.d/keepalived stop [root@hdp3~]# ``` vrrp_sync_group配置VRRP同步组。不使用Sync Group的话,如果机器有两个网段,一个内网一个外网,每个网段开启一个VRRP实例。假设VRRP配置为检查内网,那么当外网出现问题时,VRRPD认为自己仍然健康,那么不会触发Master和Backup的切换,从而导致问题。Sync Group解决这个问题,可以把两个实例都放进一个Sync Group,这样的话,Sync Group里面任何一个实例出现问题都会发生切换。 * group:设置同一组中的VRRP实例名,这里只有一个实例VI_1。 vrrp_instance配置VRRP实例。VRRP实例表示在上面开启了VRRP协议。这个实例说明了VRRP的一些特性,比如主从、VRID等等。可以在每个网卡上开启一个实例。VRRP实例主要定义vrrp_sync_group里面的每个组的漂移IP等。 * state:指定实例的初始状态。在两台路由都启动后,马上会发生竞争,高priority的会竞选为Master,所以这里的state并不表示这台就一直是Backup。 * interface:实例绑定的网卡。 * virtual_router_id:VRID标记,值为0..255,这里使用默认的51。 * priority:高优先级竞选为Master,Master要高于Backup至少50。这里MySQL主从库两个优先级分别设置为100和90,因此当Keepalived启动后,MySQL主库会被选为Master。 * advert_int:检查间隔,这里设置为默认的1秒。 * nopreempt:设置为不抢占,注意这个配置只能设置在state为BACKUP的主机上。当MASTER出现问题后,BACKUP会竞选为新的MASTER,那么当之前的MASTER重新在线后,是继续成为MASTER还是变成BACKUP呢?默认不设置不抢占,那么之前的MASTER起来后会继续抢占成为MASTER。这样的频繁切换对于业务是不能容忍的,我们希望MASTER起来后成为BACKUP,所以要设置不抢占。又因为nopreempt配置只能用在state为BACKUP的主机上,因此MASTER的state也得设置为BACKUP,也就是说172.16.1.126和172.16.1.127都要将state设置为BACKUP。通过在两台BACKUP上面设置不同的priority,让它们一起来就抢占,高priority的172.16.1.126成为最初的MASTER。 * authentication:设置认证类型和认证密码。 * auth_type:认证类型,支持PASS、AH两种,通常使用PASS类型。 * auth_pass:明文认证密码。同一VRRP实例的MASTER与BACKUP使用相同的密码才能正常通信。 * track_script:设置追踪脚本,这里为check_run,即调用vrrp_script中定义的脚本。 * notify_master:指定当切换到MASTER时执行的脚本。 * notify_stop:VRRP停止以后执行的脚本。 * virtual_ipaddress:指定漂移地址(VIP),也就是切换到MASTER时,这些IP或被添加,切换到BACKUP时,这些IP会被删除。因此每台服务器上可以不绑定任何虚拟地址,而都把它们放到virtual_ipaddress里面,可以都多个。Keepalived会自动使用ip addr进行绑定。 /home/mysql/master.sh的作用是状态改为master以后执行的脚本。首先判断复制是否有延迟,如果有延迟,等1分钟后,不论是否有延迟,都并停止复制,并且记录binlog和pos点。文件内容如下。 ```bash [root@hdp3~]#more /home/mysql/master.sh #!/bin/bash . /home/mysql/.bashrc Master_Log_File=$(mysql -uroot -S /data/mysql.sock -e "show slave status\G" | grep -w Master_Log_File | awk -F": " '{print $2}') Relay_Master_Log_File=$(mysql -uroot -S /data/mysql.sock -e "show slave status\G" | grep -w Relay_Master_Log_File | awk -F": " '{print $2}') Read_Master_Log_Pos=$(mysql -uroot -S /data/mysql.sock -e "show slave status\G" | grep -w Read_Master_Log_Pos | awk -F": " '{print $2}') Exec_Master_Log_Pos=$(mysql -uroot -S /data/mysql.sock -e "show slave status\G" | grep -w Exec_Master_Log_Pos | awk -F": " '{print $2}') i=1 while true do if [ $Master_Log_File = $Relay_Master_Log_File ] && [ $Read_Master_Log_Pos -eq $Exec_Master_Log_Pos ] then echo "ok" break else sleep 1 if [ $i -gt 60 ] then break fi continue let i++ fi done mysql -uroot -S /data/mysql.sock -e "stop slave;" mysql -uroot -S /data/mysql.sock -e "reset slave all;" mysql -uroot -S /data/mysql.sock -e "reset master;" mysql -uroot -S /data/mysql.sock -e "show master status;" > /tmp/master_status_$(date "+%y%m%d-%H%M").txt [root@hdp3~]# ``` /home/mysql/stop.sh表示Keepalived停止以后需要执行的脚本。检查是否还有复制写入操作,最后无论是否执行完毕都退出。文件内容如下。 ```bash [root@hdp3~]#more /home/mysql/stop.sh #!/bin/bash . /home/mysql/.bashrc M_File1=$(mysql -uroot -S /data/mysql.sock -e "show master status\G" | awk -F': ' '/File/{print $2}') M_Position1=$(mysql -uroot -S /data/mysql.sock -e "show master status\G" | awk -F': ' '/Position/{print $2}') sleep 1 M_File2=$(mysql -uroot -S /data/mysql.sock -e "show master status\G" | awk -F': ' '/File/{print $2}') M_Position2=$(mysql -uroot -S /data/mysql.sock -e "show master status\G" | awk -F': ' '/Position/{print $2}') i=1 while true do if [ $M_File1 = $M_File1 ] && [ $M_Position1 -eq $M_Position2 ] then echo "ok" break else sleep 1 if [ $i -gt 60 ] then break fi continue let i++ fi done [root@hdp3~]# ``` # 五、测试 1. 分别在master上和slave上启动keepalived进程。 ```csharp /etc/init.d/keepalived start ``` 成功启动后可以看到如图3所示的3个Keepalived进程。 ![](https://img-blog.csdn.net/20180704182027164?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d6eTA2MjM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 图3 2. 查看master上的VIP ```ruby [root@hdp3~]#ip addr ``` 结果如图4所示,可以看到VIP已经绑定成功。 ![](https://img-blog.csdn.net/20180704181247510?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d6eTA2MjM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 图4 3. 客户端使用VIP连接数据库,创建测试库,插入数据。 ```sql C:\WINDOWS\system32>mysql -u wxy -p -h 172.16.1.100 Enter password: ****** Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 2958 Server version: 5.6.14-log Source distribution Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> create database test; Query OK, 1 row affected (0.00 sec) mysql> use test; Database changed mysql> create table t1(a int); Query OK, 0 rows affected (0.01 sec) mysql> insert into t1 values (1); Query OK, 1 row affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> select * from t1; +------+ | a | +------+ | 1 | +------+ 1 row in set (0.00 sec) mysql> ``` 4. 模拟mysqld crash 在master上执行以下命令: ```ruby [root@hdp3~]#pkill -9 mysqld ``` 5. 再次使用VIP连接,数据没有异常,复制停止了,因为已经切换为主库。 ```sql C:\WINDOWS\system32>mysql -u wxy -p -h 172.16.1.100 Enter password: ****** Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 1568 Server version: 5.6.14-log Source distribution Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> use test; Database changed mysql> select * from t1; +------+ | a | +------+ | 1 | +------+ 1 row in set (0.00 sec) mysql> show slave status; Empty set (0.00 sec) mysql> show master status; +------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------+ | mysql-bin.000001 | 120 | | | | +------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec) mysql> ``` binlog和pos点记录如下: ```ruby [root@hdp4~]#more /tmp/master_status_180704-1751.txt File Position Binlog_Do_DB Binlog_Ignore_DB Executed_Gtid_Set mysql-bin.000001 120 [root@hdp4~]# ``` 此时VIP漂移到172.16.1.127,而172.16.1.126上绑定的VIP已经删除。在172.16.1.126查看IP地址如图5所示。 ![](https://img-blog.csdn.net/20180704182618767?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d6eTA2MjM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 图5 同时172.16.1.126的keepalived进程也已经被杀掉: ```ruby [root@hdp3~]#ps -ef | grep keepalived | grep -v grep [root@hdp3~]# ``` **六、总结** 本MySQL高可用方案配置简单,对现有MySQL架构无任何影响,也不需要停止数据库服务,完全联机操作即可。有一点需要注意,主从库的端口必须一样。 # 参考: 1. [Keepalived+MySQL实现高可用](http://www.cnblogs.com/gomysql/p/3856484.html) 2. [Keepalived权威指南](https://wenku.baidu.com/view/78c9168602d276a201292e04.html) 最后修改:2022 年 03 月 15 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 0 如果觉得我的文章对你有用,请随意赞赏