
目录
背景
Redis 高可用我们常用的是主备+哨兵、cluster集群来实现高可用和高可靠,除此之外,我们也可通过Keepalived + Redis主备的方式来实现高可用。
具体实现功能:
- 客户端统一 VIP 入口读写;
- 主备切换(故障转移),保证业务可用性;
- 主备节点重启后(任意节点重启或同时重启),仍可继续通过 VIP 对外提供服务。
Redis 版本:v7.0.9
Keepalived 版本:v2.3.2
OS 版本:openEuler 22.03 LTS
架构
主备架构图如下:

流程
主备高可用(故障转移)流程图如下:

实现
Redis
部署
主:192.168.1.113
备:192.168.1.114
部署过程跳过,不作为重点。需要注意的是:该方式与传统的主备部署有些许差别,主备节点部署时均以单机方式部署(即部署时主备均为 master 节点,可读写),不通过配置文件来配置主备,而是通过 keepalived 的钩子脚本来动态配置 redis 主备,这也是本次的重点。
配置
主备 redis 实例配置应用程序连接密码(主备配置保持完全一致):
sh
vim redis.conf
sh
...
requirepass Asen!@#123
...
注意和主备数据同步连接时的密码(masterauth)的区别,和 requirepass 不是一个东西,masterauth 是用于主备连接时的鉴权密码,这个密码会通过 keepalived 的钩子脚本动态配置,因此这种配置的主备是临时的,redis实例或服务器重启后主备就会失效,但由于 keepalived 的角色切换都会触发其钩子脚本(包括开关机),因此基于该特性,我们可在钩子脚本中通过命令动态配置 redis 主备,进而弥补 redis 临时主备配置时重启失效的问题。
Keepalived
部署
主:192.168.1.113
备:192.168.1.114
部署过程跳过,不作为重点,但是主备配置文件、启动配置文件需要作为重点。
配置
主节点配置
sh
vim keepalived.conf
sh
! Configuration File for keepalived
global_defs {
router_id redis-master # 主备唯一
}
vrrp_script chk_server {
script "/data/keepalived/check/chk_redis.sh" # redis状态检测脚本
interval 5
fall 3
rise 3
}
vrrp_instance VI_1 {
state BACKUP # 非抢占模式下主备均设置为BACKUP(详情请查看官方文档)
interface eth0 # VRRP检测网卡(即通过那个网卡发心跳,可自定义)
virtual_router_id 91 # 虚拟路由IP,主备需保持一致
priority 50 # 权重:主大于备,主50,备40
advert_int 1
nopreempt # VIP非抢占模式
authentication {
auth_type PASS
auth_pass Arsen@123
}
virtual_ipaddress {
192.169.1.235/24 dev eth0 # 设置VIP,并绑定到eth0网卡
}
track_script {
chk_redis # 调用redis状态检测脚本
}
track_interface {
eth0 # 网卡状态检测
}
notify_master /data/keepalived/notify/notify_master.sh # keepalived为主时触发该脚本,redis主备切换的关键
notify_backup /data/keepalived/notify/notify_backup.sh # keepalived为备时触发该脚本,redis主备切换的关键
}
sh
vim notify_master.sh
sh
#!/bin/bash
# 脚本功能:
# 1.keepalived提升为MASTER时触发该脚本
# 2.配置主备鉴权密码(CONFIG SET masterauth)
# 3.将当前redis实例提升为master,等待对端redis实例同步当前实例的数据(REPLICAOF NO ONE)
# 脚本已经被我精简过了,生产中需要加上相关日志记录
redis-cli -h 192.168.1.113 -p 6379 -a Asen!@#123 CONFIG SET masterauth "Asen!@#456"
redis-cli -h 192.168.1.113 -p 6379 -a Asen!@#123 REPLICAOF NO ONE
sh
vim notify_backup.sh
sh
#!/bin/bash
# 脚本功能:
# 1.keepalived提升为BACKUP时触发该脚本
# 2.配置主备鉴权密码(CONFIG SET masterauth)
# 3.将当前redis实例降为slave,并同步对端redis实例的数据(REPLICAOF)
# 脚本已经被我精简过了,生产中需要加上相关日志记录
redis-cli -h 192.168.1.113 -p 6379 -a Asen!@#123 CONFIG SET masterauth "Asen!@#456"
redis-cli -h 192.168.1.113 -p 6379 -a Asen!@#123 REPLICAOF 192.168.1.114 6379
备节点配置
sh
vim keepalived.conf
sh
! Configuration File for keepalived
global_defs {
router_id redis-slave
}
vrrp_script chk_server {
script "/data/keepalived/check/chk_redis.sh"
interval 5
fall 3
rise 3
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 91
priority 40
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass Arsen@123
}
virtual_ipaddress {
192.169.1.235/24 dev eth0
}
track_script {
chk_redis
}
track_interface {
eth0
}
notify_master /data/keepalived/notify/notify_master.sh
notify_backup /data/keepalived/notify/notify_backup.sh
}
sh
vim notify_master.sh
sh
#!/bin/bash
# 脚本功能:
# 1.keepalived提升为MASTER时触发该脚本
# 2.配置主备鉴权密码(CONFIG SET masterauth)
# 3.将当前redis实例提升为master,等待对端redis实例同步当前实例的数据(REPLICAOF NO ONE)
# 脚本已经被我精简过了,生产中需要加上相关日志记录
redis-cli -h 192.168.1.114 -p 6379 -a Asen!@#123 CONFIG SET masterauth "Asen!@#456"
redis-cli -h 192.168.1.114 -p 6379 -a Asen!@#123 REPLICAOF NO ONE
sh
vim notify_backup.sh
sh
#!/bin/bash
# 脚本功能:
# 1.keepalived提升为BACKUP时触发该脚本
# 2.配置主备鉴权密码(CONFIG SET masterauth)
# 3.将当前redis实例降为slave,并同步对端redis实例的数据(REPLICAOF)
# 脚本已经被我精简过了,生产中需要加上相关日志记录
redis-cli -h 192.168.1.114 -p 6379 -a Asen!@#123 CONFIG SET masterauth "Asen!@#456"
redis-cli -h 192.168.1.114 -p 6379 -a Asen!@#123 REPLICAOF 192.168.1.113 6379
Systemd 管理配置
至此,已经可以实现在主备服务器不同时重启情况下的高可用了。
但是主备服务器同时重启的话,极有可能主备的 redis 实例的状态均为 master,为什么呢?试想一下:如果 keepalived 先于 redis 启动,那 keepalived 就会先提前执行其钩子脚本,此时钩子脚本对 redis 实例的一系列操作均是无效的(因为 redis 服务还没启动完成),等 redis 服务启动完成后,主备就没有生效。
因此为了实现真正的高可用,我们还需要设置一下 keepalived 和 redis 的启动顺序,保证 redis 先启动完成后再启动 keepalived,这样 keepalived 的钩子脚本就对 redis 起作用了(即完成 redis 主备的配置)。
但是 keepalived 不可能一直等待 redis 实例启动完成后再启动,如果 redis 本身就异常(如文件系统损坏)无法启动,这样的话 keepalived 将一直处于等待状态,因此我们在配置 keepalived 启动文件时,可加上一个超时时间,如 1 分钟,如果 1 分钟后 redis 实例仍未启动,则放弃等待直接进入后续启动。
具体配置如下:
sh
vim /etc/systemd/system/keepalived.service
sh
[Unit]
Description=LVS and VRRP High Availability Monitor
After=network-online.target syslog.target
Wants=network-online.target
Documentation=man:keepalived(8)
Documentation=man:keepalived.conf(5)
Documentation=man:genhash(1)
Documentation=https://keepalived.org
[Service]
Type=forking
PIDFile=/run/keepalived.pid
KillMode=process
EnvironmentFile=-/data/keepalived/etc/sysconfig/keepalived
ExecStartPre=/bin/bash -c 'for i in {1..60}; do if ss -tln | grep -q ":6379"; then echo "Port 6379 is ready"; break; fi; sleep 1; done'
ExecStart=/data/keepalived/sbin/keepalived -f /data/keepalived/etc/keepalived/keepalived.conf $KEEPALIVED_OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target
1 分钟过后不管 redis 实例是否启动成功,则会继续执行 ExecStart 部分,启动 keepalived,这种情况只能人工介入修复 redis 主备。
验证
场景:主 redis 实例异常、网卡/线异常或所在服务器宕机
初始状态:主 113, 备 114。
模拟故障:
-
关闭 113 的 redis 实例
shsystemctl stop redis -
分别查看 113、114 的 keepalived 的日志
sh# 113日志(keepalived.log) # 日志分析: # Dec 27 00:04:06 停止了redis实例 VRRP_Script(chk_redis) failed (exited with status 1) # Dec 27 00:04:06 进入了FAULT状态,注意不是BACKUP状态哦 (VI_1) Entering FAULT STATE # Dec 27 00:04:06 发送了优先级为 0 的 VRRP 通告包,表示"放弃主节点角色" (VI_1) sent 0 priority # Dec 27 00:04:06 释放VIP (VI_1) removing VIPs. # 注意:此时并不会执行notify_backup脚本,而是只会执行notify_fault脚本(但我们并没有编写该脚步,因此略过) # Dec 27 00:06:11 这个时间点我们恢复了redis实例,检测脚本返回值为0 Script `chk_server` now returning 0 # Dec 27 00:06:21 检查脚本正常 VRRP_Script(chk_server) succeeded # Dec 27 00:06:21 随着进入BACKUP状态,此时就会触发notify_backup脚本 (VI_1) Entering BACKUP STATE ... Dec 27 00:04:06 tai Keepalived_vrrp[4261]: VRRP_Script(chk_redis) failed (exited with status 1) Dec 27 00:04:06 tai Keepalived_vrrp[4261]: (VI_1) Entering FAULT STATE Dec 27 00:04:06 tai Keepalived_vrrp[4261]: (VI_1) sent 0 priority Dec 27 00:04:06 tai Keepalived_vrrp[4261]: (VI_1) removing VIPs. ... Dec 27 00:06:11 tai Keepalived_vrrp[4261]: Script `chk_server` now returning 0 Dec 27 00:06:21 tai Keepalived_vrrp[4261]: VRRP_Script(chk_server) succeeded Dec 27 00:06:21 tai Keepalived_vrrp[4261]: (VI_1) Entering BACKUP STATE ... # 114日志(keepalived.log) # 日志分析: # Dec 27 00:04:06 当前节点(备节点)收到了主节点发送的优先级为0的通告 (VI_1) Backup received priority 0 advertisement # Dec 27 00:04:07 备份节点在预期时间内没有再收到任何VRRP通告(即超时了)(VI_1) Receive advertisement timeout # Dec 27 00:04:07 进入MASTER状态,触发notify_backup脚本 (VI_1) Entering MASTER STATE # Dec 27 00:04:07 绑定VIP (VI_1) setting VIPs. # Dec 27 00:04:07 准备在eth0网卡上为IP地址192.168.1.235发送ARP(即正在发送或排队发送ARP包)Sending/queueing # Dec 27 00:04:07 正在发送免费ARP包,通知整个网络段:"192.168.1.235 的MAC地址已经改变了" Sending gratuitous ARP ... Dec 27 00:04:06 tai Keepalived_vrrp[4301]: (VI_1) Backup received priority 0 advertisement Dec 27 00:04:07 tai Keepalived_vrrp[4301]: (VI_1) Receive advertisement timeout Dec 27 00:04:07 tai Keepalived_vrrp[4301]: (VI_1) Entering MASTER STATE Dec 27 00:04:07 tai Keepalived_vrrp[4301]: (VI_1) setting VIPs. Dec 27 00:04:07 tai Keepalived_vrrp[4301]: (VI_1) Sending/queueing gratuitous ARPs on eth0 for 192.168.1.235 Dec 27 00:04:07 tai Keepalived_vrrp[4301]: Sending gratuitous ARP on eth0 for 192.168.1.235 ... -
分别查看 113、114 的 keepalived 的钩子脚本日志
sh我的脚本没有输出日志,这里就不看了
总结
实践是检验真理的唯一标准!欢迎前来探讨,共同学习,共同进步。