Redis 进阶(五)—— 哨兵

4. 哨兵机制

4.1 引入

前一篇文章中将主节点挂了的情况时,提到了哨兵机制。

主节点挂了,人工恢复的一般步骤:

  1. 先看主节点能不能抢救,好不好抢救。
  2. 如何挂的原因不好定位,或短时间难以解决。就要在从节点中选一个作为新的主节点:a. 选中的从节点通过slaveof no one 变为主节点;b. 其他从节点通过 slaveof 修改从属关系,连上新的主节点;c. 通知客户端(修改客户端配置),连接新的主节点。d. 主节点修好后,可以作为从节点,再添进去。

人工实时监测去修改,并不靠谱。因而要通过程序来自动化解决。

哨兵机制:即 通过一个 独立的进程 redis-sentinel(和 redis-server 是不同进程) 来监控 redis-server 进程。redis-sentinel 不存储数据,只监视。

4.2 工作流程

通常会存在多个 redis-sentinel 进程(单个哨兵存在单点问题,且容易误判(例如:没有挂,只是网络延迟较大)。一般为奇数个,即最少 三 个),监控现有redis master 和 slave 。

监控:这些进程间建立 tcp 长连接,定期发送心跳包

  1. 如果是从节点挂了,没什么影响。如果是主节点挂了,哨兵就有发挥作用了。 只有 一个哨兵判断主节点挂了,还不行,只有多个哨兵都判断主节点挂了才会认为主节点真挂了。(避免误判)
  2. 哨兵判定主节点真挂了,就会推举一个 leader,由 leader 负责在从节点中选出一个主节点
  3. 选出主节点后,哨兵节点,就会控制该节点,执行 slaveof no one,并控制其他节点,变为新主节点的从属
  4. 哨兵自动通知客户端程序,告知其新的主节点,后续写操作,针对新主节点进行

从上面可以得出 哨兵核心功能即 ① 监控;② 自动故障转移;③ 通知。

4.3 部署哨兵

以该图为例,一共六个节点(三个 server,三个 sentinel)。正常应部署在 6 台不同的服务器上的,但是当前只有一个(实际中,部署在同一个上,没有意义)

由于节点较大,容易冲突如 端口号/配置文件/数据文件,直接部署就要很小心,此外在不同主机上部署时,也存在较大差异。所以,这里我们使用 docker 来解决这些问题

虚拟机,是通过软件在电脑上模拟一个虚拟的电脑,但是很吃配置,对于我们 2G/4G 的云服务器来说基本没法用。

docker 可以看作是一个 "轻量级虚拟机" (和虚拟机差别还是挺大的),能像虚拟机一样进行环境隔离,但又不吃太多硬件。

4.3.1 docker & docker-compose 安装及前置操作

系统为 ubuntu24

docker安装

bash 复制代码
sudo apt install curl -y

sudo rm -f /etc/apt/keyrings/docker.gpg

sudo rm -f /etc/apt/keyrings/hashicorp.gpg

sudo rm -f /etc/apt/sources.list.d/docker.list

sudo rm -f /etc/apt/sources.list.d/hashicorp.list

sudo install -m 0755 -d /etc/apt/keyrings

sudo install -m 0755 -d /etc/apt/keyrings

curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

sudo apt update

sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin


sudo systemctl daemon-reload

sudo systemctl start docker

sudo systemctl enable docker

照着一行一行执行即可

完成之后执行 sudo systemctl status docker ,结果是这样说明没有问题

docker-compose 安装

docker-compose 是用来通过配置文件批量操作容器的(容器可以看作是 轻量级虚拟机)

bash 复制代码
apt install docker-compose

关闭之前的redis-server,使用 docker 获取 redis 镜像

bash 复制代码
docker pull redis:5.0.9

这个镜像包含了一个精简的 Linux 操作系统,且上面上面装了 redis,只要直接基于这个镜像创建一个容器,运行容器,redis服务器就搭好了。

镜像和容器,类似于可执行程序和进程的关系

4.3.2 docker 加速器配置

由于是国内的服务器,所以没法正常访问国外 Docker Hub, 没法用。这里用的腾讯云服务器,加速器就用的腾讯云的。其他厂商的可在网上看看。

创建及修改文件

bash 复制代码
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://mirror.ccs.tencentyun.com"]
}
EOF

重启服务

bash 复制代码
sudo systemctl daemon-reload
sudo systemctl restart docker

通过 docker info 命令查看是否成功配置

这就是配置成功了。

4.3.3 容器编排

使用 docker-compose 进行容器编排,通过一个配置文件,将要创建容器,容器参数,描述清楚。后续通过一个命令,就能批量启动/停止这些容器

这里的配置文件采用 yml 的格式,虽然 6 个容器可以放在一个 yml 文件中,但是 6 个容器同时启动,哪个容器先启动完成是不确定的。

所以这里节点和哨兵各一个配置文件

4.3.3.1 编排节点

先创建配置文件,同时进入文件所在目录

bash 复制代码
mkdir /root/redis/docker-compose.yml
cd /root/redis
touch docker-compose.yml

修改配置文件内容如下:

bash 复制代码
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

注意:如果粘乱了,不能用 tab 键,只能用空格,用 tab 会解析失败,后续无法启动。

  • version:docker compose 文件的语法版本

  • services:有哪些服务

  • master,slave1,slave2: 为服务名,容器逻辑名,自己定的

  • image:使用哪个镜像启动容器,这里用之前下的那个镜像

  • contcontainer_name:容器实际名,如果不指定,则自动生成名字,如:project_master_1

  • restart:容器挂了,总是重新启动,手动关停不算

  • command :覆盖镜像默认启动的命令,上面的 --slaveof redis-master 6379 中的 redis-master 是容器名,起到类似域名的作用,因为不知道容器启动后,会分配到哪个ip,虽然能配置静态ip,但是比较麻烦,直接用 容器名,docker 会自动解析得到对应域名

  • ports:容器端口映射到主机哪个端口,前一个是主机端口后一个是容器端口。每个容器的端口自成一派,容器间,容器主机间不冲突(即 容器1 的 6379 和 容器2 的 6379 不冲突这种,映射到一个还是会冲突)。

通过

bash 复制代码
docker-compose up -d

启动容器。

bash 复制代码
docker-compose down

停止并删除刚才创建的容器

通过 docker ps -a 查看所有容器的端口映射。

4.3.3.2 编排哨兵

先创建文件夹

bash 复制代码
root@VM-4-7-ubuntu:~# mkdir redis-sentinel

root@VM-4-7-ubuntu:~# cd redis-sentinel/
root@VM-4-7-ubuntu:~/redis-sentinel# touch docker-compose.yml

配置 yml 文件

bash 复制代码
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 字段,- ./sentinel2.conf:/etc/redis/sentinel.conf 含义是把宿主机当前目录下的 sentinel2.conf 文件,映射(挂载)到容器内部的 /etc/redis/sentinel.conf 路径。

原因是 redis sentinel 必须通过配置文件启动,不能只靠命令行参数,而容器内的文件是临时的,在宿主机上创建一个配置文件,通过 volumes "共享"进容器,即使容器重启,配置文件依然存在。

格式:宿主机路径:容器路径

宿主机路径:可以是相对路径(如 ./file)或绝对路径(如 /home/user/file)

容器路径:必须是完整路径

权限问题:确保宿主机上的 sentinel3.conf 可读(一般默认即可)

热更新:修改宿主机的 sentinel3.conf 后,需要重启容器才能生效(Sentinel 不支持动态重载配置)

如果宿主上不存在对应文件,Linux 就会自动创建一个空目录(不是文件)

配置 conf 文件

bash 复制代码
bind 0.0.0.0
port 26379
sentinel monitor redis-master redis-master 6379 2
sentinel down-after-milliseconds redis-master 1000

三个哨兵一人一份, conf 文件。

sentinel monitor redis-master redis-master 6379 2

规定了监视哪个节点, 前一个 redis-master 为主节点名,后一个为 主节点ip,6379 为容器端口,2 为法定票数(即大于等于2台哨兵,认为主节点挂了,就认为主节点真挂了)

`sentinel down-after-milliseconds redis-master 1000``

主节点和哨兵间通过心跳包沟通,这里是超时时间。

三个哨兵的conf 文件内容一致,还要要创建多份配置文件,是因为 redis-sentinel 在运行时可能会 rewrite 配置,共用一份会写乱。

通过 docker-compose logs 查看日志,发现 sentinel 说找不到主节点

这是因为 server 和 sentinel 是分别启动的,而同一个 yml 文件内的容器自动划分到同一个局域网。这就使得 三台节点,三台哨兵处在不在同一局域网内,因而无法解析 redis-master,进而找不到。

通过 docker network ls 命令,可以看到他们位于不同局域网中

处理方法

在 yml 文件最下面加上如下配置,让 sentinel 加入 server 的局域网中

bash 复制代码
networks:
	default:
		external:
			name: redis-data_default

注意这里的 name 和前面 docker network ls 中的名字要一致。

接着重新启动 sentinel 即可。

同时,观察主机上的 sentinel.conf 文件,发现已经被哨兵 rewrite 了

4.4 哨兵功能实验

通过 docker stop 容器名 关闭容器

发现 6381 端口的从节点 变为了主节点

4.5 重选流程

查看 sentinel 日志

sdown 主观下线,本哨兵认为主节点下线

odown 客观下线,认为主节点挂的达到法定票数(一兵一票)

更改了主节点,和余下从属节点的所属关系。

如果 旧主节点恢复了,就作为从节点再加进去,这里不做演示。

  1. 主观下线
  2. 客观下线
  3. 哨兵推选 leader, leader 负责挑选主节点,并更该余下节点从属关系(所以一般为奇数,不然容易平票)
  4. leader 推举出后,选主节点先看 1)优先级,每个redis数据节点,配置文件中都有一个优先级设置 slave-priority,优先级高的,作主节点;优先级相同则看 2)offset ,offset 大的作主;还相同则看 3)runid,runid 是启动时随机的,所以就是随便选一个了。

leader 选举,每个哨兵一票,可投自己,当所得票数超哨兵个数一半,就选举完成了(为什么为奇数个哨兵),一般都会先投自己,其他节点如果感知到了前面有人投他自己,就会投那个哨兵。大致就是看谁最早投票。

  • 哨兵节点,不能只一个,不然哨兵挂了会影响系统可用性
  • 哨兵最后奇数个,方便选 leader
  • 哨兵 + 主从复制 解决的是高可用性问题,不能解决 "极端情况下写丢失" 问题
  • 哨兵 + 主从复制 不能提高数据的存储容量,当要存的数据接近或超过物理内存,这样的结构就难以胜任了。
相关推荐
工具罗某人5 小时前
docker快速部署redis
redis·docker·容器
C_心欲无痕5 小时前
网络相关 - 强缓存与协商缓存讲解
前端·网络·网络协议·缓存
a587695 小时前
Oracle数据库体系结构深度解析:从内核到应用的理论全景
数据库·oracle
三两肉5 小时前
HTTP 缓存详解
网络协议·http·缓存
高溪流5 小时前
1.MySql概念讲解 及 MySql安装教程
数据库·mysql
语落心生5 小时前
深入doris查询计划以及io调度(四)存储引擎架构
数据库
蓝银草同学5 小时前
MySQL SQL语法详解:带注释的实用示例
前端·数据库
语落心生6 小时前
深入doris查询计划以及io调度(三)查询执行协调器QE
数据库
Hui Baby6 小时前
saga json文件阅读
java·前端·数据库
此生只爱蛋6 小时前
【Redis】RESP协议和库的安装
数据库·redis·缓存