Skip to main content

MySQL + Keepalived 高可用服务配置

IP角色
192.168.10.2MySQL Node01
192.168.10.3MySQL Node02
192.168.10.100VIP

在 Keepalived 的配置中,VIP(Virtual IP)概念是一个虚拟的IP地址,可以在备用节点接管主节点服务时使用,避免客户端连接断开。

配置主从互相同步

先参考 MySQL 主从部署文档完成两台 MySQL 服务的部署,再参考本文档方式配置主从互相同步

  1. 分别登录 MySQL Node01 与 MySQL Node02 节点,创建主从同步用户

    /usr/local/mysql/bin/mysql --socket=/usr/local/mysql/mysql.sock -uroot -p123456
    # 创建repl
    create user 'repl'@'%' identified by '123456';
    grant replication slave on *.* to 'repl'@"%";
    flush privileges;
    • 命令中新建的主从同步用户为 repl,密码为 123456,实际部署时注意替换
  2. 分别查看 MySQL Node01 与 MySQL Node02 两节点的 master_log_file , master_log_pos 信息

    > show master status;
  3. 登录 MySQL Node01 节点,配置主从同步

    /usr/local/mysql/bin/mysql --socket=/usr/local/mysql/mysql.sock -uroot -p123456
    change master to master_host="192.168.10.3",master_port=3306,master_user="repl",master_password="123456",master_log_file="mysql-bin.000001",master_log_pos=2936,get_master_public_key=1;
    start slave;
    • change master 语句中 master_host 的地址注意替换为部署时实际的 MySQL Node02 服务器IP
    • change master 语句中 master_log_file , master_log_pos 的值为在 master 节点执行 show master status; 看到的输出,如有不同请以实际部署时为准
  4. 检查主从同步状态

    show slave status\G

    # 输出结果中 Slave_IO_Running 与 Slave_SQL_Running 均为 Yes 代表主从同步正常
  5. 登录 MySQL Node02 节点,配置主从同步

    /usr/local/mysql/bin/mysql --socket=/usr/local/mysql/mysql.sock -uroot -p123456
    change master to master_host="192.168.10.2",master_port=3306,master_user="repl",master_password="123456",master_log_file="mysql-bin.000001",master_log_pos=2936,get_master_public_key=1;
    start slave;
    • change master 语句中 master_host 的地址注意替换为部署时实际的 MySQL Node01 服务器IP
    • change master 语句中 master_log_file , master_log_pos 的值为在 master 节点执行 show master status; 看到的输出,如有不同请以实际部署时为准
  6. 检查主从同步状态

    show slave status\G

    # 输出结果中 Slave_IO_Running 与 Slave_SQL_Running 均为 Yes 代表主从同步正常
  7. 此时两台 MySQL 实例的同步状态应是都开启且正常的状态

配置 keepalived

安装 keepalived

两台 MySQL 服务器都需要安装 keepalived 服务

yum install -y keepalived

修改 keepalived 配置文件

MySQL Node01 节点

修改 /etc/keepalived/keepalived.conf 文件内容如下

global_defs {
router_id hap-mysql-ha-01
}

vrrp_script check_mysql_health {
script "/usr/local/mysql/script/check_mysql_health.sh"
interval 10
}

vrrp_sync_group VG1 {
group {
VI_1
}
}

vrrp_instance VI_1 {
state BACKUP
interface eth0 # 修改为部署主机的网卡名称
virtual_router_id 180 # 同一子网上的所有 VRRP 路由器中唯一,范围0-255
priority 100 # 优先级,每个节点不同
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass HAP-MySQL-Keepalived-Auth
}
track_script {
check_mysql_health
}
notify_master /usr/local/mysql/script/notify_master.sh
notify_backup /usr/local/mysql/script/notify_backup.sh
virtual_ipaddress {
192.168.10.100 # VIP address
}
}
  • 注意两个节点配置文件中 priority 的值是不同的
  • 默认仅需修改 interface 与 VIP address 即可

MySQL Node02 节点

修改 /etc/keepalived/keepalived.conf 文件内容如下

global_defs {
router_id hap-mysql-ha-01
}

vrrp_script check_mysql_health {
script "/usr/local/mysql/script/check_mysql_health.sh"
interval 10
}

vrrp_sync_group VG1 {
group {
VI_1
}
}

vrrp_instance VI_1 {
state BACKUP
interface eth0 # 修改为部署主机的网卡名称
virtual_router_id 180 # 同一子网上的所有 VRRP 路由器中唯一,范围0-255
priority 90 # 优先级,每个节点不同
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass HAP-MySQL-Keepalived-Auth
}
track_script {
check_mysql_health
}
notify_master /usr/local/mysql/script/notify_master.sh
notify_backup /usr/local/mysql/script/notify_backup.sh
virtual_ipaddress {
192.168.10.100 # VIP address
}
}
  • 注意两个节点配置文件中 priority 的值是不同的
  • 默认仅需修改 interface 与 VIP address 即可

创建 MySQL 相关脚本

两个节点都需要创建以下 MySQL 相关脚本

创建脚本存放目录

mkdir -p /usr/local/mysql/script/

执行 vim /usr/local/mysql/script/check_mysql_health.sh 创建 MySQL 状态检查脚本

check_mysql_health.sh 脚本内容如下:

#!/bin/bash

# MySQL 相关信息
mysql_user="root"
mysql_password="123456"
mysql_host="127.0.0.1"
mysql_cli="/usr/local/mysql/bin/mysql"

# 日志函数
mysql_keepalived_log_file="/var/log/mysql_keepalived.log"
log_info() {
echo "$(date +"%Y-%m-%d %H:%M:%S") INFO: $1" >> "$mysql_keepalived_log_file"
}

# 检查配置
max_retries=3
retry_interval=2

check_mysql_status() {
# 检查 MySQL 服务是否在运行中
$mysql_cli -h"$mysql_host" -u"$mysql_user" -p"$mysql_password" <<< 'show status;' > /dev/null 2>&1
mysql_status=$?

# 检查 MySQL 进程是否在运行中
ps aux | grep mysqld | grep -v grep | grep -v check_mysql_health > /dev/null 2>&1
mysql_process=$?
}

# 连续检查 MySQL 服务状态,最多尝试 max_retries 次
retry_count=0
while true; do
check_mysql_status

if [ $mysql_status -eq 0 ] && [ $mysql_process -eq 0 ]; then
# MySQL 服务和进程都正常,退出循环
exit 0
else
if [ $retry_count -ge $max_retries ]; then
# 达到最大重试次数,执行停止 Keepalived 服务的操作并退出
log_info "After $max_retries retries, MySQL remains unhealthy, stop Keepalived."
systemctl stop keepalived
exit 0
fi
fi

retry_count=$((retry_count + 1))
sleep $retry_interval
done

执行 vim /usr/local/mysql/script/notify_master.sh 创建该节点 Keepalived 被选为 Master 时,Keepalived 所执行的脚本

notify_master.sh 脚本内容如下:

#!/bin/bash
# MySQL 相关信息
mysql_user="root"
mysql_password="123456"
mysql_host="127.0.0.1"
mysql_cli="/usr/local/mysql/bin/mysql"

# 日志函数
mysql_keepalived_log_file="/var/log/mysql_keepalived.log"
log_info() {
echo "$(date +"%Y-%m-%d %H:%M:%S") INFO: $1" >> "$mysql_keepalived_log_file"
}

# 停止当前节点的同步线程
if $mysql_cli -h"$mysql_host" -u"$mysql_user" -p"$mysql_password" <<< 'stop slave;'; then
log_info "The current node becomes master, successfully stopped master-slave replication on the current node."
else
log_info "The current node becomes master, failed to stop master-slave replication on the current node."
fi

执行 vim /usr/local/mysql/script/notify_backup.sh 创建该节点 Keepalived 被选为 Backup 时,Keepalived 所执行的脚本

notify_backup.sh 脚本内容如下:

#!/bin/bash
# MySQL 相关信息
mysql_user="root"
mysql_password="123456"
mysql_host="127.0.0.1"
mysql_cli="/usr/local/mysql/bin/mysql"

# 日志函数
mysql_keepalived_log_file="/var/log/mysql_keepalived.log"
log_info() {
echo "$(date +"%Y-%m-%d %H:%M:%S") INFO: $1" >> "$mysql_keepalived_log_file"
}

# 启动当前节点的同步线程
if $mysql_cli -h"$mysql_host" -u"$mysql_user" -p"$mysql_password" <<< 'start slave;' ; then
log_info "The current node becomes backup, successfully started master-slave replication on the current node."
else
log_info "The current node becomes backup, failed to start master-slave replication on the current node."
fi

给所有脚本添加可执行权限

chmod +x /usr/local/mysql/script/*.sh

启动 Keepalived

systemctl start keepalived
systemctl enable keepalived

再次检查 MySQL 主从同步状态以及 VIP 情况

此时的正常状态应为:

  • VIP 所在节点的 MySQL 的同步线程是停止的
  • 另外一个节点的 MySQL 的同步线程是运行并正常的

故障排除

两台服务器都有 VIP

通常可能受以下原因导致:

  • 两台机器之间有防火墙或其他网络限制

    • 限制了端口(默认使用 112 端口)

    • 网络环境不支持 VRRP(虚拟路由冗余协议)

  • 两台机器之间互相到对方地址网络不通

  • 两台节点配置的优先级可能一致,导致无法选举主节点

  • 可以通过 /var/log/messages 日志文件进行排查

两台服务器都没有 VIP

通常可能受以下原因导致:

  • keepalived.conf 中指定的 vrrp_script 脚本文件执行异常,返回总是非0错误值导致keepalived 状态一直无法完成初始化,无法绑定vip

    • 比如 mysql 未启动,或者脚本文件中连接 mysql 的密码不对都有可能,也可以手动执行脚本进行排查
  • 可以通过 /var/log/messages 日志文件进行排查