目录
1.3、使用 Docker 和 DockerCompose 模拟部署哨兵模式
a)首先要安装好 docker 和 docker-compose.
f)使用 docker-compose 启动 redis-sentinel 结点
重点:为什么引入哨兵模式?什么是哨兵模式?哨兵模式的工作原理?哨兵模式的缺陷?
实际的开发中,对于服务器后端开发,监控程序,是非常重要的!服务器一般要有比较高的可用性,7 * 24 小时运行,服务器长期运行,总会有一些 “意外” ,具体啥时候出现意外,也不能全靠人工来盯着服务器,所以在早期的主从结构中,会写一个程序来盯着服务器的运行状态.
这个程序需要干两件事:
Ps:互联网公司的程序员,尤其是大厂,公司都会明确要求,程序员手机要 24 小时开机,并且随时关注,报警不仅仅是给这一个程序员,还会给程序员的领导,还有领导的领导~ 关键时候错过领导的电话,可能就会有比较负面的评价,升职加薪就要往后了~
程序员如何恢复的?
Ps:
1.只要是涉及到人工干预,不说繁琐,至少是很烦人的~
2.另外,这个操作过程一旦出错了,就可能让问题更加严重。
3.通过人工干预的做法,就算程序员第一时间看到了报警信息,第一时间处理,也至少需要半个小时以上的恢复时间,也就是说,这半个小时里,整个 redis 就一直不能写?显然不合适。
为了提高 “可用性” 引入以下架构模式~
Redis Sentinel 是⼀个分布式架构,其中包含若干个 Sentinel 节点(哨兵)和 Redis 数据节点,这两种结点之间的哨兵机制,是通过独立的进程来体现的,和 redis-server 是不同进程!redis-sentinel 不负责存储数据,只是对其他的 redis-server 进程起到监控的效果.

a)redis sentinel 进程,会监控现有的主从结点(这里的监控就是指,sentinel 进程会和 主从结构进程 之间建立 tcp 长连接,通过这样的长连接,定期发送心跳包,看主节点是否挂了,如果 心跳包 没有如果如约而至,就说明 redis 主节点挂了).
b)如果是从节点挂了,其实没关系,如果是主节点挂了,哨兵就要发挥作用了,此时一个哨兵发现了主节点挂了,还不够,需要多个哨兵来认同这件事,主要防止出现误判(网络抖动,导致心跳包丢失).
c)如果主节点确实挂了,这些哨兵结点中,就会选举出一个 leader,由这个 leader 负责从现有的从节点中,挑选出一个新的 主节点.
d) 挑选出新的主节点之后,哨兵结点就会自动控制被选中的结点,执行 slaveof no one,让他先自立山头,然后再控制其他从节点,修改 slaveof 到新的主节点上.
e)哨兵结点会自动通知客户端程序,告知新的主节点是谁,后续,客户端再进行写操作,就会针对新的主节点进行操作了.
Ps:以上这三大点,也是 redis 哨兵的核心功能.(这里只是粗略的讲了以下 “哨兵重新选取主节点的流程” ,后面会详细讲!这是面试经常会问的问题!)
redis 哨兵结点,只有一个也是可以的.
但值得注意的是,再分布式系统中,应该避免 “单点” 问题(1.一个服务器挂了,整个就服务就瘫痪了;2.并发量有限).
在 redis 中可能会导致以下问题:
Ps:哨兵结点,最好搞奇数个,最少也要有 3 个
这里我们将以下图为例,通过 Docker 和 DockerCompose 部署和编排 3 个哨兵和 1 个主 2 个从结点.

案例说,这 6 个结点是要部署到 6 个不同的服务器主机上的.
但我这里只有一个云服务器(实际的工作中,上述结点放在一个云服务器上事没有意义的,这么做也是没办法,经济有限), 如果直接部署,就需要小心避开这些冲突:端口号、配置文件、数据文件....
使用 docker 就可以有效解决上述问题~
虚拟机,就是在电脑上通过软件模拟出另外一些硬件(构造了一个台虚拟电脑),也就是说,通过虚拟机这样的软件,就可以使用一个计算机,来模拟出多个电脑的情况.
但是虚拟机有个很大的问题:很吃配置,这个事情对于我的云服务器来说,压力山大~
docker 可以认为是一个 “轻量级” 的虚拟机(现在后端开发很流行这个组件),既不吃很多的硬件资源,又可以起到虚拟机这样的隔离环境的效果(即使你云服务器配置很低,也能构造出好几个这样的虚拟环境).
这里我之前有出过专门的文章,可以去翻翻.
通过以下命令,就可以从 docker hb 上拉取 redis 5.0.9 的镜像
docker pull redis:5.0.9

Ps:此处涉及到多个 redis server 和多个 redis 哨兵结点,每一个 redis server 和每一个 redis 哨兵结点都是作为一个单独的容器(按照案例,这里总共要部署 6 个容器),为了方便部署,使用 docker-compose 进行容器编排.

由于 redis 哨兵结点是单独的 redis 服务器进程,因此之后对于哨兵和主从结点的 yml 配置文件要分开!!!
虽然也可以使用一个 yml 文件 直接启动 6 个容器,但是如果 6 个容器同时启动,可能是 哨兵 先启动完成,数据节点后启动完成,哨兵可能就会先认为是数据结点挂了!虽然从大体上来看不影响,但是会影响到我们对执行日志的观察.
这里分别在 redis-data 目录 和 redis-sentinel 目录下创建一个 yml 配置文件(注意,文件名必须是 docker-compose.yml)

分别用来描述 3 个数据结点和 3 个哨兵结点 , 具体要创建哪些容器,每个容器运行的各种参数,描述清除。
后续通过一个简单的命令,就可以批量进行 启动/停止 这些容器了.
Ps:下来讲到的配置文件不需要记,只需要了解其中一些参数的意思即可,实际工作中不可能让你去写,只会让你去 copy 现有的,对关键参数进行修改.
数据结点配置文件如下:
- version: '3.7'
- services:
- master:
- image: 'redis:5.0.9'
- container_name: redis-master
- restart: always
- command: redis-server --appendonly yes
- ports:
- - 6379:6379
- slave1:
- image: 'redis:5.0.9'
- container_name: redis-slave1
- restart: always
- command: redis-server --appendonly yes --slaveof redis-master 6379
- ports:
- - 6380:6379
- slave2:
- image: 'redis:5.0.9'
- container_name: redis-slave2
- restart: always
- command: redis-server --appendonly yes --slaveof redis-master 6379
- ports:
- - 6381:6379
对一些关键参数的解释:
Ps:站在宿主的角度,访问上述几个端口的时候,也不知道这个端口是一个宿主机上的服务,还是来自于容器内部的服务,只要正常使用即可。这里的映射过程很像 NAT.

哨兵结点配置文件如下:
- version: '3.7'
- services:
- sentinel1:
- image: 'redis:5.0.9'
- container_name: redis-sentinel-1
- restart: always
- command: redis-sentinel /etc/redis/sentinel.conf
- volumes:
- - ./sentinel1.conf:/etc/redis/sentinel.conf
- ports:
- - 26379:26379
- sentinel2:
- image: 'redis:5.0.9'
- container_name: redis-sentinel-2
- restart: always
- command: redis-sentinel /etc/redis/sentinel.conf
- volumes:
- - ./sentinel2.conf:/etc/redis/sentinel.conf
- ports:
- - 26380:26379
- sentinel3:
- image: 'redis:5.0.9'
- container_name: redis-sentinel-3
- restart: always
- command: redis-sentinel /etc/redis/sentinel.conf
- volumes:
- - ./sentinel3.conf:/etc/redis/sentinel.conf
- ports:
- - 26381:26379
对一些关键参数的解释:
volumes:建立映射. 哨兵结点在运行的过程中,会对配置文件进行自动修改,因此就不能只拿 /etc/redis/sentinel.conf 这一个配置文件,给三个容器分别进行映射. 以上 volumes 的内容,就需要我们在当前目录下(redis-sentinel目录下)继续创建三个 conf 文件.
创建 sentinel1.conf、sentinel2.conf、sentinel2.conf 这三个配置文件,内容可以是一样的,容器启动后,会自动对这三个 conf 文件进行修改.

- bind 0.0.0.0
- port 26379
- sentinel monitor redis-master redis-master 6379 2
- sentinel down-after-milliseconds redis-master 1000
参数解释:
使用 docker-compose up -d 命令(-d 表示后天运行)启动所有容器(必须和 docker-compose.yml 同文件路径下)
Ps:如果启动后发现前面配置有误,需要重新操作,使用 docker-compose down 即可停止刚创建好的容器.
如果出现以下错误,说明 yml 中配置的 version 不支持,修改成报错信息中支持的版本即可(以下报错中提示,只支持 version 为 2.2 或者 3.3 的版本)

如下信息为启动成功

通过 redis-compose logs 命令查看运行日志信息

通过连接 redis 验证主从结构
连接主节点:redis-cli -p 6379

连接第一个从节点:redis-cli -p 6380

连接第二个从节点:redis-cli -p 6381

在 redis-sentine 目录下,使用 docker-compose up -d 启动所有容器

通过 docker-compose logs 查看日志文件

日志里为什么全都是报错信息呢?都是什么意思?(重!)
docker-compose 一下启动了 N 个容器,此时 N 个容器都是处于同一个 “局域网” 中,可以使这 N个容器之间可以互相访问~
但是!!! 三个 redis-server 结点是一个局域网,三个哨兵结点是另外一个局域网,默认情况下,这两网络是不互通的!!!
可以使用 docker-compose 把此处两组服务放到同一个局域网中.
具体的,使用 docker network ls 列出当前 docker 中的局域网,就可以拿到 redis-data 服务的 docker-compose 文件所在目录名(用来修改配置文件)

修改哨兵结点中配置文件,在之前配置文件的基础上加上如下配置:
- networks:
- default:
- external:
- name: redisdata_default
然后再启动哨兵,观察日志如下:

打开 sentinel1.conf 文件,就可以看到 ,哨兵结点启动之后,自动进行修改的内容如下:

哨兵存在的意义,就是在 redis 主从机构出现问题的时候(主节点挂了),此时哨兵结点能够自动帮我我们重新选出一个主节点,来代替之前挂了的结点,保证整个 redis 仍然是可用的状态.
这里我们可以通过 docker stop redis-master 命令手动把主节点干掉,然后观察哨兵日志,可以看到

此时就需要先选出一个 leader哨兵,由这个 leader 哨兵结点选出一个从节点,作为新的主节点.
接着往下看日志,就可以看出 3 个哨兵的投票过程.

1. 主观下线(sdown):哨兵节点通过心跳包,判定 redis 服务器是否正常工作. 如果心跳包没有如约而至,就说明 redis 服务器挂了.
Ps:此时还不能排除网络波动的影响,因此就只能单方面的认为这个 redis 结点挂了.
比如是否可能出现非常严重的网络波动,导致所有哨兵都联系不上 redis 主节点,误判成挂了呢?
当然可能是有的!如果出现这个情况,怕是用户的客户端也连接不上 redis 主节点,此时这个主节点基本上也就无法正常工作了.
“挂了” 不一定是进程崩了,只要无法正常访问,都可以视为挂了.
2. 客观下线(odown):多个哨兵都认为主节点挂了,(认为挂了的哨兵数目到达 “法定票数”),哨兵们就认为这个主节点是 客观下线.
3. 多个哨兵结点会通过投票的方式,选出一个 leader 哨兵结点,有这个 leader 负责选出一个 从节点 作为新的主节点.
具体的,每个哨兵手里只有一票(接下来的投票过程,就是看谁反应快,网络延迟小)
1 号哨兵 第一个发现当前是客观下线之后,就立即给自己投了一票(推举自己成为 leader),并且告诉 2 3 ,我来负责这个事情(2 3 当他们没有投出这一票的时候,拉票请求,就会投出去,如果有多个拉票请求,就投给最先到达的) ,如下:

2 号哨兵不乐意,认为自己和 1 号同时发现,也给自己投了一票,3 号哨兵慢了半拍,才发现是客观下线,就把这一票投给了 1 号哨兵,如下:

如果总的票数到达了哨兵总数的一半,选举就完成了.(把哨兵个数设置成奇数个结点,就是为了方便投票).
4. 此时 leader 选举完毕,leader 需要挑选出一个从节点,作为新的主节点.
具体的挑选从节点的规则如下:
a)优先级:每个 redis 数据结点,都会在配置文件中有一个优先级的设置(slave-priority)优先级高的从节点,就会胜出(如果没有设置,默认优先级都是一样的). 如果当前优先级一样,就看下一个规则.
b)offset :offset 就是从节点从主节点这边同步数据的进度,数值越大,说明从结点的数据和主节点的数据越接近,因此,最大的胜出. 如果当前优先级一样,就看下一个规则.
c)run id:每个 redis 结点启动的时候随机生成一串数字(大小全随机),这时候就是相当于是随机了.
这就像是,大学的学生如果和辅导员有亲戚关系,那么班长就是钦定的,如果没有关系,那么就看谁更听她的话,如果都是刺头,那就只能看谁名字好听了~
5. 新的主节点指定好了之后,leader 就会控制这个结点,执行 slave no one,让其独立山头,成为 master,然后再控制其他节点,执行 slave of,让其他节点以新的 master 作为主节点.
6. 最后,如果之前挂掉的主节点修复了,就直接让这个节点变为从节点加入到机器中(为什么不在成为主节点?因为成为主节点,还需要通过 slave of 修改正在运行的主节点,这个过程,可能会丢失数据).
使用 docker start redis-master 命令启动恢复之前挂掉的主节点后,会发现,已经变为从节点

哨兵 + 主从解决的问题是 “提高可用性”,不能解决极端情况下写丢失的问题~
最关键的一点就是,在数量十分庞大的情况下,这种模式就难以胜任了!
因此,redis 集群,就是解决存储容量问题的有效方案.
