Redis + Keepalived 实现高可用主从自动切换(踩坑与解决方案)

本文最后更新于 1 分钟前,文中所描述的信息可能已发生改变。

在生产环境中,MySQL 一般都会做主从复制和高可用切换;但如果 Redis 仍然停留在“单机 + 简单从库”的阶段,就很容易出现数据库已经切到新主库,但缓存层没有跟着切换的问题:

  • 应用已经连到新的 MySQL 主库,但 Redis 依然是旧主库的从库
  • 写请求失败或一直打到只读从库,造成缓存穿透、数据不一致

因此,Redis 的主从角色必须与 MySQL 的主从切换保持一致。本文记录一次在 CentOS 7 / Ubuntu 20.04 环境中,使用 Redis 6.x + Keepalived 2.x 搭建高可用、支持秒级切换的实践过程,重点总结踩坑和解决方案。

一、整体环境与目标

  • 操作系统:CentOS 7 / Ubuntu 20.04
  • 组件版本:Redis 6.x、Keepalived 2.x
  • 目标
    • MySQL 使用 Keepalived + 检测脚本实现主从切换
    • Redis 跟随 MySQL 的主从切换,实现:
      • 主节点故障后,备节点自动接管 VIP
      • 备机 Redis 自动提升为主库(SLAVEOF NO ONE
      • 原主恢复后,根据策略可降为从库

1.1 拓扑架构

节点IP角色
Node A10.111.101.3MySQL 主 + Redis 主(初始)
Node B10.111.101.6MySQL 从 + Redis 从(初始)
VIP10.111.101.8浮动 IP,由 Keepalived 统一管理

Keepalived 通过 MySQL 健康检查脚本(如 check_mysql.sh)判断主库是否可用,触发 VRRP 状态切换;同时通过 notify_master / notify_backup 脚本完成 Redis 主从角色切换。

二、安装 Redis 和 Keepalived

2.1 安装 Redis

Ubuntu/Debian 系统

bash
# 更新软件包列表
sudo apt update

# 安装 Redis
sudo apt install redis-server -y

# 启动 Redis 服务
sudo systemctl start redis-server

# 设置开机自启
sudo systemctl enable redis-server

CentOS/RHEL 系统

bash
# 安装 EPEL 仓库
sudo yum install epel-release -y

# 安装 Redis
sudo yum install redis -y

# 启动 Redis 服务
sudo systemctl start redis

# 设置开机自启
sudo systemctl enable redis

配置 Redis 密码

编辑 Redis 配置文件:

bash
# Ubuntu/Debian
sudo nano /etc/redis/redis.conf

# CentOS/RHEL
sudo nano /etc/redis.conf

找到 # requirepass foobared 这一行,去掉注释并设置密码:

conf
requirepass Hc202512

保存后重启 Redis:

bash
# Ubuntu/Debian
sudo systemctl restart redis-server

# CentOS/RHEL
sudo systemctl restart redis

验证密码设置:

bash
redis-cli -a Hc202512 ping
# 应返回 PONG

2.2 安装 Keepalived

Ubuntu/Debian 系统

bash
# 更新软件包列表
sudo apt update

# 安装 Keepalived
sudo apt install keepalived -y

# 启动 Keepalived 服务
sudo systemctl start keepalived

# 设置开机自启
sudo systemctl enable keepalived

CentOS/RHEL 系统

bash
# 安装 Keepalived
sudo yum install keepalived -y

# 启动 Keepalived 服务
sudo systemctl start keepalived

# 设置开机自启
sudo systemctl enable keepalived

2.3 验证安装

检查 Redis 版本:

bash
redis-server --version
# 应显示 Redis 6.x 版本

检查 Keepalived 版本:

bash
keepalived --version
# 应显示 Keepalived 2.x 版本

三、配置 Redis 主从和 Keepalived

3.1 清理 Redis 配置:不要写死主从关系

所有 Redis 节点上,检查配置文件中是否存在主从相关配置:

bash
# Ubuntu/Debian
grep -i "replicaof\|slaveof" /etc/redis/redis.conf

# CentOS/RHEL
grep -i "replicaof\|slaveof" /etc/redis.conf

重要提示:

  • 不要在 redis.conf 中写死 replicaof / slaveof
  • Redis 的主从关系应由 Keepalived 的通知脚本动态控制SLAVEOF 命令),而不是依赖静态配置
  • 这能避免重启后角色"自动反转"或与 Keepalived 状态不一致

3.2 编写 Redis 主从切换脚本

3.2.1 主角色脚本:/etc/keepalived/notify_redis_master.sh

当本节点成为 Keepalived 的 MASTER(拿到 VIP)时执行:

bash
#!/bin/bash
export REDISCLI_AUTH=Hc202512

logger "[Redis-HA] $(hostname): Promoting Redis to MASTER"

# 提升为主库
redis-cli SLAVEOF NO ONE

# 主库可读写
redis-cli CONFIG SET slave-read-only no

echo "master" > /var/run/redis-role

说明:

  • 使用 SLAVEOF NO ONE 将当前节点提升为 Redis 主节点
  • slave-read-only 设置为 no,避免残留只读限制
  • 通过写入 /var/run/redis-role 记录当前 Redis 角色,方便排查

3.2.2 备角色脚本:/etc/keepalived/notify_redis_backup.sh

当本节点降为 BACKUP(丢失 VIP)时执行:

bash
#!/bin/bash
export REDISCLI_AUTH=Hc202512

PEER_IP="10.111.101.3"  # 对端主节点 IP

logger "[Redis-HA] $(hostname): Demoting Redis to REPLICA of $PEER_IP"

# 设置为指定主库的从节点
redis-cli SLAVEOF "$PEER_IP" 6379

# 从库保持只读
redis-cli CONFIG SET slave-read-only yes

echo "replica" > /var/run/redis-role

密码传递方式建议:

建议使用环境变量:

bash
export REDISCLI_AUTH=Hc202512

而不是:

bash
redis-cli -a Hc202512 ...

这样可以避免密码出现在 ps 输出中,更安全。

注意: 若是双主架构或 IP 会变化,需要配合额外脚本动态获取对端 IP,这里示例使用固定 IP。

3.2.3 设置脚本权限

脚本写好之后,需要确保在 Linux 下可以正常执行:

bash
# 添加执行权限
chmod +x /etc/keepalived/notify_redis_*.sh

# 检查首行是否为 Shebang
head -n1 /etc/keepalived/notify_redis_backup.sh

# 若曾在 Windows 编辑过,需转换换行符
dos2unix /etc/keepalived/notify_redis_*.sh

3.3 配置 Keepalived

在 Keepalived 的配置文件(/etc/keepalived/keepalived.conf)的 vrrp_instance 中添加通知脚本:

主节点配置(10.111.101.3):

conf
vrrp_instance VI_1 {
    state MASTER
    interface eno3np2
    virtual_router_id 51
    priority 100
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass 1111
    }

    virtual_ipaddress {
        10.111.101.8/24
    }

    notify_master "/etc/keepalived/notify_redis_master.sh"
    notify_backup "/etc/keepalived/notify_redis_backup.sh"
}

备节点配置(10.111.101.6):

conf
vrrp_instance VI_1 {
    state BACKUP
    interface eno3np2
    virtual_router_id 51
    priority 90
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass 1111
    }

    virtual_ipaddress {
        10.111.101.8/24
    }

    notify_master "/etc/keepalived/notify_redis_master.sh"
    notify_backup "/etc/keepalived/notify_redis_backup.sh"
}

配置说明:

  • 主节点(10.111.101.3)对应的 state 设为 MASTERpriority 为 100
  • 备节点(10.111.101.6)为 BACKUPpriority 为 90
  • 两边都要配置 notify_master / notify_backup,脚本逻辑保持一致,避免脑裂
  • interface 需要根据实际网卡名称修改(可通过 ip addr 查看)

3.4 启动服务并验证

配置修改完成后,分别在两台机器上重启相关服务:

bash
systemctl restart keepalived redis

在备机上检查 Redis 主从状态:

bash
redis-cli -a Hc202512 info replication

预期输出示例:

ini
role:slave
master_host:10.111.101.3
master_link_status:up

检查 VIP 是否在主节点:

bash
ip addr show | grep 10.111.101.8

四、实战中遇到的问题

4.1 问题一:备机 Redis 始终是 master,未连到主节点

在备机执行:

bash
redis-cli INFO replication

输出类似:

ini
role:master
connected_slaves:0

按预期,此节点应该是 Redis 从库,但实际却是 master 且没有连接任何从库

排查后发现:Keepalived 的 notify_backup.sh 脚本根本没有生效,Redis 从未执行过 SLAVEOF,所以一直保持在 master 状态。

4.2 问题二:Keepalived 日志报 Exec format error

查看系统日志:

bash
journalctl -u keepalived -f

可以看到类似报错:

text
Error exec-ing command '/etc/keepalived/notify_redis_backup.sh', error 8: Exec format error

这类错误通常有三个典型原因:

  1. 脚本缺少 Shebang(第一行没有 #!/bin/bash
  2. 脚本为 Windows 格式(CRLF 换行符),Linux 无法正确解释
  3. 脚本无执行权限(缺少 chmod +x

结果就是:Keepalived 虽然配置了 notify_*,但执行失败,Redis 角色无法切换。

4.3 问题三:主节点 Redis 重启后竟然变成 slave

在某次维护重启后,发现原本的 Redis 主节点起来后竟然是 slave 身份:

ini
role:slave
master_host:10.111.101.6

主要有两类原因:

  1. Redis 配置文件中写死了 replicaof(旧版本为 slaveof,导致每次启动都强制变为从库
  2. 上一次故障切换后,未显式执行 SLAVEOF NO ONE 恢复为主,仅依赖了内存状态

这会直接破坏整个集群的主从关系,甚至造成“双主”或“无主”的混乱状态。

五、问题解决方案

5.1 问题一解决方案:确保脚本正确执行

问题现象: 备机 Redis 始终是 master,未连到主节点

排查步骤:

  1. 检查 Keepalived 日志:
bash
journalctl -u keepalived -f
  1. 检查脚本是否存在且可执行:
bash
ls -la /etc/keepalived/notify_redis_*.sh
  1. 手动执行脚本测试:
bash
/etc/keepalived/notify_redis_backup.sh

解决方案:

确保脚本满足以下条件:

bash
# 1. 添加执行权限
chmod +x /etc/keepalived/notify_redis_*.sh

# 2. 检查首行是否为 Shebang
head -n1 /etc/keepalived/notify_redis_backup.sh
# 应输出:#!/bin/bash

# 3. 若曾在 Windows 编辑过,需转换换行符
dos2unix /etc/keepalived/notify_redis_*.sh

# 4. 验证脚本语法
bash -n /etc/keepalived/notify_redis_backup.sh

5.2 问题二解决方案:修复 Exec format error

问题现象: Keepalived 日志报 Exec format error

原因分析:

  1. 脚本缺少 Shebang(第一行没有 #!/bin/bash
  2. 脚本为 Windows 格式(CRLF 换行符),Linux 无法正确解释
  3. 脚本无执行权限(缺少 chmod +x

解决方案:

bash
# 检查文件格式
file /etc/keepalived/notify_redis_backup.sh
# 如果显示 "CRLF" 或 "with CRLF line terminators",需要转换

# 转换换行符
dos2unix /etc/keepalived/notify_redis_*.sh

# 或者使用 sed 命令
sed -i 's/\r$//' /etc/keepalived/notify_redis_*.sh

# 确保有执行权限
chmod +x /etc/keepalived/notify_redis_*.sh

# 验证脚本可以执行
bash /etc/keepalived/notify_redis_backup.sh

5.3 问题三解决方案:清理 Redis 配置中的主从关系

问题现象: 主节点 Redis 重启后竟然变成 slave

排查步骤:

检查 Redis 配置文件中是否写死了主从关系:

bash
# Ubuntu/Debian
grep -i "replicaof\|slaveof" /etc/redis/redis.conf

# CentOS/RHEL
grep -i "replicaof\|slaveof" /etc/redis.conf

解决方案:

  1. 编辑 Redis 配置文件,注释或删除 replicaof / slaveof 行:
bash
# Ubuntu/Debian
sudo nano /etc/redis/redis.conf

# CentOS/RHEL
sudo nano /etc/redis.conf

找到类似这样的行并注释掉:

conf
# replicaof 10.111.101.3 6379
# 或
# slaveof 10.111.101.3 6379
  1. 重启 Redis 服务:
bash
# Ubuntu/Debian
sudo systemctl restart redis-server

# CentOS/RHEL
sudo systemctl restart redis
  1. 验证 Redis 角色:
bash
redis-cli -a Hc202512 info replication | grep role

重要原则:

  • Redis 的主从关系必须由 Keepalived 的通知脚本动态控制
  • 不要在配置文件中写死主从关系
  • 每次故障切换后,确保脚本正确执行了角色切换

5.4 问题排查通用方法

当遇到 Redis 主从状态异常时,按以下顺序排查:

  1. 检查 Keepalived 状态:
bash
systemctl status keepalived
ip addr show | grep 10.111.101.8
  1. 检查 Keepalived 日志:
bash
journalctl -u keepalived -n 50
  1. 检查 Redis 主从状态:
bash
redis-cli -a Hc202512 info replication
  1. 检查脚本是否执行:
bash
cat /var/run/redis-role
journalctl | grep "Redis-HA"
  1. 手动测试脚本:
bash
export REDISCLI_AUTH=Hc202512
/etc/keepalived/notify_redis_master.sh
redis-cli info replication | grep role

六、故障切换演练:验证是否"真高可用"

高可用架构不是“配完就算完”,必须通过故障演练来验证。

4.1 模拟主库 MySQL 故障

在主节点(10.111.101.3)上停止 MySQL:

bash
systemctl stop mysqld

观察备机(10.111.101.6):

  1. VIP 漂移 使用 ip addrip a 确认 10.111.101.8 是否已漂移到备机

  2. Redis 自动升主

    bash
    redis-cli -a Hc202512 info replication | grep role

    期望输出:

    ini
    role:master
  3. 日志记录 在系统日志中查看 Keepalived 输出:

    bash
    journalctl -u keepalived -f

    理想情况下能看到类似日志:

    text
    [Redis-HA] node-b: Promoting Redis to MASTER

4.2 主节点恢复后的行为

当原主节点的 MySQL 和 Redis 恢复后,根据 Keepalived 的配置(是否启用“抢占”)会有不同表现:

  • 若启用抢占(默认):
    • VIP 会重新漂移回优先级高的节点
    • 对应的 notify_* 会再次触发,Redis 角色也会相应变化
  • 若关闭抢占:
    • 当前 MASTER 节点保持不变,原主节点恢复后继续作为备机

无论哪种策略,都建议在演练阶段多次测试,确保 MySQL 与 Redis 的角色始终一致。

七、经验总结与排查要点

可以用一个简化表格来总结本次实践中的关键点:

关键点说明
不要在 redis.conf 写死 replicaof主从关系必须由脚本动态控制
脚本必须可执行#!/bin/bash + chmod +x + dos2unix
密码传递方式要安全推荐 export REDISCLI_AUTH 而非 -a
日志是排查问题的关键journalctl -u keepalived、Redis 日志等
定期进行切换演练避免"纸面高可用",确保真实故障也能扛得住

高可用架构的价值,在于能经得起真正的故障冲击,而不是停留在配置文件和文档上。

八、参考资料

  • Redis 官方文档:
    • https://redis.io/docs/
  • Keepalived 官网:
    • https://www.keepalived.org/

目前该方案在生产环境中已经稳定运行,主从切换基本可以做到秒级完成,业务无明显感知。 如果你也在搭建 Redis 高可用,欢迎参考以上实践,并结合自身环境做进一步优化和扩展。

H3C IRF 堆叠完整配置手册
Docker离线安装完整指南