[Redis#18] 哨兵机制 | docker 部署实验 | 选举机制(leader&主)

目录

基本概念

工作原理

哨兵节点的作用与配置

监控与心跳检测

⭕故障恢复流程

[Docker 部署 Redis Sentinel](#Docker 部署 Redis Sentinel)

场景介绍

[Docker 简介](#Docker 简介)

[Docker 安装](#Docker 安装)

[Docker Compose 编排 Redis 主从及哨兵节点](#Docker Compose 编排 Redis 主从及哨兵节点)

[编排 Redis 主从节点](#编排 Redis 主从节点)

[编排 Redis 哨兵节点](#编排 Redis 哨兵节点)

创建哨兵配置文件

启动服务

问题与解决

选举机制(常考)

主节点选举过程

实验

注意事项


  • 本章节相关操作不需要记忆,重点在于理解流程和原理。后续工作中如果用到了能查到即可。
  • 问题背景 :在Redis的主从复制模式下,主节点故障时需要 人工 进行主从切换,并通知客户端切换到新的主节点,这对大规模应用来说是不可接受的。
  • 因此,自Redis 2.8开始提供了Redis Sentinel(哨兵)来解决这个问题。
基本概念

由于对 Redis 的许多概念都有不同的名词解释,所以在介绍 Redis Sentinel之前,先对几个名词概念进行必要的说明

    • 主节点:独立的redis-server进程。
    • 从节点:独立的redis-server进程,作为主节点的数据备份并分担读压力。
    • 数据节点:包括主节点和从节点。
    • 哨兵节点:监控数据节点的独立redis-sentinel进程。
    • 哨兵节点集合:多个redis-sentinel进程的组合。
    • 应用方:一个或多个连接Redis的进程。

之前我们学到的 人工干预流程如下:

  • 故障发现
    • 运维人员通过监控系统检测到Redis主节点出现宕机情况。
  • 选择新的主节点
    • 运维人员从现有的从节点中挑选一个(例如slave1),并通过执行命令SLAVEOF NO ONE将其转换为新的主节点。
  • 更新其他从节点配置
    • 对剩余的从节点(如slave2),运维人员执行命令SLAVEOF <newMasterIp> <newMasterPort>,让这些从节点开始与新的主节点同步数据。
  • 更新应用连接信息
    • 更新应用程序配置,使其指向新的主节点地址{newMasterIp}:{newMasterPort},确保客户端能够继续正常访问服务。
  • 处理恢复的原主节点
    • 如果原先宕机的主节点恢复正常,运维人员应在其上执行SLAVEOF <newMasterIp> <newMasterPort>命令,使其转变为从节点,加入到新的复制拓扑结构中。

工作原理

Redis Sentinel是Redis提供的 高可用实现方案,在生产环境中对提高系统高可用性非常有帮助。

  • 当主节点出现故障时,哨兵能够自动完成故障的发现和故障的转移,并通知应用方,从而实现真正的高可用.
  • 哨兵最主要的核心功能 就是监控 ,自动故障转移,通知新的主节点到客户端.
  • Redis Sentinel是一个 分布式架构,其中包含若干个 Sentinel 节点和 Redis 数据节点,每个Sentinel节点会对数据节点和其余 Sentinel节点进行监控,当它发现节点不可达时,会对节点做下线表示。
  • 如果下线的是主节点,它还会和其他的 Sentinel 节点进行**协商** ,当大多数 Sentinel 节点对主节点不可达这个结论达成共识之后,它们会在内部**选举出一个领导节点** 来完成自动故障转移的工作,同时将这个变化实时通知给 Redis 应用方。
  • 整个过程是完全自动的,不需要人工介入。

整体的架构如图所示:

哨兵节点的作用与配置

Redis Sentinel 是独立的 redis-sentinel 进程,系统中通常配置多个哨兵节点来共同监控数据存储节点(包括主节点和从节点)。部署多个哨兵节点的主要目的是为了增强系统的健壮性和可靠性:

  1. 防止哨兵节点自身宕机
    • 如果只有一个哨兵节点,一旦该节点故障,则无法继续监控数据节点的状态。这将导致在数据节点发生故障时,无法自动进行恢复。
  1. 防止误判数据节点故障
    • 单个哨兵节点可能会由于网络抖动、延迟或丢包等问题而误判数据节点故障。多个哨兵节点可以相互验证,减少误判的可能性。
监控与心跳检测
  • 心跳检测机制 :哨兵节点通过与数据存储节点建立 TCP长连接,并定期发送心跳包以确保数据节点处于运行状态。
    • 如果从节点挂掉,影响较小,因为可以从主节点重新同步数据。
    • 主节点挂掉则需要哨兵节点介入,启动故障恢复流程。
⭕故障恢复流程

当主节点无响应时,多个哨兵节点之间的交互过程如下:

  1. 共识达成 :多个哨兵节点向主节点发送心跳包,若主节点持续无响应,则各哨兵节点之间达成"主节点已挂"的共识。
  2. 选举Leader :在这些哨兵节点中,会通过 选举算法(如Raft)选出一个 Leader哨兵节点。
  3. 选择新主节点:由Leader负责 从现有的从节点中挑选一个作为新的主节点。
  4. 执行故障转移
    • Leader控制被选中的节点执行SLAVEOF NO ONE命令,使其成为新的主节点。
    • 控制其他从节点修改其SLAVEOF配置到新的主节点上。

5 . 通知客户端:哨兵节点会自动通知客户端程序有关新的主节点信息,确保后续写操作针对新的 主节点进行。


Docker 部署 Redis Sentinel

场景介绍
  • 在理想情况下,Redis Sentinel 的六个节点应该部署在六台不同的服务器上。然而,在实际操作中,++我们可能没有足够的物理服务器资源。++
  • 如果我们按照传统的主从复制模式来配置,需要修改每台服务器的端口号、配置文件和数据文件,这不仅配置复杂,而且容易引起端口冲突问题。此外,这种方式与多主机部署存在较大差异。

为了解决这些问题,我们可以 引入Docker 这个非常有用的工具。

Docker 简介

什么是 Docker?

  • 虚拟化技术对比:传统虚拟机通过软件模拟硬件环境,但其对硬件资源消耗较大,这对于资源有限的服务器来说是一个挑战。
  • Docker 特点:Docker可以视为"轻量级"的虚拟机,它提供了类似虚拟机的环境隔离效果,但不会大量占用硬件资源。它使得应用程序可以在几乎任何地方以相同的方式运行。

Docker 核心概念

  • 镜像 (Image):类似于模板或蓝图,包含了应用程序及其所有依赖文件、环境配置等。它是只读的,并且是创建容器的基础。
  • 容器 (Container) :基于镜像创建的运行实例,每个容器都是一个独立且隔离的进程空间。镜像与容器 的关系类似于操作系统中的"可执行程序"与"进程"。
Docker 安装

为了简化部署过程并解决资源限制的问题,我们将使用 Docker 和 Docker Compose 来安装和管理 Redis 及其哨兵节点。详情可看博主这两篇前文

[Docker#3] LXC | 详解安装docker | docker的架构与生态

[Docker#11] 容器编排 | .yml | up | 实验: 部署WordPress

以下是安装步骤:

1.安装 Docker 和 Docker Compose

  • Docker 的安装和配置(包括镜像源)相对复杂,建议参考官方文档或相关文章进行详细操作。例如,可以参阅 Docker官方文档 或者其他社区提供的教程。

2.配置 Redis 和 Sentinel

  • 使用 Docker Hub 上现成的 Redis 和 Redis Sentinel 镜像,或者自己构建自定义镜像。
  • 编写 docker-compose.yml 文件来定义服务(如 Redis 主节点、从节点和哨兵节点),并通过 Docker Compose 启动这些服务。

3.启动服务

  • 使用命令 docker-compose up -d 启动所有定义的服务,确保它们在后台以分离模式运行。

4.验证部署

  • 检查各个容器是否正常启动,并验证 Redis 主从复制和哨兵监控功能是否按预期工作。

停止之前的redis主节点和从节点的服务

  • 使用docker获取到redis镜像
    docker pull redis:5.0.9

思考:

yml 和 JSON 都是这种比较直观的键值对结构。

  • JSON 是使用 {} 来表示层级结构,YAML 则是使用缩进来表示。

Docker Compose 编排 Redis 主从及哨兵节点

为了简化 Redis 主从复制和哨兵集群的部署,我们使用 docker-compose.yml 文件来编排容器。该配置文件定义了所有需要创建的容器及其运行参数,允许我们通过一个简单的命令批量启动或停止这些容器。

编排 Redis 主从节点

首先,在 /root/redis/ 目录下创建名为 docker-compose.yml 的文件(注意:文件名必须固定,类似于 Spring 中的 application.yml)。在文件中添加如下内容:

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

字段解释

  • services 下的名称代表每个服务的名字,可以根据需求自定义。
  • ports 字段用于端口映射,如 6380:6379 表示 宿主机的 6380 端口映射到容器内的 6379 端口。
  • command 启动 Redis 的命令行参数,从节点的 --slaveof 参数可以直接使用主节点的服务名,Docker 会自动解析为主节点的 IP 地址。
  • 如果实在是向在后面直接写ip地址的话,也不一定写得上去,这是以为在容器启动之后,会动态分配ip地址,被分配的ip地址是什么,我们自己也不清楚**,**当然,我们可以通过某些手段来配置静态的ip.

该代码完成了三个数据节点的创建,通过命令完成主从关系的配置。执行之前,记得把主机上的redis停止,把637963806381三个端口空出来给docker启动的redis

在目录redis-data中执行:

docker compose up -d

这样就创建好了三个redis服务端,可以通过redis-cli -p 6379redis-cli -p 6380redis-cli -p 6381来验证是否启动成功。

编排 Redis 哨兵节点

为什么要把redis的哨兵和主从节点的配置文件分开创建?

这样做一方面是为了方便观察日志,另一方面是为了确保redis主从节点启动之后再启动哨兵节点,,如果我们把六个容器都配置在一个yml文件中的话,有可能先启动哨兵,日志的执行可能会产生变数,我们分成两组来启动的话,就可以保证上述顺序的正确.

为了便于管理和确保正确的启动顺序,建议将 Redis 哨兵节点的配置与主从节点分开。在 /root/redis-sentinel/ 目录下创建另一个 docker-compose.yml 文件:

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
    networks:
      - redis-data_default

  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
    networks:
      - redis-data_default

  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
    networks:
      - redis-data_default

networks:
  default:
    external:
      name: redis-data_default

字段解释

  • volumes 用于挂载哨兵配置文件到容器内路径。
  • networks如果没有这个字段,我们在启动redis哨兵的时候,哨兵和数据节点就处于两个局域网中.默认情况下,两个局域网是不互通的.这里我们加入了这个字段,来把redis的数据节点和哨兵节点放入同一个局域网中.
  • 其中我们先需要使用docker network ls查询到redis数据节点的局域网,之后我们把哨兵节点的局域网和数据节点修改成一样的即可.上面我们都修改成了redis-data_default,该节点就是redis数据节点的局域网.

创建哨兵配置文件

在同一目录 (/root/redis-sentinel/) 下创建三个哨兵配置文件 sentinel1.conf, sentinel2.conf, 和 sentinel3.conf,内容相同:

bind 0.0.0.0
port 26379
sentinel monitor redis-master redis-master 6379 2
sentinel down-after-milliseconds redis-master 1000

字段解释

  • bind 0.0.0.0:允许所有网络接口上的连接。
  • port 26379:指定哨兵节点监听的端口号。
  • sentinel monitor redis-master redis-master 6379 2:此行定义了哨兵监控的目标主节点。第一个 redis-master 是哨兵内部使用的名称;第二个 redis-master 是主节点的服务名(Docker会自动解析为主节点的IP地址);6379 是主节点的端口号;2 表示法定票数,即至少需要两个哨兵确认故障才能进行切换。
  • sentinel down-after-milliseconds redis-master 1000:设置哨兵认为主节点不可达的时间阈值为1000毫秒。
启动服务

完成上述配置后,可以通过以下命令启动所有容器:

# 启动Redis主从节点
cd /root/redis/
docker-compose up -d

# 启动哨兵节点
cd /root/redis-sentinel/
docker-compose up -d

如果需要停止并删除容器,可以使用 docker-compose down 命令。确保在 docker-compose.yml 文件所在的同级目录中执行这些命令。

启动后,哨兵节点会自动修改配置文件,将其监控的主节点名称解析为实际的 IP 地址,并记录其他哨兵节点的信息。

哨兵节点配置文件的自动修改

启动哨兵节点后,配置文件会被 Docker 自动修改以反映实际运行时的状态。例如,原本的 redis-master 名称被解析为具体的 IP 地址,并添加了其他哨兵节点的信息。修改后的配置文件示例:

可以看到,配置文件中的 redis-master 已经被自动解析成了 IP 地址,并且记录了已知的副本和哨兵节点信息。


问题与解决

从错误信息来看,docker-compose.yml 文件中存在几个问题导致 docker compose up -d 命令无法成功执行。以下是逐步解决这些问题的方法:

1. 更新 Docker Compose 文件格式

警告: version属性已过时

  • Docker Compose V2(即 docker compose)不再需要 version 字段,因为它会自动使用最新支持的版本。因此,建议移除 version 字段。

警告: networks.default: external.name已弃用

  • 使用 external: truename 来定义外部网络,而不是 external.name

2. 确保所有服务引用的网络已正确定义

错误: service "sentinel3" refers to undefined network redis-data_default

  • 确保在 docker-compose.yml 中正确地定义了所有服务使用的网络,并且这些网络确实存在于你的环境中。

修改后的 docker-compose.yml****示例

根据上述指导,这里是修改后的 docker-compose.yml 文件示例:

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
    networks:
      - redis-data-network

  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
    networks:
      - redis-data-network

  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
    networks:
      - redis-data-network

networks:
  redis-data-network:
    external: true
    name: redis-data_default

验证和创建外部网络

确保外部网络 redis-data_default 已经存在。如果不存在,你可以通过以下命令创建它:

docker network create redis-data_default

检查现有网络

你可以使用以下命令列出所有现有的 Docker 网络,以验证 redis-data_default 是否已经存在:

docker network ls

启动服务

完成以上步骤后,再次尝试启动服务:

docker compose up -d

注意事项

如果你之前有运行过的容器或网络,可能需要先清理它们,以避免冲突。

  • 确保你在正确的目录中执行命令,即包含 docker-compose.yml 文件的目录。
  • 如果仍然遇到问题,请检查 Docker 和 Docker Compose 的日志输出,或者提供更详细的错误信息以便进一步诊断。

选举机制(常考)

------解决监控主机宕机问题

哨兵存在的意义在于能够在 Redis 主从结构出现问题时(如主节点挂掉),自动选举一个新的主节点来代替故障的主节点,从而保证整个 Redis 系统仍然可用。

主节点选举过程

主节点的选举过程可以分为几个关键步骤:

1. 判断 主观下线

  • 哨兵节点通过给 Redis 服务器发送心跳包来判定服务器是否正常工作。
  • 如果心跳包没有得到响应,当前哨兵节点会认为该服务器已挂掉。但此时只是单个哨兵节点的主观判断。

2. 判断客观下线

  • 当 多个 哨兵节点通过投票确认服务器确实挂掉,并且票数 超过了在配置文件中设定的法定票数时,所有哨兵节点达成一致意见,认为服务器确实已宕机。

3. 选取哨兵Leader

  • 多个哨兵节点需要通过 Raft 算法选举出一个 Leader 节点,负责从现有从节点中提拔新的主节点。
  • 简而言之,Raft 算法的核心就是"先下手为强" 谁率先发出了拉票请求,谁就有更大的概率成为 leader,这里的决定因素成了 "网络延时"。

Raft 算法

  1. 每个哨兵节点都给其他所有哨兵节点发起一个拉票请求"
  2. 收到拉票请求的节点,会回复一个投票响应,当哨兵节点收到多个拉票请求,只对第一个节点投票,后续节点都不投票
  3. 一轮投票完成之后,发现得票超过半数的节点,自动成为 leader 如果出现平票的情况,就重新再投一次即可。

因此建议哨兵节点设置成奇数,如果是偶数个,则增大了平票的概率,带来不必要的开销。

最终leader 节点负责挑选一个 slave 成为新的 master 当其他的 sentenal发现新的 master 出现了,就说明选举结束了。

4.Leader 提拔新主节点

  • ⭕提拔原则
    • 优先级 :每个 Redis 数据节点在配置文件中都有一个**slave-priority**设置,优先级越高越可能被选中。
    • 数据复制偏移量 (offset) :偏移量越大表示该从节点与原主节点的数据同步程度越高,更接近最新的数据状态,因此 优先考虑 offset 较大的节点。
    • runid :如果上述条件相同,则随机挑选一个节点。runid 是每个 Redis 节点启动时自动生成的一串随机数字。

5. 设置新主节点

  • Leader 控制选定的节点执行 SLAVEOF NO ONE 命令,使其成为新的主节点。
  • 接着,Leader 再控制其他从节点执行 SLAVEOF <newMasterIp> <newMasterPort> 命令,让它们跟随新的主节点进行数据同步。

实验

现在通过docker关掉master的容器,来模拟主节点崩溃:

输出:

在日志中,vote-for-leader就是在投票选举leader哨兵。

这是第二个哨兵的日志

  • sdown master:这代表哨兵节点发现了master节点掉线,sdown表示主观认为,也就是说哨兵还不能保证master一定掉线
  • odown master:经过与其它哨兵交流,多个哨兵都认为master节点掉线,odown表示客观认为,#quorum 3/2表示法定票数为2票,目前有三个哨兵都投票认为master掉线
  • switch-master:切换主节点,此时已经有新的节点变成主节点了

进入6380端口的数据节点,输入info replication

可以看到role:master6380成为了新的主节点,当然也有可能是6381

尝试重启redis-master

这个redis-master重启后,就变成了从节点,不再是主节点了。

⭕总结:

  1. 主观下线 sdown:单个哨兵节点认为主节点掉线
  2. 客观下线 odown:投票后客观认为主节点掉线
  3. 选取哨兵leader:依据网络情况,选出一个哨兵成为leader,由leader完成选举master
  4. 选举master:由leader依据优先级,数据同步进度,run id来选出一个节点成为master
  5. 重构主从关系:由leader哨兵,指定数据节点执行指令,重构主从关系
注意事项
  • 哨兵节点数量 :建议哨兵节点为奇数个,以避免在投票选举 Leader 时出现平票情况。
  • 哨兵职责 :哨兵不负责存储数据,仅用于监控和故障恢复。
  • 数据丢失风险:哨兵模式不能完全防止极端情况下写数据丢失的问题,例如,在数据未完全同步到从节点前主节点就宕机了,这种情况下丢失的数据将无法恢复。
  • 存储容量 :哨兵模式不会提高数据的存储容量,它主要关注的是高可用性和故障转移。
  • 哨兵节点 可以使用一些 配置不高的机器来部署

Redis 的主从复制模式可以将主节点的数据改变同步给从节点,这样从节点就可以起到两个作用:

  1. 作为主节点的一个备份,一旦主节点出了故障不可达的情况,从节点可以作为后备,并且保证数据尽量不丢失(主从复制表现为最终一致性)。
  2. 从节点可以分担主节点上的读压力,让主节点只承担写请求的处理,将所有的读请求负载均衡到各个从节点上。

但是主从复制模式并不是万能的,它同样遗留下以下几个问题:

  1. 主节点发生故障时,进行主备切换的过程是复杂的,需要完全的人工参与,导致故障恢复时间无法保障。
  2. 主节点可以将读压力分散出去,但 写压力/存储压力是无法被分担的,还是受到单机的限制。

其中第一个问题是高可用问题,即 Redis 哨兵主要解决的问题。第二个问题是属于存储分布式的问题,留给 Redis 集群去解决,本篇博客集中讨论了第一个问题,下一篇将对第二个问题进行讨论~

相关推荐
liuweni2 分钟前
Next.js系统性教学:深入理解缓存交互与API缓存管理
开发语言·前端·javascript·经验分享·缓存·前端框架·交互
潘多编程10 分钟前
Spring Boot性能提升:实战案例分析
java·spring boot·后端
m0_7482561410 分钟前
Spring Boot 整合 Keycloak
java·spring boot·后端
web1368856587117 分钟前
Spring Boot 中使用 @Transactional 注解配置事务管理
数据库·spring boot·sql
AskHarries1 小时前
如何利用EasyExcel导出带有选择校验框的excel?
java·后端·spring cloud·excel
小码编匠1 小时前
C#上位机实现高效示波器功能
后端·c#·.net
骷大人1 小时前
mysql开启配置binlog
数据库·mysql
Yolo_nn1 小时前
MySQL_第14章_存储过程与函数
数据库·mysql·存储过程·函数
GDDGHS_2 小时前
Flink自定义数据源
大数据·数据库·flink
Milk夜雨2 小时前
数据库进阶教程:结合编程实现动态数据操作
数据库·python·adb