目录
[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
进程,系统中通常配置多个哨兵节点来共同监控数据存储节点(包括主节点和从节点)。部署多个哨兵节点的主要目的是为了增强系统的健壮性和可靠性:
- 防止哨兵节点自身宕机:
-
- 如果只有一个哨兵节点,一旦该节点故障,则无法继续监控数据节点的状态。这将导致在数据节点发生故障时,无法自动进行恢复。
- 防止误判数据节点故障:
-
- 单个哨兵节点可能会由于网络抖动、延迟或丢包等问题而误判数据节点故障。多个哨兵节点可以相互验证,减少误判的可能性。
监控与心跳检测
- 心跳检测机制 :哨兵节点通过与数据存储节点建立 TCP长连接,并定期发送心跳包以确保数据节点处于运行状态。
-
- 如果从节点挂掉,影响较小,因为可以从主节点重新同步数据。
- 主节点挂掉则需要哨兵节点介入,启动故障恢复流程。
⭕故障恢复流程
当主节点无响应时,多个哨兵节点之间的交互过程如下:
- 共识达成 :多个哨兵节点向主节点发送心跳包,若主节点持续无响应,则各哨兵节点之间达成"主节点已挂"的共识。
- 选举Leader :在这些哨兵节点中,会通过 选举算法(如Raft)选出一个 Leader哨兵节点。
- 选择新主节点:由Leader负责 从现有的从节点中挑选一个作为新的主节点。
- 执行故障转移:
-
- Leader控制被选中的节点执行
SLAVEOF NO ONE
命令,使其成为新的主节点。 - 控制其他从节点修改其
SLAVEOF
配置到新的主节点上。
- Leader控制被选中的节点执行
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
停止,把6379
、6380
、6381
三个端口空出来给docker
启动的redis
。
在目录redis-data
中执行:
docker compose up -d
这样就创建好了三个redis
服务端,可以通过redis-cli -p 6379
、redis-cli -p 6380
、redis-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: true
和name
来定义外部网络,而不是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
算法:
- 每个哨兵节点都给其他所有哨兵节点发起一个
拉票请求
" - 收到拉票请求的节点,会回复一个
投票响应
,当哨兵节点收到多个拉票请求
,只对第一个节点投票,后续节点都不投票 - 一轮投票完成之后,发现得票超过半数的节点,自动成为
leader
如果出现平票的情况,就重新再投一次即可。
因此建议哨兵节点设置成奇数,如果是偶数个,则增大了平票的概率,带来不必要的开销。
最终leader
节点负责挑选一个 slave
成为新的 master
当其他的 sentenal
发现新的 master
出现了,就说明选举结束了。
4.Leader 提拔新主节点
- ⭕提拔原则
-
- 优先级 :每个 Redis 数据节点在配置文件中都有一个**
slave-priority
**设置,优先级越高越可能被选中。 - 数据复制偏移量 (offset) :偏移量越大表示该从节点与原主节点的数据同步程度越高,更接近最新的数据状态,因此 优先考虑 offset 较大的节点。
- runid :如果上述条件相同,则随机挑选一个节点。
runid
是每个 Redis 节点启动时自动生成的一串随机数字。
- 优先级 :每个 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:master
,6380
成为了新的主节点,当然也有可能是6381
。
尝试重启redis-master
:
这个redis-master
重启后,就变成了从节点,不再是主节点了。
⭕总结:
- 主观下线 sdown:单个哨兵节点认为主节点掉线
- 客观下线 odown:投票后客观认为主节点掉线
- 选取哨兵leader:依据网络情况,选出一个哨兵成为leader,由leader完成选举master
- 选举master:由leader依据优先级,数据同步进度,run id来选出一个节点成为master
- 重构主从关系:由leader哨兵,指定数据节点执行指令,重构主从关系
注意事项
- 哨兵节点数量 :建议哨兵节点为奇数个,以避免在投票选举 Leader 时出现平票情况。
- 哨兵职责 :哨兵不负责存储数据,仅用于监控和故障恢复。
- 数据丢失风险:哨兵模式不能完全防止极端情况下写数据丢失的问题,例如,在数据未完全同步到从节点前主节点就宕机了,这种情况下丢失的数据将无法恢复。
- 存储容量 :哨兵模式不会提高数据的存储容量,它主要关注的是高可用性和故障转移。
- 哨兵节点 可以使用一些 配置不高的机器来部署
Redis
的主从复制模式可以将主节点的数据改变同步给从节点,这样从节点就可以起到两个作用:
- 作为主节点的一个备份,一旦主节点出了故障不可达的情况,从节点可以作为后备,并且保证数据尽量不丢失(主从复制表现为最终一致性)。
- 从节点可以分担主节点上的读压力,让主节点只承担写请求的处理,将所有的读请求负载均衡到各个从节点上。
但是主从复制模式并不是万能的,它同样遗留下以下几个问题:
- 主节点发生故障时,进行主备切换的过程是复杂的,需要完全的人工参与,导致故障恢复时间无法保障。
- 主节点可以将读压力分散出去,但 写压力/存储压力是无法被分担的,还是受到单机的限制。
其中第一个问题是高可用问题,即 Redis
哨兵主要解决的问题。第二个问题是属于存储分布式的问题,留给 Redis
集群去解决,本篇博客集中讨论了第一个问题,下一篇将对第二个问题进行讨论~