keepalived

Keepalived提供了兼具负载均衡和高可用性的框架

这里以mysql的主从切换为例,实现了从节点自动切换为主库(从节点检测主节点keepalived心跳)

主节点

/etc/keepalived/keepalived.conf
! Configuration File for keepalived

global_defs {
   router_id MYSQL_MASTER  # 标识主节点,唯一即可
}

# 定义 MySQL 检测脚本
vrrp_script check_mysql {
    script "/data/app/mysql/keepalived_check_mysql.sh"  # 检测脚本路径
    interval 5                               # 检测间隔(秒)
    fall 5                                   # 连续5次失败,判定为故障
    rise 1                                   # 连续1次成功,恢复正常
}

# VRRP 实例(核心:关闭 IP 漂移)
vrrp_instance VI_1 {
    state MASTER  # 主节点标识
    interface eth0  # 网卡名称(根据实际修改,如 ens33)
    virtual_router_id 51  # VRRP 组 ID(主从必须一致,0-255)
    priority 100  # 主节点优先级(高于从节点)
    advert_int 1  # 心跳发送间隔(秒)
    nopreempt     # 关闭抢占(避免主恢复后抢回主身份,可选)

    # 单播模式
    # 自己 IP
    unicast_src_ip 10.33.9.38
    # 从节点 IP
    unicast_peer {
        10.33.9.39
    }

    # 关联检测脚本
    track_script {
        check_mysql
    }

    # 当进入故障状态(即连续失败 2 次后)执行此脚本
    notify_fault "/data/app/mysql/stop_keepalived.sh"
}
/data/app/mysql/keepalived_check_mysql.sh
#!/bin/bash
# 配置项
MYSQL_USER="root"
MYSQL_PWD="password"
MYSQL_PORT="3306"
MYSQL_FOLDER="/data/app/mysql"
LOG_FILE="${MYSQL_FOLDER}/log-${MYSQL_PORT}/keepalived_mysql.log"

# 日志函数
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> ${LOG_FILE}
}

# 检测 MySQL 存活
docker run -i -v ${MYSQL_FOLDER}/data-${MYSQL_PORT}/mysql/mysql.sock:/tmp/mysql.sock --rm mysql:8.0 mysqladmin -u${MYSQL_USER} -p${MYSQL_PWD} -S /tmp/mysql.sock ping > /dev/null 2>&1
if [ $? -ne 0 ]; then
    log "MySQL 主库故障(ping 失败)"
    exit 1
else
    log "MySQL 主库运行正常"
    exit 0
fi
/data/app/mysql/stop_keepalived.sh
#!/bin/bash
LOG_FILE="/data/logs/keepalived.log"

mkdir -p /data/logs/

# 日志函数
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> ${LOG_FILE}
}

# 停止 Keepalived,停止发送 VRRP 心跳
systemctl stop keepalived
log "Keepalived 已停止,主节点心跳终止"
exit 1

从节点

/etc/keepalived/keepalived.conf
! Configuration File for keepalived

global_defs {
   router_id MYSQL_SLAVE
}

vrrp_instance VI_1 {
    state BACKUP          # 固定为备节点
    interface eth0        # 与主节点一致的网卡
    virtual_router_id 51  # 与主节点一致
    priority 90           # 优先级低于主节点
    advert_int 1          # 心跳间隔1秒

    # 单播模式
    # 自己 IP
    unicast_src_ip 10.33.9.39
    # 主节点 IP
    unicast_peer {
        10.33.9.38
    }

    # 核心:成为主节点时执行升主脚本
    notify_master "/data/app/mysql/promote_to_master.sh"
}
/data/app/mysql/promote_to_master.sh
#!/bin/bash
# 配置项
MYSQL_USER="root"
MYSQL_PWD="password"
MYSQL_PORT="3306"
MYSQL_FOLDER="/data/app/mysql"
LOG_FILE="${MYSQL_FOLDER}/log-${MYSQL_PORT}/keepalived_mysql.log"

log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> ${LOG_FILE}
}

# 防止重复执行(加锁)
LOCK_FILE="/tmp/mysql_promote.lock"
if [ -f ${LOCK_FILE} ]; then
    log "升主脚本已在执行,跳过"
    exit 0
fi
touch ${LOCK_FILE}

# 检测 MySQL 存活
docker run -i -v ${MYSQL_FOLDER}/data-${MYSQL_PORT}/mysql/mysql.sock:/tmp/mysql.sock --rm mysql:8.0 mysqladmin -u${MYSQL_USER} -p${MYSQL_PWD} -S /tmp/mysql.sock ping > /dev/null 2>&1
if [ $? -ne 0 ]; then
    log "MySQL 从库故障(ping 失败),不切换成主库"
    exit 1
else
    # 核心:MySQL 升主 SQL
    log "开始执行升主操作..."
    docker run -i -v ${MYSQL_FOLDER}/data-${MYSQL_PORT}/mysql/mysql.sock:/tmp/mysql.sock --rm mysql:8.0 \
        mysql -u${MYSQL_USER} -p${MYSQL_PWD} -S /tmp/mysql.sock -e "
STOP SLAVE;
RESET SLAVE ALL;
SET GLOBAL read_only = OFF;
SET GLOBAL super_read_only = OFF;
    " >> ${LOG_FILE} 2>&1

    log "升主完成"
    rm -f ${LOCK_FILE}
    exit 0
fi