1概念
主从复制:指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master/Leader),后者称为从节点(Slave/Follower), 数据的复制是单向的!只能由主节点复制到从节点(主节点以写为主、从节点以读为主)。Redis的主从复制是异步复制,异步分为两个方面,一个是master服务器在将数据同步到slave时是异步的,因此master服务器在这里仍然可以接收其他请求,一个是slave在接收同步数据也是异步的。
2主从复制的作用
保存Redis数据副本:
当我们只是通过RDB或AOF把Redis的内存数据持久化毕竟只是在本地,并不能保证绝对的安全,而通过将数据同步slave服务器上,可以保留多一个数据备份,更好地保证数据的安全。
读写分离:
在配置了主从复制之后,如果master服务器的读写压力太大,可以进行读写分离,客户端向master服务器写入数据,在读数据时,则访问slave服务器,从而减轻master服务器的访问压力。
高可用性与故障转移:
服务器的高可用性是指服务器能提供7*24小时不间断的服务,Redis可以通过Sentinel系统管理多个Redis服务器,当master服务器发生故障时,Sentineal系统会根据一定的规则将某台slave服务器升级为master服务器,继续提供服务,实现故障转移,保证Redis服务不间断
redis的主从同步过程:
我们redis有一个主服务器,有一个从服务器,如何把主服务器的数据传输到从服务器?主服务器h会往从服务器发送3类数据:
1.快照数据,主服务器与从服务器同步之前所传输的数据
2.缓存区储存的数据:在同步的过程中会建立一个缓存区,缓存区存储的数据就是主服务每一次接受到的写命令,如果有命令过来就会把这些命令写到这个缓存区当中。
3.第三个数据就是同步完成之后的数据 就是完成同步数据之后 主服务器写入的数据一条一条发送到从服务器上,从服务器也相应的执行这些数据的操作过程。
第一步:从服务器向主服务器 发送一个同步的sync命令
第二步:主服务器收到之后,他会做俩个步骤:
1.开辟缓存区,记录Bgsave(Redis Bgsave 命令用于在后台异步保存当前数据库的数据到磁盘)后的命令
2.就是在这个期间,有数据的修改写入的命令过来,他就会写入到这个缓存区当中。
第三步:执行bgsave命令,生成快照,主服务器把现在储存的数据生成一个快照,
(所谓的快照就是把数据都备份在一个文件中,如果重启的话就将数据加载到redis中即可)
第四步:把这个快照文件发送到从服务器当中,
第五步:从服务器就是把旧的数据文件丢掉,
第六步:换成现在新的快照文件。
到这一步位置为止,从服务器就完成了载入快照文件。
前面六步都是在同步之前做的操作
第七步:载入快照文件完成之后主服务器就会把缓存区的写命令发送到从服务器,从服务器已经接受到主服务器发送的快照文件,他就会执行主服务器发送过来的写命令。
第八步:就是主服务器每执行一遍写命令,就会向从服务器发送相同的写命令
第九步:从服务器执行发过来的写命令
这就完成了一个完整的主从同步过程。
接下来就开始一个实际操作:
环境:linux + docker
启动docker
下载redis镜像
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]# docker pull redis
然后是准备配置1个主服务器,2个从服务器,他们分别的端口是:
主:6396
从1:6397
从2:6398
提前配置好redis的配置文件,放到自己可以找到的目录:(这边是先建立了3个各个端口的文件夹)
[root@iZ2ze4a0aldb5pxrf1am7oZ docker]# ls
redis6396 redis6397 redis6398
[root@iZ2ze4a0aldb5pxrf1am7oZ docker]# pwd
/mnt/docker
[root@iZ2ze4a0aldb5pxrf1am7oZ docker]#
文件夹的内容是:(这里以6397为实例,里边有data文件和conf文件,data储存的是他的数据,conf是redis的配置文件)
[root@iZ2ze4a0aldb5pxrf1am7oZ docker]# ls
redis6396 redis6397 redis6398
[root@iZ2ze4a0aldb5pxrf1am7oZ docker]# cd redis6396
[root@iZ2ze4a0aldb5pxrf1am7oZ redis6396]# ls
conf data
[root@iZ2ze4a0aldb5pxrf1am7oZ redis6396]# pwd
/mnt/docker/redis6396
[root@iZ2ze4a0aldb5pxrf1am7oZ redis6396]#
我们切换到配置文件目录,然后自己手动新建一个文件夹redis.conf
[root@iZ2ze4a0aldb5pxrf1am7oZ redis6396]# cd conf
[root@iZ2ze4a0aldb5pxrf1am7oZ conf]# ls
redis.conf
[root@iZ2ze4a0aldb5pxrf1am7oZ conf]# pwd
/mnt/docker/redis6396/conf
[root@iZ2ze4a0aldb5pxrf1am7oZ conf]#
里边书写的内容是:
protected-mode no
##主要是这个端口得与你一致
port 6396
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize no
supervised no
##这边的文件夹名字也是相同
pidfile /var/run/redis_6396.pid
loglevel notice
logfile ""
databases 30
always-show-logo yes
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir ./
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
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 yes
appendfilename "appendonly.aof"
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-max-len 128
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 4096
stream-node-max-entries 100
activerehashing yes
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
然后复制3份,修改里边的内容,改成不同的端口,放到刚刚新建的目录。
接着我们就应该去创建3个redis容器
docker run -p 6396:6396 --name redis6396 \
--sysctl net.core.somaxconn=1024 \
-v /mnt/docker/redis6396/data:/data \
-v /mnt/docker/redis6396/conf/:/usr/local/etc/redis/redis.conf \
-e TIME_ZONE="Asia/Shanghai" \
-e TZ="Asia/Shanghai" \
-d redis redis-server /usr/local/etc/redis/redis.conf --appendonly yes
docker run -p 6397:6397 --name redis6397 \
--sysctl net.core.somaxconn=1024 \
-v /mnt/docker/redis6397/data:/data \
-v /mnt/docker/redis6397/conf/:/usr/local/etc/redis/redis.conf \
-e TIME_ZONE="Asia/Shanghai" \
-e TZ="Asia/Shanghai" \
-d redis redis-server /usr/local/etc/redis/redis.conf --appendonly yes
docker run -p 6398:6398 --name redis6396 \
--sysctl net.core.somaxconn=1024 \
-v /mnt/docker/redis6398/data:/data \
-v /mnt/docker/redis6398/conf/:/usr/local/etc/redis/redis.conf \
-e TIME_ZONE="Asia/Shanghai" \
-e TZ="Asia/Shanghai" \
-d redis redis-server /usr/local/etc/redis/redis.conf --appendonly yes
--docker run (启动并创建容器容器)
-- -p 指定映射的端口 左边的是本地,右边是docker内
-- --name 指定容器的名字
-- --sysctl 指定给他分配的大小
-- 第一个-v 是指定他的数据文件指定挂载到我们的哪个目录下(就是我们上边自己手动建立的文件夹)
-- 第二个 -v 是指定他的配置文件挂载到我们本地的哪个文件夹。
-- 后边俩个-e 是指定他的时区
-- -d 是 指定他在后台运行
-- redis-sever 指定启动哪个目录下的redis文件
-- --appendonly yes 开启持久化
然后启动3个redis容器
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c825c33b7214 redis "docker-entrypoint.s…" 11 hours ago Up 11 hours 6379/tcp, 0.0.0.0:6396->6396/tcp, :::6396->6396/tcp redis6396
066d9a712ac9 portainer/portainer-ce "/portainer" 34 hours ago Up 34 hours 8000/tcp, 9443/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp portainer
5748a1c58048 redis "docker-entrypoint.s…" 34 hours ago Up 12 hours 6379/tcp, 0.0.0.0:6398->6398/tcp, :::6398->6398/tcp redis6398
4078ef866d5d redis "docker-entrypoint.s…" 34 hours ago Up 12 hours 6379/tcp, 0.0.0.0:6397->6397/tcp, :::6397->6397/tcp redis6397
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]#
然后现在就有了3个容器 ,现在就去配置主从复制,然后我刚指定的是6396为主服务器
我现在以命令的方式进入其他俩个从服务器:
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c825c33b7214 redis "docker-entrypoint.s…" 11 hours ago Up 11 hours 6379/tcp, 0.0.0.0:6396->6396/tcp, :::6396->6396/tcp redis6396
066d9a712ac9 portainer/portainer-ce "/portainer" 34 hours ago Up 34 hours 8000/tcp, 9443/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp portainer
5748a1c58048 redis "docker-entrypoint.s…" 34 hours ago Up 12 hours 6379/tcp, 0.0.0.0:6398->6398/tcp, :::6398->6398/tcp redis6398
4078ef866d5d redis "docker-entrypoint.s…" 34 hours ago Up 12 hours 6379/tcp, 0.0.0.0:6397->6397/tcp, :::6397->6397/tcp redis6397
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]# docker exec -it 5748a1c58048 bash
root@5748a1c58048:/data#
-it 后跟的是 容器id 接下来我们进入后 使用redis进入redis命令行模式指定他的父容器:
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]# docker exec -it 5748a1c58048 bash
root@5748a1c58048:/data# redis-cli -h 47.94.223.116 -p 6398
47.94.223.116:6398> slaveof 47.94.223.116 6396
OK
47.94.223.116:6398>
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]# docker exec -it 4078ef866d5d bash
root@4078ef866d5d:/data# redis-cli -h 47.94.223.116 -p 6397
47.94.223.116:6397> slaveof 47.94.223.116 6396
OK
47.94.223.116:6397>
这时候我指定了俩个从服务器的主服务器为端口为6396的主服务器,接下来我们进入到redis的主服务器查看是否配置成功
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e4c28bac8d21 redis "docker-entrypoint.s…" About a minute ago Up About a minute 6379/tcp, 0.0.0.0:6396->6396/tcp, :::6396->6396/tcp redis6396
066d9a712ac9 portainer/portainer-ce "/portainer" 34 hours ago Up 34 hours 8000/tcp, 9443/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp portainer
5748a1c58048 redis "docker-entrypoint.s…" 34 hours ago Up 12 hours 6379/tcp, 0.0.0.0:6398->6398/tcp, :::6398->6398/tcp redis6398
4078ef866d5d redis "docker-entrypoint.s…" 35 hours ago Up 12 hours 6379/tcp, 0.0.0.0:6397->6397/tcp, :::6397->6397/tcp redis6397
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]# docker exec -it e4c28bac8d21 bash
root@e4c28bac8d21:/data# redis-cli -h 47.94.223.116 -p 6396
47.94.223.116:6396> replication
(error) ERR unknown command 'replication', with args beginning with:
47.94.223.116:6396> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=47.94.223.116,port=6398,state=online,offset=224,lag=1
slave1:ip=47.94.223.116,port=6397,state=online,offset=224,lag=0
master_failover_state:no-failover
master_replid:d69b81b32087d6152c6e68578342aff0673cf90b
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:224
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:224
47.94.223.116:6396>
这边可以看到他的role为 master 拥有俩个从服务器,这就说明我们的主从复制就已经配置好了!!
接下来就要配置哨兵,先简短的介绍一下,说说为什么要配置它。
一、哨兵的主要功能
哨兵是redis集群架构中非常重要的一个组件,主要功能如下:
(1)集群监控:负责监控redis master和slave进程是否正常工作。
(2)消息通知:如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
(3)故障转移:如果master node挂掉了,会自动转移到slave node上。
(4)配置中心:如果故障转移发生了,通知client客户端新的master地址。
二、哨兵的核心知识
(1)哨兵至少需要3个实例来保证自己的健壮性。
(2)哨兵 + redis主从的部署架构,是不会保证数据零丢失的,只能保证redis集群的高可用性。
(3)哨兵本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。
(4)故障转移时,判断一个master node是宕机了,需要大部分的哨兵都同意才行。
下面我们说说第一点,为什么至少需要3个实例来保证自己的健壮性。假设哨兵集群仅仅部署了2个实例,然后quorum=1(至少有1个哨兵认为master宕机),majority=2(用于表示集群中有几个哨兵是运行的,通常是集群数的一半+1)。
现在当Master进行挂掉后,sentinal-1和sentinal-2只要有1个哨兵认为master宕机就可以进行切换,同时sentinal-1和sentinal-2两个哨兵都是正常运行的,就可以允许执行故障转移,此时会选举出一个哨兵来执行故障转移。
但是如果节点1宕机了,那么只有哨兵sentinal-2正常运行,即使只要它认为master宕机了,但是由于没有达到majority的条件,所以不会进行故障转移。如果是3个节点,那会是怎样的呢?
quorum=2,majority=2 。此时即使节点1宕机了,那么三个哨兵还剩下2个,sentinal-2和sentinal-3可以一致认为master宕机,然后选举出一个来执行故障转移。同时3个哨兵还剩下的2个哨兵运行着,等于majority,就可以允许执行故障转移。
接下来就开始配置哨兵
首先的话与上边相同需要提前把哨兵的一个配置文件提前在本地写好,放到自己可以找到的目录。
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]# cd /usr/local/redis
[root@iZ2ze4a0aldb5pxrf1am7oZ redis]# pwd
/usr/local/redis
[root@iZ2ze4a0aldb5pxrf1am7oZ redis]# ls
sentinel79.conf sentinel80.conf sentinel81.conf
[root@iZ2ze4a0aldb5pxrf1am7oZ redis]#
我在这边创建了3个哨兵文件他们的端口分别是26379 26380 26381
建立好之后往对应的配置文件里边写入对应配置参数:
port 26379
sentinel monitor sentinel79 47.94.223.116 6396 1
port 26380
sentinel monitor sentinel79 47.94.223.116 6396 1
port 26381
sentinel monitor sentinel79 47.94.223.116 6396 1
-- port 是指定本哨兵的端口
-- sentinel monitor 名称 主服务器ip 主服务器的端口 代表有多少个哨兵认为他宕机才会去选举
配置好之后直接使用docker建立3个哨兵容器
docker run -itd \
-p 26379:6379 \
--name=sentinel79 \
--sysctl net.core.somaxconn=1024 \
-v /usr/local/redis/sentinel79.conf:/etc/sentinel79.conf \
-v /root/usr/local/redis/data:/data \
-e TIME_ZONE="Asia/Shanghai" -e TZ="Asia/Shanghai" \
-d redis
docker run -itd \
-p 26380:6379 \
--name=sentinel80 \
--sysctl net.core.somaxconn=1024 \
-v /usr/local/redis/sentinel80.conf:/etc/sentinel80.conf \
-v /root/usr/local/redis/data:/data \
-e TIME_ZONE="Asia/Shanghai" -e TZ="Asia/Shanghai" \
-d redis
docker run -itd \
-p 26381:6379 \
--name=sentinel81 \
--sysctl net.core.somaxconn=1024 \
-v /usr/local/redis/sentinel81.conf:/etc/sentinel81.conf \
-v /root/usr/local/redis/data:/data \
-e TIME_ZONE="Asia/Shanghai" -e TZ="Asia/Shanghai" \
-d redis
--docker run (启动并创建容器容器)
-- -p 指定映射的端口 左边的是本地,右边是docker内
-- --name 指定容器的名字
-- --sysctl 指定给他分配的大小
-- 第一个 -v 是指定他的配置文件挂载到我们本地的哪个文件夹。
-- 第二个-v 是指定他的数据文件指定挂载到我们的哪个目录下(就是我们上边自己手动建立的文件夹)
-- 后边俩个-e 是指定他的时区
-- -d 是 指定他在后台运行
这时候就会看到有3个哨兵容器启动成功
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e4c28bac8d21 redis "docker-entrypoint.s…" 47 minutes ago Up 47 minutes 6379/tcp, 0.0.0.0:6396->6396/tcp, :::6396->6396/tcp redis6396
066d9a712ac9 portainer/portainer-ce "/portainer" 35 hours ago Up 35 hours 8000/tcp, 9443/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp portainer
3121f99b52ca redis "docker-entrypoint.s…" 35 hours ago Up 18 seconds 0.0.0.0:26379->6379/tcp, :::26379->6379/tcp sentinel79
4aa0f844231c redis "docker-entrypoint.s…" 35 hours ago Up 17 seconds 0.0.0.0:26380->6379/tcp, :::26380->6379/tcp sentinel80
7cc52be28b2d redis "docker-entrypoint.s…" 35 hours ago Up 15 seconds 0.0.0.0:26381->6379/tcp, :::26381->6379/tcp sentinel81
5748a1c58048 redis "docker-entrypoint.s…" 35 hours ago Up 13 hours 6379/tcp, 0.0.0.0:6398->6398/tcp, :::6398->6398/tcp redis6398
4078ef866d5d redis "docker-entrypoint.s…" 35 hours ago Up 13 hours 6379/tcp, 0.0.0.0:6397->6397/tcp, :::6397->6397/tcp redis6397
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]#
这下配置都好了 直接可以开始测试,我们需要进入到容器 中把三个哨兵文件分别启动起来,然后去把主服务器手动停掉。去看从服务器替换成主服务器。
这里以端口为26379的哨兵启动为示例 其他俩个也需要都启动起来
root@iZ2ze4a0aldb5pxrf1am7oZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e4c28bac8d21 redis "docker-entrypoint.s…" 50 minutes ago Up 50 minutes 6379/tcp, 0.0.0.0:6396->6396/tcp, :::6396->6396/tcp redis6396
066d9a712ac9 portainer/portainer-ce "/portainer" 35 hours ago Up 35 hours 8000/tcp, 9443/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp portainer
3121f99b52ca redis "docker-entrypoint.s…" 35 hours ago Up 4 minutes 0.0.0.0:26379->6379/tcp, :::26379->6379/tcp sentinel79
4aa0f844231c redis "docker-entrypoint.s…" 35 hours ago Up 4 minutes 0.0.0.0:26380->6379/tcp, :::26380->6379/tcp sentinel80
7cc52be28b2d redis "docker-entrypoint.s…" 35 hours ago Up 4 minutes 0.0.0.0:26381->6379/tcp, :::26381->6379/tcp sentinel81
5748a1c58048 redis "docker-entrypoint.s…" 35 hours ago Up 13 hours 6379/tcp, 0.0.0.0:6398->6398/tcp, :::6398->6398/tcp redis6398
4078ef866d5d redis "docker-entrypoint.s…" 35 hours ago Up 13 hours 6379/tcp, 0.0.0.0:6397->6397/tcp, :::6397->6397/tcp redis6397
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]# docker exec -it 3121f99b52ca bash
root@3121f99b52ca:/data# redis-sentinel /etc/sentinel79.conf
23:X 19 Sep 2022 22:11:54.717 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
23:X 19 Sep 2022 22:11:54.717 # Redis version=7.0.4, bits=64, commit=00000000, modified=0, pid=23, just started
23:X 19 Sep 2022 22:11:54.717 # Configuration loaded
23:X 19 Sep 2022 22:11:54.717 * monotonic clock: POSIX clock_gettime
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 7.0.4 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 23
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | https://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
23:X 19 Sep 2022 22:11:54.722 # Could not rename tmp config file (Device or resource busy)
23:X 19 Sep 2022 22:11:54.722 # WARNING: Sentinel was not able to save the new configuration on disk!!!: Device or resource busy
23:X 19 Sep 2022 22:11:54.722 # Sentinel ID is c6bd844cb4a3c1b60140d04e710720e8aa160458
23:X 19 Sep 2022 22:11:54.722 # +monitor master sentinel79 47.94.223.116 6396 quorum 1
23:X 19 Sep 2022 22:11:54.727 * +slave slave 47.94.223.116:6398 47.94.223.116 6398 @ sentinel79 47.94.223.116 6396
23:X 19 Sep 2022 22:11:54.729 # Could not rename tmp config file (Device or resource busy)
23:X 19 Sep 2022 22:11:54.730 # WARNING: Sentinel was not able to save the new configuration on disk!!!: Device or resource busy
23:X 19 Sep 2022 22:11:54.730 * +slave slave 47.94.223.116:6397 47.94.223.116 6397 @ sentinel79 47.94.223.116 6396
23:X 19 Sep 2022 22:11:54.732 # Could not rename tmp config file (Device or resource busy)
23:X 19 Sep 2022 22:11:54.732 # WARNING: Sentinel was not able to save the new configuration on disk!!!: Device or resource busy
现在手动去宕掉端口为6396的主服务器
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e4c28bac8d21 redis "docker-entrypoint.s…" 55 minutes ago Up 55 minutes 6379/tcp, 0.0.0.0:6396->6396/tcp, :::6396->6396/tcp redis6396
066d9a712ac9 portainer/portainer-ce "/portainer" 35 hours ago Up 35 hours 8000/tcp, 9443/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp portainer
3121f99b52ca redis "docker-entrypoint.s…" 35 hours ago Up 8 minutes 0.0.0.0:26379->6379/tcp, :::26379->6379/tcp sentinel79
4aa0f844231c redis "docker-entrypoint.s…" 35 hours ago Up 8 minutes 0.0.0.0:26380->6379/tcp, :::26380->6379/tcp sentinel80
7cc52be28b2d redis "docker-entrypoint.s…" 35 hours ago Up 8 minutes 0.0.0.0:26381->6379/tcp, :::26381->6379/tcp sentinel81
5748a1c58048 redis "docker-entrypoint.s…" 35 hours ago Up 13 hours 6379/tcp, 0.0.0.0:6398->6398/tcp, :::6398->6398/tcp redis6398
4078ef866d5d redis "docker-entrypoint.s…" 35 hours ago Up 13 hours 6379/tcp, 0.0.0.0:6397->6397/tcp, :::6397->6397/tcp redis6397
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]# docker stop e4c28bac8d21
e4c28bac8d21
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]#
现在去看哨兵的日志:
root@4aa0f844231c:/data# redis-sentinel /etc/sentinel80.conf
24:X 19 Sep 2022 22:14:58.318 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
24:X 19 Sep 2022 22:14:58.318 # Redis version=7.0.4, bits=64, commit=00000000, modified=0, pid=24, just started
24:X 19 Sep 2022 22:14:58.318 # Configuration loaded
24:X 19 Sep 2022 22:14:58.319 * monotonic clock: POSIX clock_gettime
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 7.0.4 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26380
| `-._ `._ / _.-' | PID: 24
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | https://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
24:X 19 Sep 2022 22:14:58.323 # Could not rename tmp config file (Device or resource busy)
24:X 19 Sep 2022 22:14:58.323 # WARNING: Sentinel was not able to save the new configuration on disk!!!: Device or resource busy
24:X 19 Sep 2022 22:14:58.323 # Sentinel ID is bab6cacfc50da65e92d743e4bc11d9d0241d0796
24:X 19 Sep 2022 22:14:58.323 # +monitor master sentinel79 47.94.223.116 6396 quorum 1
24:X 19 Sep 2022 22:14:58.328 * +slave slave 47.94.223.116:6398 47.94.223.116 6398 @ sentinel79 47.94.223.116 6396
24:X 19 Sep 2022 22:14:58.330 # Could not rename tmp config file (Device or resource busy)
24:X 19 Sep 2022 22:14:58.330 # WARNING: Sentinel was not able to save the new configuration on disk!!!: Device or resource busy
24:X 19 Sep 2022 22:14:58.330 * +slave slave 47.94.223.116:6397 47.94.223.116 6397 @ sentinel79 47.94.223.116 6396
24:X 19 Sep 2022 22:14:58.333 # Could not rename tmp config file (Device or resource busy)
24:X 19 Sep 2022 22:14:58.333 # WARNING: Sentinel was not able to save the new configuration on disk!!!: Device or resource busy
24:X 19 Sep 2022 22:15:00.223 * +sentinel sentinel c6bd844cb4a3c1b60140d04e710720e8aa160458 172.17.0.3 26379 @ sentinel79 47.94.223.116 6396
24:X 19 Sep 2022 22:15:00.228 # Could not rename tmp config file (Device or resource busy)
24:X 19 Sep 2022 22:15:00.228 # WARNING: Sentinel was not able to save the new configuration on disk!!!: Device or resource busy
24:X 19 Sep 2022 22:16:29.079 # +sdown master sentinel79 47.94.223.116 6396
24:X 19 Sep 2022 22:16:29.079 # +odown master sentinel79 47.94.223.116 6396 #quorum 1/1
24:X 19 Sep 2022 22:16:29.079 # +new-epoch 1
24:X 19 Sep 2022 22:16:29.079 # +try-failover master sentinel79 47.94.223.116 6396
24:X 19 Sep 2022 22:16:29.083 # Could not rename tmp config file (Device or resource busy)
24:X 19 Sep 2022 22:16:29.083 # WARNING: Sentinel was not able to save the new configuration on disk!!!: Device or resource busy
24:X 19 Sep 2022 22:16:29.083 # +vote-for-leader bab6cacfc50da65e92d743e4bc11d9d0241d0796 1
24:X 19 Sep 2022 22:16:29.092 # c6bd844cb4a3c1b60140d04e710720e8aa160458 voted for bab6cacfc50da65e92d743e4bc11d9d0241d0796 1
24:X 19 Sep 2022 22:16:29.174 # +elected-leader master sentinel79 47.94.223.116 6396
24:X 19 Sep 2022 22:16:29.174 # +failover-state-select-slave master sentinel79 47.94.223.116 6396
24:X 19 Sep 2022 22:16:29.226 # +selected-slave slave 47.94.223.116:6398 47.94.223.116 6398 @ sentinel79 47.94.223.116 6396
24:X 19 Sep 2022 22:16:29.226 * +failover-state-send-slaveof-noone slave 47.94.223.116:6398 47.94.223.116 6398 @ sentinel79 47.94.223.116 6396
24:X 19 Sep 2022 22:16:29.344 * +failover-state-wait-promotion slave 47.94.223.116:6398 47.94.223.116 6398 @ sentinel79 47.94.223.116 6396
24:X 19 Sep 2022 22:16:30.337 # Could not rename tmp config file (Device or resource busy)
24:X 19 Sep 2022 22:16:30.337 # WARNING: Sentinel was not able to save the new configuration on disk!!!: Device or resource busy
24:X 19 Sep 2022 22:16:30.337 # +promoted-slave slave 47.94.223.116:6398 47.94.223.116 6398 @ sentinel79 47.94.223.116 6396
24:X 19 Sep 2022 22:16:30.337 # +failover-state-reconf-slaves master sentinel79 47.94.223.116 6396
24:X 19 Sep 2022 22:16:30.414 * +slave-reconf-sent slave 47.94.223.116:6397 47.94.223.116 6397 @ sentinel79 47.94.223.116 6396
24:X 19 Sep 2022 22:16:31.522 * +slave-reconf-inprog slave 47.94.223.116:6397 47.94.223.116 6397 @ sentinel79 47.94.223.116 6396
24:X 19 Sep 2022 22:16:31.522 * +slave-reconf-done slave 47.94.223.116:6397 47.94.223.116 6397 @ sentinel79 47.94.223.116 6396
24:X 19 Sep 2022 22:16:31.575 # +failover-end master sentinel79 47.94.223.116 6396
24:X 19 Sep 2022 22:16:31.575 # +switch-master sentinel79 47.94.223.116 6396 47.94.223.116 6398
24:X 19 Sep 2022 22:16:31.575 * +slave slave 47.94.223.116:6397 47.94.223.116 6397 @ sentinel79 47.94.223.116 6398
24:X 19 Sep 2022 22:16:31.575 * +slave slave 47.94.223.116:6396 47.94.223.116 6396 @ sentinel79 47.94.223.116 6398
24:X 19 Sep 2022 22:16:31.579 # Could not rename tmp config file (Device or resource busy)
24:X 19 Sep 2022 22:16:31.579 # WARNING: Sentinel was not able to save the new configuration on disk!!!: Device or resource busy
24:X 19 Sep 2022 22:17:01.632 # +sdown slave 47.94.223.116:6396 47.94.223.116 6396 @ sentinel79 47.94.223.116 6398
现在他已经把端口为6398的从服务器变为主服务器
接下来去6398的redis看他是否变为主服务器
root@5748a1c58048:/data# redis-cli -h 47.94.223.116 -p 6398
47.94.223.116:6398> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=47.94.223.116,port=6397,state=online,offset=71991,lag=0
master_failover_state:no-failover
master_replid:e7d50ddaa263f25e9134fe7c3b69f149def5d5b8
master_replid2:d69b81b32087d6152c6e68578342aff0673cf90b
master_repl_offset:71991
second_repl_offset:28203
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:71991
47.94.223.116:6398>
如果这时候我去把之前的主服务器6396再次开启他就会变为6398的从服务器,就是成了从服务器
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e4c28bac8d21 redis "docker-entrypoint.s…" About an hour ago Exited (0) 6 minutes ago redis6396
066d9a712ac9 portainer/portainer-ce "/portainer" 35 hours ago Up 35 hours 8000/tcp, 9443/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp portainer
3121f99b52ca redis "docker-entrypoint.s…" 35 hours ago Up 15 minutes 0.0.0.0:26379->6379/tcp, :::26379->6379/tcp sentinel79
4aa0f844231c redis "docker-entrypoint.s…" 35 hours ago Up 15 minutes 0.0.0.0:26380->6379/tcp, :::26380->6379/tcp sentinel80
7cc52be28b2d redis "docker-entrypoint.s…" 35 hours ago Up 15 minutes 0.0.0.0:26381->6379/tcp, :::26381->6379/tcp sentinel81
5748a1c58048 redis "docker-entrypoint.s…" 35 hours ago Up 13 hours 6379/tcp, 0.0.0.0:6398->6398/tcp, :::6398->6398/tcp redis6398
4078ef866d5d redis "docker-entrypoint.s…" 36 hours ago Up 13 hours 6379/tcp, 0.0.0.0:6397->6397/tcp, :::6397->6397/tcp redis6397
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]# docker start e4c28bac8d21
e4c28bac8d21
[root@iZ2ze4a0aldb5pxrf1am7oZ ~]# docker exec -it e4c28bac8d21 bash
root@e4c28bac8d21:/data# redis-cli -h 47.94.223.116 -p 6396
47.94.223.116:6396> info replication
# Replication
role:slave
master_host:47.94.223.116
master_port:6398
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_read_repl_offset:115780
slave_repl_offset:115780
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:e7d50ddaa263f25e9134fe7c3b69f149def5d5b8
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:115780
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:111968
repl_backlog_histlen:3813
47.94.223.116:6396>
这时候就说明配置成功!!!!
注:运行命令复制下来运行 ,需要修改映射的文件夹,与自己的目录位置相同
总结:在第一次配置的时候肯定会遇到问题,首先是挂载文件 没有挂载成功的问题,你在每次新建完容器之后,去本地与docker都查看一下文件是否同步。
然后是会报一个511的报错 这个报错如果你是按我的运行命令去走就不会遇到,他这个的原因是分配的内存问题 在运行容器的时候加上这个--sysctl net.core.somaxconn=1024 就不会遇到
还有端口的问题,建议是提前在服务器中开启这些端口。
还有就是测试的时候,把3个哨兵全部打开,再去测试。
两种数据丢失的情况
(1)异步复制导致的数据丢失
因为master -> slave的复制是异步的,所以可能有部分数据还没复制到slave,master就宕机了,此时这些部分数据就丢失了。
(2)脑裂导致的数据丢失
脑裂,也就是说某个master所在机器突然脱离了正常的网络,跟其他slave机器不能连接,但是实际上master还运行着。此时哨兵可能就会认为master宕机了,然后开启选举,将其他slave切换成了master。这个时候,集群里就会有两个master,也就是所谓的脑裂。
此时虽然某个slave被切换成了master,但是可能client还没来得及切换到新的master,还继续写向旧master的数据可能也丢失了。因此旧master再次恢复的时候,会被作为一个slave挂到新的master上去,自己的数据会清空,重新从新的master复制数据。
解决异步复制和脑裂导致的数据丢失
两个相关的配置项如下,即要求至少有1个slave,数据复制和同步的延迟不能超过10秒。如果说一旦所有的slave,数据复制和同步的延迟都超过了10秒钟,那么这个时候,master就不会再接收任何请求了。
min-slaves-to-write 1
min-slaves-max-lag 10
(1)减少异步复制的数据丢失
有了min-slaves-max-lag这个配置,就可以确保说,一旦slave复制数据和ack延时太长,就认为可能master宕机后损失的数据太多了,那么就拒绝写请求,这样可以把master宕机时由于部分数据未同步到slave导致的数据丢失降低的可控范围内。
(2)减少脑裂的数据丢失
如果一个master出现了脑裂,跟其他slave丢了连接,那么上面两个配置可以确保说,如果不能继续给指定数量的slave发送数据,而且slave超过10秒没有给自己ack消息,那么就直接拒绝客户端的写请求。
如遇到解决不了的问题 可以私信或评论在下方