Skip to main content

Redis 主从 + Keepalived 高可用服务配置

IP角色
192.168.1.13redis Node01
192.168.1.14redis Node02
192.168.1.100VIP

配置 redis

  1. 先参考 redis 主从文档完成部署

  2. 修改 Master 节点 redis.conf 文件,增加 slaveof 参数同步从节点

    slaveof 192.168.1.14 6379

配置 Keepalived

安装 keepalived

yum install -y keepalived

修改 keepalived 配置文件

Redis Node01 节点

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

global_defs {
router_id hap-redis-ha-01
}

vrrp_script check_redis_health {
script "/usr/local/redis/script/check_redis_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-redis-Keepalived-Auth
}
track_script {
check_redis_health
}
notify_master /usr/local/redis/script/notify_master.sh
notify_backup /usr/local/redis/script/notify_backup.sh
virtual_ipaddress {
192.168.1.100 # VIP address
}
}
  • 注意两个节点配置文件中 priority 的值是不同的
  • 默认仅需修改 interface 与 VIP address 即可

Redis Node02 节点

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

global_defs {
router_id hap-redis-ha-01
}

vrrp_script check_redis_health {
script "/usr/local/redis/script/check_redis_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-redis-Keepalived-Auth
}
track_script {
check_redis_health
}
notify_master /usr/local/redis/script/notify_master.sh
notify_backup /usr/local/redis/script/notify_backup.sh
virtual_ipaddress {
192.168.1.100 # VIP address
}
}
  • 注意两个节点配置文件中 priority 的值是不同的
  • 默认仅需修改 interface 与 VIP address 即可

创建 redis 相关脚本

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

创建脚本存放目录

mkdir -p /usr/local/redis/script/

执行 vim /usr/local/redis/script/check_redis_health.sh 创建 redis 状态检查脚本

check_redis_health.sh 脚本内容如下:

#!/bin/bash

# Redis 相关信息
redis_cli="/usr/local/redis/bin/redis-cli"
redis_host="127.0.0.1"
redis_port="6379"
redis_password="123456"

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

# 检查配置
max_retries=3
retry_interval=2

check_redis_status() {
# 检查 Redis 服务是否在运行中
$redis_cli -h $redis_host -p $redis_port -a $redis_password ping > /dev/null 2>&1
redis_status=$?
}

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

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

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

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

notify_master.sh 脚本内容如下:

#!/bin/bash

# Redis 相关信息
redis_host="127.0.0.1"
redis_port="6379"
redis_password="123456"
redis_cli="/usr/local/redis/bin/redis-cli"
redis_config="/usr/local/redis/redis.conf"

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

# 停止当前节点的同步线程
if $redis_cli -h $redis_host -p $redis_port -a $redis_password <<< "slaveof no one"; then
log_info "The current node becomes master, successfully stopped redis master-slave replication on the current node."
else
log_info "The current node becomes master, failed to stop redis master-slave replication on the current node."
fi

# 注释掉当前节点的 slaveof
if sed -i '/^slaveof/ s/^/#/' $redis_config; then
log_info "Successfully commented out the slaveof for the current node."
else
log_info "Failed to comment out the slaveof for the current node."
fi

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

notify_backup.sh 脚本内容如下:

#!/bin/bash

# Redis 相关信息
redis_host="127.0.0.1"
redis_port="6379"
redis_password="123456"
redis_cli="/usr/local/redis/bin/redis-cli"
redis_config="/usr/local/redis/redis.conf"
redis_master_host="192.168.1.13"

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

# 启动当前节点的同步线程
if $redis_cli -h $redis_host -p $redis_port -a $redis_password <<< "slaveof $redis_master_host 6379"; then
log_info "The current node becomes backup, successfully started redis master-slave replication on the current node."
else
log_info "The current node becomes backup, failed to start redis master-slave replication on the current node."
fi

# 打开当前节点的 slaveof 注释
if sed -i '/^#slaveof/ s/^#//' $redis_config; then
log_info "Successfully uncommented the slaveof configuration for the current node."
else
log_info "Failed to uncomment the slaveof configuration for the current node."
fi
  • redis_master_host 两个节点的配置文件中,这个变量值不是相同的,注意替换为另外一台 redis 的 IP 地址

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

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

启动 Keepalived

systemctl start keepalived
systemctl enable keepalived

测试

可以通过重启 keepalived 来观察是否符合预期

正常现象:

  • VIP 所在节点,redis 为 master 状态, redis.conf 文件 slaveof 参数是注释的状态

  • 非 VIP 所在节点,redis 为 slave 同步状态, redis.conf 文件 slaveof 参数是非注释的状态

故障排除

两台服务器都有 VIP

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

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

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

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

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

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

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

两台服务器都没有 VIP

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

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

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