redis同城双机房容灾
(在集群node创建时,master和slave都会将各自的currentEpoch设置为0,每次从其他node接收到数据包时,如果发现发送者的epoch值比自己的大,那么当前node将自己的currentEpoch设置为发送者的epoch,最终所有的nodes都会认同集群中最大的epoch值;当该slave节点执行 cluster failover命令时,(将切换为新master节点)处理完来自
目录
1、知识准备
1.1 Slave的选举与提升
当slaves节点进行选举时,会在其他masters的帮助下进行投票,选举出一个slave并提升为master。当master处于FAIL状态时,将会触发slave的选举。slaves都希望将自己提升为master, 此master的所有slaves都可以开启选举,不过最终只有一个slave获胜。具体的选举与提升流程如下:
① 满足如下情况slave即可进行选举:
1)当此slave的master处于FAIL状态;
2)此master持有非零个slots;
3)此slave的replication链接与master断开时间没有超过设定值,为了确保此被提升的slave的数据是新鲜的,这个时间用户可以配置。
②slave自增它的currentEpoch值,然后向其他masters请求投票(需求支持,votes)。slave通过向其他masters传播“FAILOVER_AUTH_REQUEST”数据包,然后最长等待2倍的NODE_TIMEOUT时间,来接收反馈。(在集群node创建时,master和slave都会将各自的currentEpoch设置为0,每次从其他node接收到数据包时,如果发现发送者的epoch值比自己的大,那么当前node将自己的currentEpoch设置为发送者的epoch,最终所有的nodes都会认同集群中最大的epoch值;当集群的状态变更,或者node为了执行某个行为需求agreement时,都将需要epoch(传递或者比较)。)
③一旦一个master向此slave投票,将会响应“FAILOVER_AUTH_ACK”,此后在2 * NODE_TIMOUT时间内,它将不会向同一个master的slaves投票;虽然这对保证安全上没有必要,但是对避免多个slaves同时选举时有帮助的。slave将会丢弃那些epoch值小于自己的currentEpoch的AUTH_ACK反馈,即不会对上一次选举的投票计数(只对当前轮次的投票计数)
④一旦此slave获取了大多数master的ACKs,它将在此次选举中获胜;否则如果大多数master不可达(在2 * NODE_TIMEOUT)或者投票额不足,那么它的选举将会被中断,那么其他的slave将会继续尝试。
1.2 主从切换命令
cluster failover [force|takeover]命令解析
该命令只能在群集slave节点执行,让slave节点进行一次人工故障切换。 人工故障切换是预期的操作,而非发生了真正的故障,目的是以一种安全的方式(数据无丢失)将当前master节点和其中一个slave节点(执行cluster-failover的节点)交换角色。
【 cluste failover】
当该slave节点执行 cluster failover命令时,(将切换为新master节点)处理完来自master的所有复制,客户端的访问将会自动由原master节点切换至新master节点。处理的具体流程如下:
1. 当前slave节点告知其master节点停止处理来自客户端的请求
2. master 节点将当前replication offset 回复给该slave节点
3. 该slave节点在未应用至replication offset之前不做任何操作,以保证master传来的数据均被处理。
4. 该slave 节点进行故障转移,从群集中大多数的master节点获取epoch,然后广播自己的最新配置
5. 原master节点收到配置更新:解除客户端的访问阻塞,回复重定向信息,以便客户端可以和新master通信。
【 cluste failover force】
slave节点不和master协商(master也许已不可达),从上如4步开始进行故障切换。当master已不可用,而我们想要做人工故障转移时,该选项很有用。但是,即使使用FORCE选项,我们依然需要群集中大多数master节点有效,以便对这次切换进行验证,同时为将成为新master的salve节点生成新的配置epoch。
特点:忽略主备同步的状态,设置mf_can_start = 1,标记failover开始。
【 cluste failover takeover】
TAKEOVER选项 实现了FORCE的所有功能,同时为了能够进行故障切换放弃群集验证。当slave节点收到命令CLUSTER FAILOVER TAKEOVER会做如下操作:
1. 独自生成新的configEpoch,若本地配置epoch非最大的,则取当前有效epoch值中的最大值并自增作为新的配置epoch。
2. 将原master节点管理的所有哈希槽分配给自己,同时尽快分发最新的配置给所有当前可达节点,以及后续恢复的故障节点,期望最终配置分发至所有节点。
特点:忽略主备同步,忽略集群其他master的投票。
参考文献:cluster-failover 命令 -- Redis中国用户组(CRUG)
2、方案规划
2.1 redis-cluster模式
redis-cluster模式架构图如下:
架构说明:
1、redis-cluster采用了同城双活架构,AZ1和AZ2分别为可用区1、可用区2,主节点(AZ1)按3主3从部署,备节点(AZ2)作为Cluster的6从,整个集群为3主9从;
2、单个master节点发生故障,redis集群自动感知并进行选主,完成主从切换,不影响业务正常使用;
3、应用服务连接所有Redis集群主从节点,以便自动感知主从切换情况。其实只要连接到redis集群一个节点,应用服务便可以获取到集群信息,从而获取主从切换情况,建议还是把所有节点都配置上,防止某些节点宕机后读取不了节点信息而报错。
注意:
1、单AZ1池资源出现问题,可能导致redis集群进入fail状态,可以使用已准备好的脚本进行快速切换AZ2节点,完成集群恢复。可以考虑把master节点打散到两个AZ,避免集群超半数master节点宕机;
2.2 redis-proxy模式
redis-proxy模式采用官方的redis-proxy+cluster模式,优点就是应用只需要连接proxy节点即可,不需要配置更多的node节点,生产环境proxy需要考虑高可用,而proxy也可以考虑用lvs+keepalived作为代理取代,架构图如下所示:
3、安装部署
3.1 实验准备
配置 | IP | 系统 | redis版本 |
2C8G | 172.16.0.8 | centos7.6 | redis6.2.7 |
3.2 编译redis
下载对应版本的redis(下载地址:Index of /releases/)
wget http://download.redis.io/releases/redis-6.2.7.tar.gz
编译安装redis
tar xf redis-6.2.7.tar.gz && cd redis-6.2.7/
make && make install
3.3 配置redis
在/data/redis/目录下创建文件7001
mkdir -p /data/redis && cd /data/redis
mkdir -p {7001..7006}
在7001文件夹下创建相当于文件夹
cd /data/redis/7001
mkdir -p bin conf data logs redis-cluster
cd bin
cp -r /usr/local/bin/redis-* ./
编写启动redis脚本redis.sh
#!/bin/bash
#
# Simple Redis init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.
REDIS_HOME="/data/redis/7001"
EXEC="$REDIS_HOME/bin/redis-server"
CLIEXEC="$REDIS_HOME/bin/redis-cli"
CONF="$REDIS_HOME/conf/redis.conf"
REDISPORT=$(awk '/^port/{print $2}' $CONF)
PIDFILE=$(awk '/^pidfile/{print $2}' $CONF)
start() {
if [ -f $PIDFILE ]
then
echo "$PIDFILE exists, process is already running or crashed"
else
echo "Starting Redis server..."
$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 to shutdown ..."
sleep 1
done
echo "Redis stopped"
fi
}
restart() {
stop
sleep 1
start
}
case "$1" in
start)
start;;
stop)
stop;;
restart)
restart;;
esac
chmod +x redis.sh
编制redis.conf配置文件
protected-mode no
port 7001
maxmemory 1gb
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
supervised no
pidfile "/data/redis/7001/redis_7001.pid"
loglevel notice
logfile "/data/redis/7001/logs/redis.log"
databases 16
always-show-logo yes
save ""
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename "dump.rdb"
dir "/data/redis/7001/data"
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 10
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
lua-time-limit 5000
cluster-enabled yes
cluster-config-file "/data/redis/7001/redis-cluster/nodes-7001.conf"
cluster-node-timeout 5000
slowlog-log-slower-than 10000
slowlog-max-len 10000
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
把7001文件夹复制到其他文件夹,并将7001端口改成其他的
cd /data/redis
rm -rf {7002..7006}
cp -r 7001 7002
cp -r 7001 7003
cp -r 7001 7004
cp -r 7001 7005
cp -r 7001 7006
修改bin/redis.sh配置文件夹位置
sed -i 's/7001/7002/g' /data/redis/7002/bin/redis.sh
sed -i 's/7001/7003/g' /data/redis/7003/bin/redis.sh
sed -i 's/7001/7004/g' /data/redis/7004/bin/redis.sh
sed -i 's/7001/7005/g' /data/redis/7005/bin/redis.sh
sed -i 's/7001/7006/g' /data/redis/7006/bin/redis.sh
修改conf/redis.conf配置端口
sed -i 's/7001/7002/g' /data/redis/7002/conf/redis.conf
sed -i 's/7001/7003/g' /data/redis/7003/conf/redis.conf
sed -i 's/7001/7004/g' /data/redis/7004/conf/redis.conf
sed -i 's/7001/7005/g' /data/redis/7005/conf/redis.conf
sed -i 's/7001/7006/g' /data/redis/7006/conf/redis.conf
3.4 启动redis
启动redis命令
/data/redis/7001/bin/redis.sh start
/data/redis/7002/bin/redis.sh start
/data/redis/7003/bin/redis.sh start
/data/redis/7004/bin/redis.sh start
/data/redis/7005/bin/redis.sh start
/data/redis/7006/bin/redis.sh start
检查redis是否已经启动
ps aux | grep redis
3.5 搭建集群
搭建redis-cluster集群,中间有提示,输入“yes”
/data/redis/7001/bin/redis-cli --cluster create 172.16.0.8:7001 172.16.0.8:7002 172.16.0.8:7003 172.16.0.8:7004 172.16.0.8:7005 172.16.0.8:7006 --cluster-replicas 1
3.6 安装proxy
centos7默认是4.8,redis-cluster-proxy需要4.9以上版本GCC
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
scl enable devtoolset-9 bash
下载安装redis cluster proxy(https://github.com/RedisLabs/redis-cluster-proxy/tree/1.0)
yum -y install git
git clone https://github.com/RedisLabs/redis-cluster-proxy.git
cd redis-cluster-proxy
make && make install
配置redis-cluster-proxy
mkdir -p /data/redis/proxy/{conf,bin,logs}
cp -r src/redis-cluster-proxy /data/redis/proxy/bin
编写redis-cluster-proxy配置文件/data/redis/proxy/conf/proxy.conf
cluster 172.16.0.14:7001
cluster 172.16.0.14:7002
cluster 172.16.0.14:7003
cluster 172.16.0.14:7004
cluster 172.16.0.14:7005
cluster 172.16.0.14:7006
port 7000
bind 0.0.0.0
threads 8
logfile "/data/redis/proxy/logs/redis_cluster_proxy.log"
enable-cross-slot yes
启动redis-cluster-proxy
nohup /data/redis/proxy/bin/redis-cluster-proxy -c /data/redis/proxy/conf/proxy.conf > /data/redis/proxy/logs/proxy.log 2>&1 &
4、灾备方案
4.1 机房出现故障
最开始cluster搭建方式如下图所示
在AZ1机房全部宕机后,我们需要通过执行cluster failover takeover命令将AZ2机房切换为Master,如下图所示:
在AZ1机房恢复正常之后,如下图所示:
4.2 单个master宕机
在某个Master宕机后,集群会自动主从切换,需要检测哪个Slave成为了Master
如果是AZ2的Salve成为Master,使用命令cluster failover在AZ1的Slave上执行,让其成为Master;
如果是AZ1的Slave成为了Master,就不需要进行操作;
4.3 切换脚本切换
redis_check.sh脚本为检测redis节点是否存活
#!/bin/bash
LOGDIR="/root"
BINDIR="/data/redis/7001/bin/redis-cli"
PASSWD="123456"
IPDIR="172.16.0.8:7001 172.16.0.8:7002 172.16.0.8:7003 172.16.0.8:7004 172.16.0.8:7005 172.16.0.8:7006"
DATE=`date`
LOGFILE=${LOGDIR}/logs
ERROR_LOG=${LOGDIR}/error.logs
cd ${LOGDIR}
if [ ! -d bak ] ; then
mkdir -p bak
fi
for i in $IPDIR
do
port=${i#*:}
ip=${i%:*}
ALIVE=`$BINDIR -h $ip -p $port PING`
#ALIVE=`$BINDIR -h $ip -p $port -a $PASSWD PING`
if [ "$ALIVE" == "PONG" ]; then
echo "${DATE} Success: redis-cli -h $ip -p $port PING $ALIVE" >> $LOGFILE 2>&1
else
echo "${DATE} Failed:redis-cli -h $ip -p $port PING $ALIVE " >> $ERROR_LOG 2>&1
fi
done
redis_task.sh为AZ1节点宕机后,执行脚本切换到AZ2节点
#!/bin/bash
IPMASTER="172.16.0.8:7001 172.16.0.8:7002 172.16.0.8:7003"
BINDIR="/data/redis/7001/bin/redis-cli"
PASSWD="123456"
DATE=`date`
LOGDIR="/root"
LOGFILE=${LOGDIR}/logs
ERROR_LOG=${LOGDIR}/error.logs
for i in $IPMASTER
do
port=${i#*:}
ip=${i%:*}
status=`$BINDIR -h $ip -p $port -c cluster failover takeover`
#status=` $BINDIR -h $ip -p $port -a $PASSWD -c cluster failover takeover`
if [ "$status" == "OK" ]; then
echo "${DATE} Success: $i 成功切换成master节点" >> $LOGFILE 2>&1
else
echo "${DATE} Failed: $i 切换master节点失败 " >> $ERROR_LOG 2>&1
fi
done
4.4 手工切换
4.4.1 主从切换
进入需要切换的从节点
127.0.0.1:7005> CLUSTER FAILOVER
OK
如果不小心登录到了主节点,也没有关系,节点会提示如下错误
127.0.0.1:7001> CLUSTER FAILOVER
(error) ERR You should send CLUSTER FAILOVER to a replica
如果不想进去集群,可以输入以下命令
redis-cli -p 7001 -c CLUSTER FAILOVER
4.4.2 灾备切换
redis-cli -p 7004 -c cluster failover takeover
更多推荐
所有评论(0)