前言
Redis高可用常见的方式有两种:
- 主从复制(Replication-Sentinel模式),至少需要3台服务器,1个Master节点,2个Slave节点,3个Sentinel节点,当有2个Sentinel认为Master挂了,则把其中1个Slave调整为Master
- Redis集群(Redis-Cluster模式)Slave节点,至少需要6台服务器,即3个Master节点,每个Master至少都有1个Slave节点
- 本文主要介绍redis主从+哨兵实现高可用
- 我这里先准备3台服务器,安装目录为
/www/server/redis
IP地址 redis版本(其他版本操作方式一致)
192.168.88.5(主) redis 6.0.10
192.168.88.6(从) redis 6.0.10
192.168.88.7(从) redis 6.0.10
建议先通读本文后在实操
安装redis
...略
配置防火墙
# 查看防火墙状态
systemctl status firewalld
# 启用防火墙
systemctl start firewalld
# 停用防火墙
systemctl stop firewalld
# 设置开机时启用防火墙
systemctl enable firewalld.service
# 设置开机时不启用防火墙
systemctl disable firewalld.service
# 防火墙开放6379端口
firewall-cmd --zone=public --add-port=6379/tcp --permanent
# 防火墙开放26379端口
firewall-cmd --zone=public --add-port=26379/tcp --permanent
# 重启防火墙使配置生效
systemctl restart firewalld
建议先关闭防火墙
配置主从
- 连接某台服务器做主节点,我这里使用192.168.88.5做主节点
# 进入redis安装目录
cd /www/server/redis
# 备份配置文件
mv redis.conf bak_redis.conf
# 创建新的配置文件
vim redis.conf
- 编辑配置文件内容,内容如下,其中带注释的配置需要你评估是否要修改,其余配置是redis 6.0.10的默认配置内容
requirepass 123123 # redis密码,不需要则删除该行
masterauth 123123 # 不需要密码则删除该行,哨兵模式需要用到
bind 0.0.0.0 # 所有ip可以访问redis
port 6379 # 端口
logfile /www/server/redis/logs/redis.log # 新建目录和日志文件
pidfile var/run/redis.pid
dir ./
dbfilename "dump.rdb"
protected-mode yes
maxclients 10000
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
supervised no
loglevel notice
databases 16
always-show-logo yes
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
replica-priority 100
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4kb
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
- 重复以上步骤配置从节点服务器,从节点的redis.conf,只需额外添加如下内容
replicaof 192.168.88.5 6379 # 主节点的ip+端口,如果你的redis版本小于5.0,则把replicaof改为slaveof
- 分别启动配置好的三个redis节点
# 启动redis
./src/redis-server ./redis.conf
- 查看主从配置结果
# -a 指定密码连接redis
./src/redis-cli -a 123123
# 查看主从关系
info Replication
- 如下可以看到当前role是master以及从节点的ip和端口等信息
[root@localhost ~]# cd /www/server/redis
[root@localhost redis]# ./src/redis-cli -a 123123
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> info Replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.88.6,port=6379,state=online,offset=1688303,lag=0
slave1:ip=192.168.88.7,port=6379,state=online,offset=1688303,lag=0
master_replid:f035e75692e9f119d2c095fd4c79e606146b4c4f
master_replid2:bbd9b99985d844bc3e4031de95e9601b7d0513db
master_repl_offset:1688303
second_repl_offset:1653771
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:639728
repl_backlog_histlen:1048576
配置哨兵
- 所有哨兵配置文件是一样的,所以每台服务器重复以下操作
# 进入redis安装目录
cd /www/server/redis
# 备份配置文件
mv sentinel.conf bak_sentinel.conf
# 创建新的配置文件
vim sentinel.conf
- 编辑配置文件内容,内容如下
bind 0.0.0.0
port 26379
daemonize yes
pidfile /var/run/redis-sentinel.pid
logfile /www/server/redis/logs/sentinel.log # 新建目录和日志文件
dir ./
sentinel deny-scripts-reconfig yes
sentinel monitor mymaster 192.168.88.5 6379 2 # 配置主节点的ip和端口,2表示当有2个哨兵认为master失效,master才算真正失效
sentinel auth-pass mymaster 123123 # 访问密码
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 15000
- 分别启动配置好的三个哨兵节点
# 启动redis
./src/redis-sentinel ./sentinel.conf
- 查看哨兵配置结果
# -p 指定哨兵端口连接redis sentinel
./src/redis-cli -p 26379
# 使用哨兵查看master信息
sentinel master mymaster
# 使用哨兵查看slaves信息
sentinel slaves mymaster
验证哨兵自动故障转移
- 连接Master节点服务器,当前这里是192.168.88.5
- kill掉Master节点,注意不是kill哨兵
- 通过哨兵查看新的master,
./src/redis-cli -p 26379
, 如下发现新的Master节点为192.168.88.6
[root@localhost redis]# ps -ef | grep redis
redis 17087 1 0 01:28 ? 00:00:00 /www/server/redis/src/redis-server 0.0.0.0:6379
root 17244 1 1 01:28 ? 00:00:01 /www/server/redis/src/redis-sentinel 0.0.0.0:26379 [sentinel]
root 17499 2227 0 01:30 pts/0 00:00:00 grep --color=auto redis
[root@localhost redis]# kill -9 17087
[root@localhost redis]# ./src/redis-cli -p 26379
127.0.0.1:26379> sentinel master mymaster
1) "name"
2) "mymaster"
3) "ip"
4) "192.168.88.6"
5) "port"
6) "6379"
也可以查看哨兵日志文件
tailf ./logs/sentinel.log
应用高可用配置
当哨兵发现Master节点挂掉,会自动把其中一个Slave节点选举为Master节点,这个时候Master节点的ip就变了,所以当前我们还没有实现高可用,以下有两种方式处理ip变化
方式一:应用配置多个哨兵,通过哨兵获取真实的Master节点
- 如下是spingboot应用集成redis哨兵的配置方式,代码层面不需要改动
spring:
redis:
password: 123123
sentinel:
master: mymaster
nodes: 192.168.88.5:26379,192.168.88.6:26379,192.168.88.7:26379
- 其他应用可能涉及到代码改造,请自行查找相关文档
方式二:VIP方式,当哨兵发现故障,把新的Master对应的ip到绑定虚拟ip上
注:VIP方式需要使用ROOT权限,若安全管理要求不允许使用ROOT则只能使用第一种方式
- 连接Master节点对应的服务器,手动给Master添加虚拟ip
# 查看网卡类型,即将用到,我这里是ens32,Centos6及以下默认为eth0
ls /etc/sysconfig/network-scripts | grep ifcfg-
# 这里我设置的虚拟ip是192.168.88.88
/sbin/ip addr add 192.168.88.88 dev ens32
sbin/arping -q -c 3 -U 192.168.88.88 -I ens32
# 查看虚拟ip是否设置成功
ip addr
# 测试,使用虚拟ip连接redis
/www/server/redis/src/redis-cli -h 192.168.88.88 -a 123123
- 分别给三台服务器创建脚本
# 创建脚本文件
touch /scripts/notify_master.sh
# 分配可执行权限
chmod +x /scripts/notify_master.sh
# 编辑文件
vim /scripts/notify_master.sh
- 编辑脚本内容如下,注意LOCAL_IP每台服务器都不一样,也就是每台服务器的这个配置文件都不一样
#!/bin/bash
MASTER_IP=$6 #哨兵调用该脚本会传6个参数,第6个参数是新主redis的ip地址
LOCAL_IP='192.168.88.5' #本机ip,注意每个服务器都不一样
VIP='192.168.88.88' #虚拟ip
NETMASK='24'
INTERFACE='ens32' #网卡类型
if [ ${MASTER_IP} = ${LOCAL_IP} ];then
/sbin/ip addr add ${VIP}/${NETMASK} dev ${INTERFACE} #将VIP绑定到该服务器上
/sbin/arping -q -c 3 -U ${VIP} -I ${INTERFACE}
exit 0
else
/sbin/ip addr del ${VIP}/${NETMASK} dev ${INTERFACE} #将VIP从该服务器上删除
exit 0
fi
exit 1 #如果返回1,sentinel会一直执行这个脚本
注1:使用命令
/scripts/notify_master.sh 1 1 1 1 1 192.168.88.5
添加虚拟ip,验证脚本是否可以正常执行,其中192.168.88.5
为本机ip, 因为脚本是获取$6(第6个参数),这里的 1 1 1 1 1是参数填充
注2:当哨兵选举新的Master后,会调用上面脚本,当${MASTER_IP}
等于${LOCAL_IP}
会给当前服务器添加虚拟ip,否则删除虚拟ip
- 编辑每个哨兵配置文件
sentinel.conf
,最后一行添加如下内容
sentinel client-reconfig-script mymaster /scripts/notify_master.sh
- 停止所有哨兵服务,然后重启所有哨兵
- 停止Master节点,查看虚拟ip是否绑定到新Master对应的服务器上
# 每台服务器分别执行ip addr查看那台服务器绑定了虚拟ip
ip addr | grep 88
- 最后,应用通过虚拟ip连接redis即可
使用chkconfig设置开机启动
设置哨兵开机启动
- 创建脚本
cd /etc/rc.d/init.d
vim redis-sentinel
- 编辑脚本内容
#!/bin/sh
#chkconfig: 2345 80 90
#description: Redis Sentinel Service
REDISPORT=26379
REDISPATH=/www/server/redis #注意修改为你的redis安装目录
EXEC=${REDISPATH}/src/redis-sentinel
CLIEXEC=${REDISPATH}/src/redis-cli
CONF="${REDISPATH}/sentinel.conf"
PIDFILE=/var/run/redis-sentinel.pid #注意修改为你的哨兵pid文件
case "$1" in
start)
if [ -f $PIDFILE ]
then
echo "$PIDFILE exists, process is already running or crashed"
else
echo "Starting Redis sentinel..."
$EXEC $CONF
fi
;;
stop)
if [ ! -f $PIDFILE ]
then
echo "$PIDFILE does not exist, process is not running"
else
PID=$(cat $PIDFILE)
echo "Stopping ..."
$CLIEXEC -p $REDISPORT shutdown
while [ -x /proc/${PID} ]
do
echo "Waiting for Redis-sentinel to shutdown ..."
sleep 1
done
echo "Redis-sentinel stopped"
fi
;;
*)
echo "Please use start or stop as first argument"
;;
esac
- 设置开机启动
# 给脚本添加可执行权限
chmod +x redis-sentinel
# 验证脚本能否关闭哨兵服务
./redis-sentinel stop
# 验证脚本能否启动哨兵服务
./redis-sentinel start
# 把哨兵添加到chkconfig列表中
chkconfig --add redis-sentinel
# 设置哨兵服务开机启动
chkconfig redis-sentinel on
设置redis开机启动
- 创建脚本
cd /etc/rc.d/init.d
vim redis
- 编辑脚本内容
#!/bin/sh
# chkconfig: 2345 56 26
# description: Redis Service
### BEGIN INIT INFO
# Provides: Redis
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts Redis
# Description: starts the BT-Web
### END INIT INFO
# Simple Redis init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.
REDISPATH=/www/server/redis #注意修改为你的redis安装目录
PIDFILE=/www/server/redis/redis.pid #注意修改为你的redis pid文件
CONF="${REDISPATH}/redis.conf"
REDISPORT=$(cat $CONF |grep port|grep -v '#'|awk '{print $2}')
REDISPASS=$(cat $CONF |grep requirepass|grep -v '#'|awk '{print $2}')
REDISHOST=$(cat $CONF |grep bind|grep -v '#'|awk '{print $2}')
if [ "$REDISPASS" != "" ];then
REDISPASS=" -a $REDISPASS"
fi
if [ -f "${REDISPATH}/start.pl" ];then
STARPORT=$(cat ${REDISPATH}/start.pl)
else
STARPORT="6379"
fi
EXEC=${REDISPATH}/src/redis-server
CLIEXEC="${REDISPATH}/src/redis-cli -h ${REDISHOST} -p ${REDISPORT}${REDISPASS}"
echo "${REDISPATH}/src/redis-cli -h ${REDISHOST} -p ${REDISPORT}${REDISPASS}"
redis_start(){
if [ -f "${REDISPATH}/redis.pid" ]; then
ps -p $(cat ${PIDFILE}) > /dev/null 2>&1
if [ $? -ne "0" ]; then
rm -f ${PIDFILE}
else
echo "redis is running! ($(cat ${PIDFILE}))"
exit 0
fi
fi
echo "Starting redis server..."
sudo -u redis $EXEC $CONF
echo ${REDIS_PORT} > ${REDISPATH}/start.pl
echo "Starting redis success!"
}
redis_status(){
if [ -f "${REDISPATH}/redis.pid" ]; then
ps -p $(cat ${PIDFILE}) > /dev/null 2>&1
if [ $? -ne "0" ]; then
echo "Redis is not running, buy pid file is exits ${PIDFILE}"
exit 1
else
echo "redis is running! ($(cat ${PIDFILE}))"
exit 0
fi
else
echo "redis is stopped"
exit 0
fi
}
redis_stop(){
echo "Stopping ..."
$CLIEXEC shutdown
sleep 1
pkill -9 redis-server
rm -f ${PIDFILE}
echo "Redis stopped"
}
case "$1" in
start)
redis_start
;;
stop)
redis_stop
;;
status)
redis_status
;;
restart|reload)
redis_stop
sleep 0.3
redis_start
;;
*)
echo "Please use start or stop as first argument"
;;
esac
- 设置开机启动
# 给脚本添加可执行权限
chmod +x redis
# 验证脚本能否关闭redis
./redis stop
# 验证脚本能否启动redis
./redis start
# 把redis添加到chkconfig列表中
chkconfig --add redis
# 设置redis服务开机启动
chkconfig redis on
其他
# 查看所有chklist中服务
chkconfig --list
# 关闭xxx开机启动
chkconfig xxx off
# 把xxx从chkconfig列表中删除
chkconfig --del xxx
最后
- 至此已经实现redis高可用
- 参考1:Redis高可用技术解决方案
- 参考2:redis主从+哨兵+vip实现高可用
- 补充1:本文在做redis高可用时,经常会依次操作每台服务器执行相同的命令,重复操作显然效率低,这里推荐-linux自动化运维工具ansible,批量操作多台服务器执行命令
- 补充2:服务器使用WinSCP传输文件效率低下,如你使用Xshell命令行工具,可以使用如下方式实现文件上传与下载
# 安装lrzsz yum -y install lrzsz rz # 上传 sz fileName # 下载,fileName是你要下载的文件名
安全管理要求
在对服务器安全要求比较严格的情况下,需要考虑以下几点
- redis的安装及操作不能使用root权限
- redis不能使用默认端口
- 配置文件不能使用bind 0.0.0.0,必须制定ip
- 密码必须不能使用弱密码