实战 MySQL 高可用架构(五)手把手教你 Keepalived 原理和部署

855 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情

搭建 keepalived 环境

Keepalived 的应用场景

Keepalived 软件在主主架构中,可以配置成两种应用场景:

  • ① 当这台服务器上的 keepalived 发现 MySQL 服务崩了后,立刻停掉这台服务器上 keepalived 自己,这样流量就会自动切到另外一台 keepalived 服务器。
  • ② 当这台服务器上的 keepalived 发现 MySQL 服务崩了后,立刻尝试重启 MySQL 服务,如果重启失败,则停掉 keepalived 自己。和第一种方案的区别是会尝试重启 MySQL 服务。

这里我配置成第二种功能场景,保障 MySQL 服务的高可用。另外可以配置 MySQL 服务异常时,发送邮件给运维或开发人员,由他们检查服务器的状态。

使用 Keepalived 的原理

Keepalived 提供了一个虚拟 IP (简称 VIP),对外提供访问。当客户端连接这个虚拟 IP 后,只会访问其中一个 MySQL。MySQL 节点故障后,keepalived 执行脚本进行重启,如果重启失败,脚本自动停掉 keepalived,备用节点自动切换为主节点。

安装 keepalived 软件

安装依赖、获取 keepalived 安装包、解压安装包、删除安装包。

# 安装依赖
sudo apt-get install -y libssl-dev
sudo apt-get install -y openssl 
sudo apt-get install -y libpopt-dev
sudo apt-get install -y libnl-dev
sudo apt-get install -y libnl-3-dev
sudo apt-get install -y libnl-genl-3.dev
sudo apt-get install daemon
sudo apt-get install libc-dev
sudo apt-get install libnfnetlink-dev
sudo apt-get install gcc

# 获取 keepalived 安装包
cd /usr/local
sudo su
sudo wget https://www.keepalived.org/software/keepalived-2.2.2.tar.gz

# 解压安装包
sudo tar -zxvf keepalived-2.2.2.tar.gz 

# 删除安装包
mv keepalived-2.2.2 keepalived

配置 keepalived 软件

cd keepalived

./configure --prefix=/usr/local/keepalived --disable-dependency-tracking

执行结果如下所示:

编译 keepalived 软件

sudo make && make install

执行结果如下所示:

对于 Ubuntu ,需要做一点特别的改动,创建链接

mkdir -p  /etc/rc.d/init.d
ln -s /lib/lsb/init-functions /etc/rc.d/init.d/functions

拷贝配置文件

sudo mkdir /etc/sysconfig
sudo cp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/
sudo cp /usr/local/keepalived/keepalived/etc/init.d/keepalived /etc/init.d/
sudo cp /usr/local/keepalived/sbin/keepalived /sbin/
sudo mkdir /etc/keepalived
sudo cp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/

修改配置文件 /etc/keepalived/keepalived.conf

daemon keepalived ${KEEPALIVED_OPTIONS}

改为

daemon -- keepalived ${KEEPALIVED_OPTIONS}

添加虚拟 IP

ip addr del 192.168.56.88 dev enp0s8:1

ifconfig enp0s8:1 192.168.56.88 broadcast 192.168.56.255 netmask 255.255.255.0 up
route add -host 192.168.56.88 dev enp0s8:1

将命令写到 /usr/local/script/vip.sh文件中。最好将 /usr/local/script/vip.sh文件添加到服务器的开机启动项中,将 Keepalived 服务设置为开机自启动。

添加到开机启动项中

建立rc-local.service文件

sudo vim /etc/systemd/system/rc-local.service

将下列内容复制进 rc-local.service 文件

[Unit]
Description=/etc/rc.local Compatibility
ConditionPathExists=/etc/rc.local
 
[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
StandardOutput=tty
RemainAfterExit=yes
SysVStartPriority=99
 
[Install]
WantedBy=multi-user.target

创建文件 rc.local

sudo vim /etc/rc.local

将下列内容复制进 rc.local 文件

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
echo "看到这行字,说明添加自启动脚本成功。" > /usr/local/test.log
exit 0

给 rc.local 加上权限,启用服务

sudo chmod +x /etc/rc.local
sudo systemctl enable rc-local
sudo systemctl start rc-local.service
sudo systemctl status rc-local.service

重启并检查test.log文件

cat /usr/local/test.log

添加启动脚本到启动文件中

sudo vim /etc/rc.local

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

ifconfig ens5:1 10.27.119.88 broadcast 10.27.119.255 netmask 255.255.255.0 up
route add -host 10.27.119.88 dev ens5:1

systemctl start keepalived

exit 0

修改配置文件

备份配置文件

sudo mv /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.backup

修改配置文件

sudo vim /etc/keepalived/keepalived.conf

配置文件的内容如下:

global_defs {
   router_id MYSQL_HA  #当前节点名
}
vrrp_script restart_mysql {
   script "/usr/local/keepalived/restart_mysql.sh"  #重启 mysql 容器
   interval 15
   weight -20
}
vrrp_instance VI_1 {    
    state BACKUP         #两台配置节点均为BACKUP
    interface enp0s8       #绑定虚拟IP的网络接口
    virtual_router_id 51 #VRRP组名,两个节点的设置必须一样,以指明各个节点属于同一VRRP组
    priority 101         #节点的优先级,另一台优先级改低一点
    advert_int 1         #组播信息发送间隔,两个节点设置必须一样
    nopreempt            #不抢占,只在优先级高的机器上设置即可,优先级低的机器不设置
    authentication {      #设置验证信息,两个节点必须一致
        auth_type PASS
        auth_pass 123456
    }
    track_script {
        restart_mysql  #检测 mysql 状态,如果失败,则重启 mysql 容器
    }
    virtual_ipaddress {   #指定虚拟IP,两个节点设置必须一样
        192.168.56.88
    }
}
virtual_server 192.168.56.88 3306 {   #linux虚拟服务器(LVS)配置 
    delay_loop 2     #每个2秒检查一次real_server状态
    lb_algo wrr      #LVS调度算法,rr|wrr|lc|wlc|lblc|sh|dh
    lb_kind DR      #LVS集群模式 ,NAT|DR|TUN
    persistence_timeout 60    #会话保持时间
    protocol TCP    #使用的协议是TCP还是UDP

    real_server 192.168.56.11 3306 {
        weight 3   #权重
        TCP_CHECK {
            connect_timeout 10   #连接超时时间
            nb_get_retry 3      #重连次数
            delay_before_retry 3 #重连间隔时间
            connect_port 3306    #健康检查端口
        }
    }    
}

编写异常处理脚本

sudo vim /usr/local/keepalived/restart_mysql.sh

内容如下,

#!/bin/bash
# 定义变量,重启 mysql 容器
START_MYSQL="docker restart mysql"
# 定义变量,停止 mysql 容器
STOP_MYSQL="docker stop mysql"
# 定义变量,日志文件路径
LOG_FILE="/usr/local/keepalived/logs/mysql-check.log"
# 定义变量,检查 mysql 服务是否正常的命令
HAPS=`ps -C mysqld --no-header |wc -l`
# 打印当前时间到日志文件
date "+%Y-%m-%d %H:%M:%S" >> $LOG_FILE
# 打印提示信息到日志文件
echo "check mysql status" >> $LOG_FILE
# 检查数据库状态,如何返回 0,则重启 mysql 容器,然后休眠 3s 后,再次检测 mysql 状态,如果还是返回 0,则停止 keepalived。
if [ $HAPS -eq 0 ];then
  echo $START_MYSQL >> $LOG_FILE
  $START_MYSQL >> $LOG_FILE 2>&1
  sleep 10
  if [ `ps -C mysqld --no-header |wc -l` -eq 0 ];then
    echo "start mysql failed, killall keepalived" >> $LOG_FILE
    killall keepalived
    exit 1
   else
     exit 0
  fi
 else 
   exit 0
fi

给脚本分配权限

sudo chmod +x /usr/local/keepalived/restart_mysql.sh

创建 logs 文件夹,给 logs 文件夹分配权限

sudo mkdir /usr/local/keepalived/logs
sudo chmod +x /usr/local/keepalived/logs -r

重新加载配置文件

sudo systemctl daemon-reload

启动 keepalived

启动两台服务器上的 keepalived

启动 node2 节点:

sudo pkill keepalived

sudo systemctl start keepalived

sudo systemctl status keepalived

启动 node1 节点:

pkilll keepalived

sudo systemctl status keepalived

我们可以通过这个命令查看 keepalived 进程

ps -ef | grep keepalived

查看日志

sudo cat /var/log/syslog

测试 keepalived 是否会重启 mysql

停止 node2 上的 mysql 容器

docker stop 8cc

查看 keepalived 状态,提示移除了 mysql 服务。

因为 keepalived 会每 2s 检查一次 MySQL 的状态,发现 MySQL 异常后,就会重启 mysql 容器。所以过几秒后,重新查看容器状态,会看到 mysql 容器重新启动了。

docker  ps

查看 keepalived 状态,执行 restart_mysql 成功

查看执行日志

问题:每 2s 会打印一次,文件可能会很大。需要执行定期删除。

测试 MySQL 节点切换

验证下当 MySQL 重启失败后,keepalived 自动停止后,客户端连接的 MySQL 是否会自动切到另外一个 MySQL 节点上。

首先用 mysql 客户端工具 navicat 连接虚拟 ip 地址,账号和密码就是 node 1 和 node2 的 mysql 账号密码(root/123456)

可以连接上,然后执行以下命令,查看当前虚拟 ip 连接的是哪个数据库

SHOW VARIABLES LIKE '%hostname%'

可以看到连接的是 node2 的容器的 id,说明 keepalived 已经通过虚拟 ip 连接到 node2 的 mysql 了,是正常工作的,node2 现在是作为主节点,node1 作为备用节点。

由于本地环境重新启动 MySQL 都是成功的,不会停掉 keepalived 服务。出于演示目的,我就直接停掉 keepalived 服务。

pkill keepalived

执行下面这个命令可以查看 keepalived 进程,发现已经没有了。(控制台显示的 grep --color=auto keepalived 表示是查找命令)

ps -ef | grep keepalived

重新查询客户端的连接信息,发现已经切换到 92b (node1)机器上的 mysql 了。

SHOW VARIABLES LIKE '%hostname%'

再次查看 node1 上 keepalived 上的状态,再发送信息给

sudo systemctl status keepalived