Skip to main content

MySQL + Keepalived HA Service Configuration

IPRole
192.168.10.2MySQL Node01
192.168.10.3MySQL Node02
192.168.10.100VIP

In the Keepalived configuration, the concept of VIP (Virtual IP) refers to a virtual IP address that can be used by the standby node to take over services from the primary node, preventing client connection interruptions.

Configure Master-Slave Synchronization

First, refer to the MySQL master-slave deployment document to deploy MySQL services on both nodes, then configure master-slave synchronization as described in this document.

  1. Log in to the MySQL Node01 and MySQL Node02 nodes to create master-slave synchronization users

    /usr/local/mysql/bin/mysql --socket=/usr/local/mysql/mysql.sock -uroot -p123456
    # Create repl
    create user 'repl'@'%' identified by '123456';
    grant replication slave on *.* to 'repl'@"%";
    flush privileges;
    • The newly created master-slave synchronization user in the command is repl, with a password 123456. Be sure to replace it during actual deployment.
  2. Check the master_log_file and master_log_pos information on both MySQL Node01 and MySQL Node02 nodes

    > show master status;
  3. Log in to the MySQL Node01 node to configure master-slave synchronization

    /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;
    • In the change master statement, ensure that the master_host address is replaced with the actual MySQL Node02 server IP during deployment.
    • The values for master_log_file and master_log_pos in the change master statement are obtained from the show master status; command executed on the master node. Adjust according to your actual deployment.
  4. Check the master-slave synchronization status

    show slave status\G

    # In the output, both Slave_IO_Running and Slave_SQL_Running being Yes indicates normal master-slave synchronization.
  5. Log in to the MySQL Node02 node to configure master-slave synchronization

    /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;
    • In the change master statement, ensure that the master_host address is replaced with the actual MySQL Node01 server IP during deployment.
    • The values for master_log_file and master_log_pos in the change master statement are obtained from the show master status; command executed on the master node. Adjust according to your actual deployment.
  6. Check the master-slave synchronization status

    show slave status\G

    # In the output, both Slave_IO_Running and Slave_SQL_Running being Yes indicates normal master-slave synchronization.
  7. At this point, the synchronization status of both MySQL instances should be active and normal.

Configure Keepalived

Install Keepalived

Both MySQL servers need to install the Keepalived service

yum install -y keepalived

Modify the Keepalived Configuration File

MySQL Node01 Node

Modify the /etc/keepalived/keepalived.conf file as follows

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 # Change to the network interface name of the deployed host
virtual_router_id 180 # Unique among all VRRP routers in the same subnet, range 0-255
priority 100 # Priority, different for each node
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
}
}
  • Note that the priority values in the configuration files of the two nodes are different.
  • By default, you only need to modify the interface and VIP address.

MySQL Node02 Node

Modify the /etc/keepalived/keepalived.conf file as follows

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 # Change to the network interface name of the deployed host
virtual_router_id 180 # Unique among all VRRP routers in the same subnet, range 0-255
priority 90 # Priority, different for each node
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
}
}
  • Note that the priority values in the configuration files of the two nodes are different.
  • By default, you only need to modify the interface and VIP address.

The following MySQL related scripts need to be created on both nodes.

Create a directory for storing the scripts

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

Execute vim /usr/local/mysql/script/check_mysql_health.sh to create the MySQL status check script

The check_mysql_health.sh script content is as follows:

#!/bin/bash

# MySQL related information
mysql_user="root"
mysql_password="123456"
mysql_host="127.0.0.1"
mysql_cli="/usr/local/mysql/bin/mysql"

# Log function
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"
}

# Check configuration
max_retries=3
retry_interval=2

check_mysql_status() {
# Check if MySQL service is running
$mysql_cli -h"$mysql_host" -u"$mysql_user" -p"$mysql_password" <<< 'show status;' > /dev/null 2>&1
mysql_status=$?

# Check if MySQL process is running
ps aux | grep mysqld | grep -v grep | grep -v check_mysql_health > /dev/null 2>&1
mysql_process=$?
}

# Continuously check MySQL service status, retrying up to max_retries times
retry_count=0
while true; do
check_mysql_status

if [ $mysql_status -eq 0 ] && [ $mysql_process -eq 0 ]; then
# Both MySQL service and process are normal, exit the loop
exit 0
else
if [ $retry_count -ge $max_retries ]; then
# Maximum retry attempts reached, perform action to stop Keepalived service and exit
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

Execute vim /usr/local/mysql/script/notify_master.sh to create a script executed by Keepalived when the node is chosen as Master

The notify_master.sh script content is as follows:

#!/bin/bash
# MySQL related information
mysql_user="root"
mysql_password="123456"
mysql_host="127.0.0.1"
mysql_cli="/usr/local/mysql/bin/mysql"

# Log function
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"
}

# Stop the synchronization thread on the current node
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

Execute vim /usr/local/mysql/script/notify_backup.sh to create a script executed by Keepalived when the node is chosen as Backup

The notify_backup.sh script content is as follows:

#!/bin/bash
# MySQL related information
mysql_user="root"
mysql_password="123456"
mysql_host="127.0.0.1"
mysql_cli="/usr/local/mysql/bin/mysql"

# Log function
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"
}

# Start the synchronization thread on the current node
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

Add executable permissions to all scripts

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

Start Keepalived

systemctl start keepalived
systemctl enable keepalived

Recheck MySQL Master-Slave Synchronization and VIP Status

The normal status at this time should be:

  • The synchronization thread of the MySQL on the VIP node is stopped
  • The synchronization thread of the MySQL on the other node is running and normal

Troubleshooting

Both Servers Have VIP

Usually this can be caused by:

  • There is a firewall or other network restriction between the two machines

    • Port is restricted (defaults to using port 112)

    • The network environment doesn't support VRRP (Virtual Router Redundancy Protocol)

  • The two machines cannot communicate with each other’s address

  • The priority set in the configuration for both nodes might be the same, causing a failure to elect a primary node

  • Check the /var/log/messages log file for troubleshooting

Neither Server Has VIP

Usually this can be caused by:

  • The vrrp_script script specified in keepalived.conf is executing abnormally and always returns a non-zero error value, causing the keepalived state to be unable to initialize and bind the VIP.

    • For example, MySQL has not started, or the password in the script to connect to MySQL is incorrect. You can execute the script manually for troubleshooting.
  • Check the /var/log/messages log file for troubleshooting