接上次博客: Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、数据同步psync)、总结-CSDN博客
目录
Ubuntu安装 docker:Install Docker Engine on Ubuntu | Docker Docs
停止之前的 redis-server
理解 sentinel down-after-milliseconds
leader 挑选出合适的 slave 成为新的 master
我们接下来要学习的主要内容如下:
-
Redis Sentinel 的概念:
- Redis Sentinel是一种用于监控和管理Redis集群的系统,它能够自动进行主从切换,以及监控Redis实例的健康状态。
- Sentinel通过监控主节点的状态,并在主节点发生故障时自动将一个从节点升级为新的主节点,从而实现自动故障转移。
-
Redis Sentinel 的部署:
Redis Sentinel需要部署在独立的服务器上,并与Redis实例一起运行。通常建议部署多个Sentinel实例以提高可用性。 -
Redis Sentinel 命令:
Sentinel提供了一系列用于管理和监控Redis集群的命令,包括监控主从节点状态、执行故障转移、以及配置管理等命令。 -
Redis Sentinel 客户端:
Sentinel客户端允许应用程序与Sentinel系统进行通信,以获取Redis集群的状态信息,以及接收有关主从切换的通知。 -
Redis Sentinel 实现原理:
- Sentinel通过定期向Redis实例发送PING命令来监控其健康状态,并通过QUORUM算法来进行故障转移的决策。
- Sentinel还使用选举算法来选举出一个Sentinel作为领导者,负责执行故障转移操作。
通过使用Redis Sentinel,可以实现Redis集群的自动化监控和故障转移,从而提高了Redis集群的可用性和可靠性,减少了人工干预的需要。
基本概念
Redis作为一种流行的内存数据库,有许多独特的概念和术语,所以在介绍Redis Sentinel之前,我们需要对一些与Redis相关的名词概念进行必要的说明:
Redis Sentinel 相关名词解释
名词 | 逻辑结构 | 物理结构 |
---|---|---|
主节点 | Redis 主服务 | 一个独立的redis-server进程 |
从节点 | Redis 从服务 | 一个独立的redis-server进程 |
Redis数据节点 | 主从节点 | 主节点和从节点的进程 |
哨兵节点 | 监控Redis数据节点的节点 | 一个独立的redis-sentinel进程 |
哨兵节点集合 | 若干哨兵节点的抽象组合 | 若干redis-sentinel进程 |
Redis哨兵(Sentinel) | Redis提供的高可用方案 | 哨兵节点集合和Redis主从节点 |
应用方 | 泛指一个或多个客户端 | 一个或多个连接Redis的进程 |
哨兵机制是一种通过独立的进程来解决 Redis 主节点挂掉的问题的方法。这些独立的进程称为哨兵节点,与之前的 Redis 服务器进程不同。哨兵节点不负责存储数据,而是监控其他 Redis 服务器进程的运行状态。
当单个哨兵节点挂掉时,通常不会造成严重问题,因为通常会部署多个哨兵节点构成一个集合。这样,即使一个哨兵节点挂掉,其他哨兵节点仍然可以监控 Redis 主节点的状态,并且当主节点出现故障时,其他哨兵节点可以通过投票来选举出一个新的主节点。这种方式确保了 Redis 集群的高可用性和容错性。
Redis Sentinel 是 Redis 的高可用实现方案。在实际的生产环境中,它扮演着关键的角色,为系统提供了稳定可靠的服务。在面对可能的故障时,Redis Sentinel能够迅速识别问题并采取必要的措施,确保系统持续运行。
接下来,
我们首先将对主从复制模式下可能出现的各种问题进行全面梳理,包括网络故障、主节点故障、从节点故障等等。这些问题可能会影响整个系统的可用性和稳定性,因此需要得到妥善解决。
随后,我们将引入高可用性的概念。高可用性不仅仅是指系统能够在面对故障时保持可用,还意味着系统能够在极端条件下保持稳定运行,并且能够自动化地处理故障,以减少人为干预的需求。Redis Sentinel正是基于这样的理念,设计出了其独特的架构和算法。
最后,我们将深入分析 Redis Sentinel 的基本架构和优势。Redis Sentinel采用了分布式监控和自动故障恢复机制,能够实时监测 Redis 服务器的状态,并在发现故障时自动进行故障转移,以确保系统的连续性和可用性。同时,Redis Sentinel还支持灵活的配置和扩展,能够适应不同规模和需求的系统环境,为用户提供了强大而可靠的高可用解决方案。
主从复制的问题
Redis的主从复制模式将主节点的数据改变同步给从节点,从而实现了两个重要作用:
首先,从节点作为主节点的备份,当主节点发生故障或不可达时,从节点可以顶替主节点的角色,确保系统的持续可用性,并尽量保证数据不丢失。这种备份机制提供了一种容错机制,使得即使主节点出现问题,整个系统也能继续运行,避免了单点故障导致的系统崩溃。
其次,从节点可以分担主节点的读压力。通过将读请求负载均衡到各个从节点上,主节点可以专注于处理写请求,从而提高系统的整体性能和可扩展性。这种读写分离的设计可以有效地提高系统的并发处理能力,同时减轻主节点的负担,降低单点故障的风险。
然而,主从复制模式也存在一些问题:
-
主备切换复杂: 当主节点发生故障时,进行主备切换的过程相对复杂。在传统的主从复制模式中,通常需要管理员手动介入,识别故障并手动将一个从节点晋升为主节点。这个过程可能会涉及到网络配置、数据同步等多个步骤,导致故障恢复时间不可预测,影响系统的可用性和性能。
-
写压力无法分担: 虽然主节点可以将读压力分担给从节点,但写压力和存储压力仍然无法被分担。主节点仍然承担着所有写请求的处理,如果写压力过大,可能会导致主节点的性能瓶颈,甚至影响系统的稳定性。
针对这些问题,Redis引入了Sentinel来解决高可用性问题。Redis Sentinel是一个分布式系统,用于监控Redis集群中各个节点的状态,并在发现故障时自动进行故障转移。它可以自动识别主节点的故障,并将一个从节点晋升为主节点,从而实现故障恢复的自动化。此外,Redis Sentinel还支持配置灵活、扩展性强的特点,可以根据需要动态调整集群的配置,以适应不同规模和负载的系统环境。通过Redis Sentinel的监控和自动化故障处理机制,可以大大提高系统的可用性和稳定性,降低故障恢复的时间成本,从而保证系统能够在面对各种故障和异常情况时依然能够正常运行。
人工恢复主节点故障
Redis 主节点故障后需要进行的操作
用户监控是指通过编写程序来监视服务器的运行状态。在实际开发中,特别是对于服务器后端开发来说,监控程序是至关重要的。服务器需要具有高可用性,保证 7x24 小时的稳定运行。由于服务器长时间运行,难免会遇到一些意外情况,但具体什么时候会出现问题我们并不知道,同时也无法完全依靠人工来持续监控服务器的运行状态。
因此,我们编写监控程序来自动监视服务器的运行状态。当监控程序发现服务器的运行状态异常时,它会触发报警程序。报警程序可以通过多种方式进行通知,如短信、电话、邮件、微信、钉钉、飞书等。它会向相关的开发人员发送警报,通知他们服务器出现了问题或者程序挂了,以便及时进行处理和修复。这种自动化的用户监控系统大大提高了服务器的稳定性和可靠性,帮助开发人员及时发现并解决问题,保障了系统的正常运行。
这个时候,程序猿恢复服务器的运行通常需要遵循以下步骤:
-
评估主节点状态: 首先,程序猿会评估主节点的状态,看看是否还有可能抢救。他们会检查主节点是什么原因挂掉的,以及是否可以立即解决问题。
-
选择新的主节点: 如果主节点无法立即恢复,或者问题不易定位和解决,程序猿会选择一个从节点作为新的主节点:
- a) 独立山头: 他们会通过将选中的从节点设置为主节点,使用命令 slaveof no one,使其独立运行,成为新的主节点。
- b) 更新从节点: 然后,程序猿会修改其他从节点的配置,将它们的主节点设置为新的主节点的IP和端口,通过命令 slaveof <新主节点IP> <端口>,重新连接到新的主节点上。
- c) 通知客户端: 接下来,他们会通知客户端更新配置,以便客户端能够连接到新的主节点,并使用新的主节点来进行数据操作。
-
恢复之前的主节点: 一旦之前的主节点修复好了,程序猿可以将其作为新的从节点添加到集群中,从而恢复完整的集群结构。
综上,人工恢复主节点故障的过程如下:
-
监控系统检测故障: 运维人员通过监控系统实时监测Redis集群的状态。当监测到主节点故障或宕机时,监控系统会发出警报通知运维人员。
-
选择新主节点: 运维人员需要手动从所有可用节点中选择一个作为新的主节点。通常情况下,会选择其中一个从节点(例如slave 1)来担任新的主节点。选择新主节点时,需要考虑节点的健康状态、数据完整性等因素。
-
执行切换命令: 一旦确定了新的主节点,运维人员需要登录到被选中的从节点,并执行命令slaveof no one,将其从从节点转变为新的主节点。这个过程会导致该节点停止从原主节点复制数据,开始接受新的写入请求。
-
从节点重新同步: 运维人员还需确保其他从节点能够与新的主节点进行数据同步。他们需要逐个登录到每个从节点,执行命令slaveof {newMasterIp} {newMasterPort},将其配置为新主节点的从节点,并开始重新同步数据。
-
更新应用连接信息: 在故障切换完成后,需要确保应用程序连接的主节点信息已更新为新主节点的IP地址和端口号。这可以通过配置文件或者动态修改应用程序的连接参数来完成。
-
处理原主节点恢复: 如果原主节点在一段时间后恢复正常,运维人员需要手动将其重新加入到集群中。可以通过执行命令slaveof {newMasterIp} {newMasterPort},让原主节点成为新主节点的从节点,从而保持集群的完整性。
尽管人工恢复过程相对繁琐且容易出错,但在没有自动化的故障转移方案时仍然是一种常见的做法。
然而,随着技术的发展,自动化故障转移方案如Redis Sentinel已经成为更可靠和高效的选择。Redis Sentinel能够自动监测主节点的状态,并在发生故障时自动进行故障转移,从而大大减少了人工干预的需要,提高了系统的可用性和稳定性。
哨兵自动恢复主节点故障
单独的 Redis Sentinel 进程提供了多个实例,它们独立运行并负责监控 Redis 集群中的主节点和从节点。这些 Sentinel 进程会相互通信,建立 TCP 长连接,并定期发送心跳包以确保通信畅通。通过这种监控机制,哨兵能够及时发现某个节点是否宕机。如果是从节点宕机,通常不会对整个集群的运行造成影响;但如果是主节点宕机,哨兵就会发挥作用。
当一个哨兵节点发现主节点宕机时,它并不会单独采取行动,而是需要其他哨兵节点共同认同该主节点已经宕机,以避免误判。多个哨兵节点共同工作,通过投票的方式选举出一个领导者(leader)。领导者负责从集群中的从节点中选出一个新的主节点。
选定新的主节点后,哨兵节点会自动控制该节点执行命令 slaveof no one,使其成为新的主节点。同时,它还会控制其他从节点,将它们的主节点修改为新的主节点。此外,哨兵节点会自动向客户端程序发送通知,告知新的主节点是谁,以便客户端进行后续的写操作。这种自动化的监控和故障转移机制大大提高了 Redis 集群的可用性和稳定性。
Redis Sentinel 架构
在主节点发生故障时,Redis Sentinel发挥着关键作用,它能够自动完成故障发现和故障转移,并及时通知应用方,从而实现真正的高可用性。Redis Sentinel采用了分布式架构,由若干个Sentinel节点和Redis数据节点组成。每个Sentinel节点都负责监控数据节点以及其他Sentinel节点的状态,一旦发现节点不可达,就会做出相应的下线标识。
具体来说,当一个Sentinel节点发现某个节点(通常是主节点)不可达时,它会与其他Sentinel节点进行协商。如果大多数Sentinel节点都对主节点不可达的结论达成一致,它们会在内部进行领导节点的选举,选出一个节点来执行自动故障转移的工作。这个选举过程是基于分布式一致性算法来确保高可用性和正确性的。同时,Sentinel节点还会即时将故障转移的信息通知给Redis应用方,以便应用方能够及时感知到主节点的故障转移。
整个故障转移过程是完全自动化的,不需要人工干预。Redis Sentinel通过监控、协商、选举等机制,能够快速而可靠地应对主节点故障,确保系统的连续可用性。这种自动化的故障处理机制极大地降低了系统管理的复杂性,提高了系统的稳定性和可靠性。
💡在这里提到的分布式架构是指Redis Sentinel的架构,其中包含了Redis数据节点、Sentinel节点集合以及客户端分布在多个物理节点上。这种架构的设计旨在提高系统的可靠性和可用性,通过将不同的组件部署在多个物理节点上,可以有效地分散单点故障的风险,并且能够更好地应对硬件故障、网络问题等情况。
具体来说,Redis数据节点负责存储数据和处理客户端请求,Sentinel节点则负责监控和管理Redis数据节点的状态以及执行故障转移等任务。客户端可以连接到任何一个Redis节点,通过Sentinel节点来获取集群的状态信息和执行故障转移等操作,从而实现对Redis集群的管理和监控。
需要注意的是,在这种架构下,Redis数据节点和Sentinel节点都是独立部署的,它们之间没有直接的数据交互,而是通过网络进行通信。这种分布式架构使得整个系统更具弹性和容错能力,即使某个物理节点出现故障,也不会影响整个系统的正常运行。
总之,这里提到的分布式架构是Redis Sentinel的架构,不要与后边我们介绍的 Redis Cluster 分布式混淆。
Redis Sentinel相比主从复制模式的重要改进之一是引入了多个(通常建议奇数个)Sentinel节点来实现对数据节点的监控和管理。这些Sentinel节点充当了系统的监控者和协调者,在集群中承担着至关重要的角色。
通过引入多个Sentinel节点,系统具备了更强的可靠性和容错能力。当有多个Sentinel节点同时监控集群时,即使部分Sentinel节点出现故障或无法正常工作,仍然可以保证集群的监控和管理功能不受影响。为了确保在进行故障检测和故障转移时能够达成多数派的共识,建议使用奇数个Sentinel节点。这样可以避免出现投票的平局情况,提高了系统的稳定性和可靠性。
Sentinel节点定期监控所有节点的状态,包括数据节点和其他Sentinel节点。它们检查节点的可达性、性能指标以及其他关键信息,以便及时发现并响应任何可能影响系统运行的问题。这种监控机制能够帮助Sentinel节点实现对整个集群的实时监控,从而能够在出现故障或异常情况时快速做出响应。
当Sentinel节点发现节点出现故障或不可达时,它们会采取相应的措施来恢复集群的健康状态。这可能包括执行自动故障转移操作,将从节点提升为主节点,以确保系统的连续可用性。此外,Sentinel节点还会通知管理员或其他系统组件,以便及时进行手动干预或调整。
在主节点故障的情况下,故障转移流程经过以下步骤:
-
主节点故障:当主节点发生故障时,从节点的与主节点的同步连接会中断,主从复制停止。
-
哨兵节点监控:哨兵节点定期监控所有节点,包括数据节点和其他哨兵节点。一旦发现主节点故障,哨兵节点将触发故障转移的流程。这种机制确保了对主节点故障的快速响应和及时处理。
-
共识协商:哨兵节点之间进行共识协商,以确定主节点是否真的发生了故障。这个过程是为了防止误报,特别是在某个发现故障的哨兵节点自身出现了故障或网络分区的情况下。通过达成多数哨兵节点对主节点故障的共识,确保了故障转移操作的正确性和可靠性。
-
领导者选举:如果哨兵节点一致认定主节点发生了故障,它们将使用Raft算法进行领导者选举。Raft算法能够确保在分布式系统中选出唯一的领导者,以便协调后续的故障转移操作。
-
故障转移执行:一旦选出领导者,它将负责执行故障转移操作。这包括从多个从节点中选择一个作为新的主节点,并将其升级为主节点;然后,领导者通知其他从节点同步新的主节点;最后,它会通知应用层将操作转移到新的主节点上。
通过以上流程,Redis Sentinel能够实现对主节点故障的自动化处理,保证了系统的高可用性和稳定性。多个Sentinel节点之间的协作以及Raft算法的应用确保了故障转移过程的正确性和可靠性,使得系统能够在主节点故障时自动完成故障转移,减少了人工干预的需求,提高了系统的自动化程度。
其实我们只设置一个单独的 Redis 哨兵节点也是可行的,但是如果哨兵节点只有一个,它本身也容易出现问题。如果这个哨兵节点挂了,后续 Redis 节点出现问题时就无法进行自动的恢复过程,同时误判的概率也较高。
在分布式系统中,避免使用单点是基本的原则,因为单点存在单点故障的风险。网络传输数据容易出现抖动、延迟或丢包等问题,如果只有一个哨兵节点,一旦出现上述问题,将会对整个系统造成较大的影响。
因此,为了提高系统的可用性和稳定性,应该使用多个哨兵节点来共同监控 Redis 集群,并采用适当的故障转移策略,以确保在发生主节点宕机等故障时能够及时进行自动的故障恢复。
一定要牢记最基本的原则:分布式系统中应该要避免使用“单点”。
通过上述介绍,我们可以清楚地了解到Redis Sentinel具有以下几个重要功能:
-
监控: Sentinel节点定期检测Redis数据节点以及其他哨兵节点的可达性。这种监控机制确保了系统能够实时感知到节点的状态变化,包括主节点的故障或其他节点的不可达情况。监控功能使得Sentinel能够及时发现潜在的问题并采取相应的措施,从而确保了系统的稳定性和可用性。
-
故障转移: Redis Sentinel能够实现从节点的晋升(promotion)为主节点,并维护后续的正确主从关系。当主节点发生故障或不可达时,Sentinel节点会协调进行故障转移操作,选出一个合适的从节点作为新的主节点,并确保其他从节点及时同步新的主节点数据。这种故障转移功能使得系统能够在主节点故障时自动完成故障转移,保证了系统的连续可用性。
-
通知: Sentinel节点会将故障转移的结果通知给应用方。一旦完成故障转移,Sentinel节点会向应用方发送通知,告知新的主节点信息以及故障转移的结果。这种通知机制使得应用方能够及时了解到系统发生的变化,从而能够及时做出相应的处理和调整,确保了应用的正常运行。
综上所述,Redis Sentinel不仅能够实现对Redis集群的监控和故障转移,还能够及时向应用方提供相关通知,使得整个系统具备了更高的可靠性、稳定性和自动化程度。这些功能使得Redis Sentinel成为构建高可用Redis集群的重要组成部分,为用户提供了可靠的数据存储和服务支持。
安装部署 (基于 docker)
我们接下来就参考这张图进行部署:
在实际生产环境中,为了确保高可用性和可靠性,我们通常会将这些节点部署在不同的物理服务器上,以减少单点故障的风险。这样即使某个服务器发生故障,其他服务器上的节点仍然可以正常工作,保障系统的稳定性和可用性。
然而,由于我们只有一个云服务器,那么我们就只能暂时将上述6个节点部署在一个云服务器上,来完成这里的环境搭建。
当所有节点都部署在同一台服务器上时,会面临诸多潜在的冲突和问题,包括端口号、配置文件、数据文件等方面的冲突。这种部署方式容易引发混乱和不稳定性,增加系统运维的复杂度。
相比之下,将节点部署在不同的主机上可以有效地避免这些问题。每个节点都在独立的环境中运行,互不干扰,可以避免资源冲突和竞争。此外,在不同主机上部署还能提高系统的可用性和容错性,因为单个主机的故障不会影响其他主机上的节点正常运行。
总归,我们选择直接部署就需要小心翼翼的去避开这些冲突,类似于我们上次进行主从结构配置的方式,非常繁琐,同时也会和分布式部署存在较大差异。
Docker 是解决上述问题的一种有效方法。
通过 Docker,我们可以在同一台物理服务器上创建多个独立的容器,每个容器都运行在自己的隔离环境中,拥有独立的文件系统、网络空间等。这样就可以避免节点之间的资源冲突和干扰,同时提供了更高的灵活性和可移植性。
与传统的虚拟机相比,Docker 容器更为轻量级,因为它们共享主机操作系统的内核,不需要额外的操作系统镜像。这使得 Docker 容器的启动速度更快,资源占用更少,更适合于快速部署和扩展。
Docker简介
当谈论 Docker 时,我们其实是在谈论一种容器化技术。容器是一种轻量级、可移植的软件包,其中包含了应用程序及其所有依赖项,如代码、运行时环境、系统工具、库等。Docker 则是一种平台,用于创建、管理和运行这些容器。
现在让我们来想象一下,我们正在开发一个应用程序。通常情况下,我们需要在我们的开发环境中安装各种依赖项、库和工具,才能使应用程序正常运行。但是,一旦我们将应用程序交付给其他人,他们也需要配置相同的环境才能运行我们的应用程序。这可能会导致诸多问题,比如环境配置的不一致、依赖项的冲突等。
这就是 Docker 出现的意义所在。使用 Docker,我们可以将我们的应用程序及其所有依赖项打包成一个容器。这个容器可以在任何支持 Docker 的环境中运行,而不受环境的影响。这意味着我们可以将应用程序与其运行时环境隔离开来,使其更易于开发、交付和运行。
Docker 还提供了一种简单的方式来管理容器,包括创建、启动、停止、删除和监视容器等操作。此外,Docker 还提供了一种称为 Docker Hub 的服务,它是一个集中存储和共享容器镜像的地方,我们可以从中获取现成的容器镜像,也可以将自己的容器镜像上传到 Docker Hub 上与他人共享。
综上所述,Docker 是一种容器化平台,它使开发人员能够更轻松地构建、交付和运行应用程序,同时确保应用程序在不同环境中的一致性运行。
准备工作
安装 docker 和 docker-compose
Install Docker Engine | Docker Docs
Docker 在不同平台上的支持情况可能有所不同。一般来说,Docker 主要支持以下两类平台:
-
服务器版本(Server Versions):
- Linux:Docker 最初是在 Linux 上开发和运行的,并且对 Linux 平台的支持最为完善和稳定。几乎所有的 Linux 发行版都能够运行 Docker,包括但不限于 Ubuntu、CentOS、Debian、Fedora、Red Hat Enterprise Linux (RHEL) 等。
- Windows Server:Docker 也提供了对 Windows Server 的支持,允许用户在 Windows Server 环境中运行 Docker 容器。但是需要注意的是,Docker for Windows Server 需要 Windows Server 2016 或更新版本,并且要求使用 Windows 容器模式。
-
桌面版本(Desktop Versions):
- macOS:对于 macOS 用户,Docker 提供了 Docker Desktop for Mac,可以在 macOS 上轻松运行 Docker 容器。Docker Desktop for Mac 使用了 HyperKit 虚拟化技术,实现了在 macOS 上运行 Linux 容器的功能。
- Windows:Docker Desktop for Windows 是专为 Windows 用户设计的 Docker 版本,允许在 Windows 10 或更新版本的 Windows 操作系统上运行 Docker 容器。Docker Desktop for Windows 使用了 Hyper-V 虚拟化技术,支持在 Windows 上运行 Windows 容器和 Linux 容器。
我们重点学习Server版本。
Ubuntu安装 docker:Install Docker Engine on Ubuntu | Docker Docs
安装依赖:
1、确定操作系统版本:本次我们使用的是Ubuntu 22.04,建议至少在18.04以上,最好在20.04以上。
2、确定CPU:可以看到我们的是X86_64,是⽀持的,如果是arm⼀般会显示aarch64:
信息如下:
- 操作系统:Ubuntu
- 内核版本:5.15.0-86-generic
- 架构:x86_64
3、卸载旧版本:
sudo apt-get remove docker docker-engine docker.io container
4、卸载历史版本:
如果你卸载之后输入“docker”,相关信息仍然出现,那么需要进一步卸载历史版本:
卸载 Docker 相关软件并删除相关目录的命令:
# 卸载 Docker 相关软件
sudo apt-get purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras -y
# 删除 Docker 相关目录
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd
# 根据实际情况设置的目录
sudo rm -rf /data/var/lib/docker
sudo rm -rf /etc/docker/daemon.json
如果是新购买的云服务器是没有的,比如输⼊docker并没有这个命令,就不需要卸载。
5、配置docker下载源:
安装 curl 工具,创建用于保存 Docker GPG Key 的目录,并下载 Docker GPG Key 并保存到指定目录。然后,添加 Docker 源以便我们能够安装 Docker。
# 使用 curl 安装 curl 命令
sudo apt install curl -y
# 创建 GPG Key 目录
sudo mkdir -m 0755 -p /etc/apt/keyrings
# 下载 Docker 的 GPG Key 并保存到指定目录
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor --yes -o /etc/apt/keyrings/docker.gpg
# 添加 Docker 源
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
6、安装:
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
查看安装结果
7、⾃动启动配置:
# 配置加载
sudo systemctl daemon-reload
# 启动服务
sudo systemctl start docker
# 开启启动
sudo systemctl enable docker
# 查看服务状态
sudo systemctl status docker
8、检查安装结果查看版本:
9、更详细查看docker 信息:
10、执行hello-world可以看到,Hello from Docker,表面docker服务正常:
安装docker-compose
当我们使用 Docker 运行应用程序时,通常会涉及到多个容器,例如一个容器用于运行 Web 服务器,另一个容器用于运行数据库。这些容器之间需要协调和通信才能正确运行应用程序。
Docker Compose 就像是一个管理这些容器的“管家”。它让我们可以用一个文件(通常是 YAML 格式)描述应用程序的各个部分,包括哪些容器、它们之间如何交互、需要暴露哪些端口等等。然后,通过简单的命令,比如 docker-compose up,Docker Compose 就会自动启动所有容器,并根据我们的配置让它们开始工作。
换句话说,Docker Compose 让我们能够以一种更加简洁和便捷的方式来管理复杂的 Docker 应用程序,而不需要手动逐个操作每个容器。这使得开发人员能够更快速、更轻松地搭建和管理 Docker 化的应用程序,提高了开发效率。
apt install docker-compose
停止之前的 redis-server
# 停止 redis-server
sudo service redis-server stop
# 如果已经有 redis-sentinel 在运行,则停止它
sudo service redis-sentinel stop
使用 docker 获取 redis 镜像
镜像是 Docker 中的一种重要概念,类似于操作系统中的安装包或者可执行程序。它是一个只读的文件,其中包含了运行一个容器所需的所有文件、配置和依赖项。镜像可以理解为是一个容器的模板或者蓝图,它定义了容器运行时的环境和行为。
镜像可以由用户自行构建,也可以直接从 Docker Hub 等镜像仓库中获取。Docker Hub 是一个存储和分享 Docker 镜像的平台,类似于 GitHub,其中包含了许多其他用户构建和分享的镜像。用户可以通过 Docker Hub 方便地获取他人分享的镜像,并在自己的环境中使用。
在 Docker 中,镜像和容器之间的关系类似于“可执行程序”和“进程”的关系。镜像可以看作是容器运行时的环境配置,而容器则是根据镜像创建的运行实例。当用户启动一个容器时,Docker 会根据指定的镜像创建一个容器,并在其中运行相应的应用程序或服务。
对于 Redis 这样的应用程序,官方提供了已经构建好的镜像,我们只需从 Docker Hub 上获取官方镜像,即可快速部署 Redis 服务,而无需手动构建镜像。
# 拉取 Redis 5.0.9 镜像
docker pull redis:5.0.9
git pull 是使用 Git 从中央代码仓库拉取代码更新的命令,它会将最新的代码变更同步到本地工作目录中,使得本地代码与远程仓库保持一致。
docker pull 是使用 Docker 从中央镜像仓库(默认是 Docker Hub)拉取镜像的命令。
拉取到的镜像通常包含一个精简的 Linux 操作系统和相应的软件包,比如 Redis。通过拉取这样的镜像,用户可以轻松地部署所需的应用程序或服务。例如,拉取了包含 Redis 的镜像后,用户只需基于这个镜像创建一个容器,即可快速搭建 Redis 服务器,无需手动安装和配置。这种方式大大简化了应用程序的部署和管理流程,提高了开发和运维的效率。
编排 redis 主从节点
接下来,我们将利用 Docker 来构建 Redis 哨兵环境,借助 Docker Compose 进行容器编排。在这个环境中,我们计划包括一个主节点、两个从节点和三个哨兵节点,总共六个容器。手动配置这些容器会非常繁琐,因此我们选择使用 Docker Compose 这个工具。
Docker Compose 允许我们通过一个简单的 YAML 配置文件来描述多个容器的运行参数和关联关系。通过定义这个配置文件,我们可以清晰地指定要创建哪些容器、每个容器的运行参数以及它们之间的关系。
一旦配置文件准备好,我们只需运行简单的命令,Docker Compose 就能够根据配置文件自动创建、启动、停止和删除相关的容器。这种自动化流程大大简化了环境的部署和维护,提高了效率和一致性。
借助 Docker Compose,我们可以轻松地批量管理 Redis 服务器和哨兵节点,确保整个 Redis 哨兵环境的稳定运行和高可用性。这种方式使得部署和管理 Redis 环境变得更加简单和可靠。
编写docker-compose.yml文件:
YAML(YAML Ain't Markup Language)是一种人类可读的数据序列化格式,旨在成为一种易于阅读且易于编写的数据格式。它经常用于配置文件和数据传输,特别是在应用程序开发和系统管理中。
简介:
轻量级和人类可读:YAML使用缩进和明确的结构,使其对人类来说更易于阅读和理解,这使得它成为一个流行的配置文件格式。
层级结构:YAML支持层级结构,可以使用缩进来表示父子关系,这使得数据组织更加清晰。
可扩展性:YAML允许使用键值对、列表和嵌套结构,这使得它非常适合表示复杂的数据结构。
注释支持:YAML支持注释,这使得开发者可以在配置文件中添加解释性的说明,增强了文件的可读性。
跨平台兼容性:YAML可以在多种编程语言之间进行解析和生成,因此在不同平台上都可以使用。
优势:
易于阅读和编写:YAML的语法结构清晰简洁,使用空格缩进来表示层级关系,使得文件易于阅读和编写。
支持复杂数据结构:YAML支持表示各种复杂的数据结构,包括对象、数组、嵌套结构等,这使得它非常灵活。
与编程语言交互性强:YAML可以很容易地被大多数编程语言解析和生成,因此可以与各种编程语言进行交互,方便数据的处理和传输。
适用于配置文件:由于YAML的人类可读性和易用性,它常常被用于编写配置文件,比如在软件开发中用于配置应用程序的参数和选项。
标记支持:YAML支持标记,这使得在文档中引用其他部分变得更容易,有利于数据的重用和管理。
总的来说,YAML在易读性、易写性和灵活性方面都具有很大的优势,使得它成为了许多开发人员和系统管理员首选的数据格式之一。
首先,我们先进行一些准备工作:在/root/redis目录下创建docker-compose.yml文件。
第一步,我们需要创建三个容器作为 Redis 的数据节点,其中一个是主节点,另外两个是从节点。我们可以使用 Docker Compose 来管理这些容器,并将它们的配置放在一个 YAML 文件中。首先,我们在这个 YAML 文件中定义了三个服务,分别代表主节点、从节点1和从节点2。每个服务都使用 Redis 官方镜像,并且指定了容器名称、端口映射以及 Redis 服务器的启动命令。主节点服务会监听默认的 Redis 端口 6379,而从节点则会连接到主节点并监听其他端口。通过这样的配置,我们可以确保数据节点的正确启动和连接。
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
这段代码是使用 Docker Compose 描述了一个包含三个 Redis 容器的服务群。
具体来说,定义了三个服务:
- master: 作为 Redis 主节点的服务。
- slave1 和 slave2: 作为 Redis 从节点的服务。
每个服务都使用 Redis 5.0.9 镜像,并设置容器的名称和重启策略为始终重启。
对于 master 服务:
- 使用 Redis 服务器命令 redis-server --appendonly yes 启动容器,开启了 AOF 持久化。
- 将容器的 6379 端口映射到主机的 6379 端口,以便外部访问 Redis 服务。
对于 slave1 和 slave2 服务:
- 使用 Redis 服务器命令 redis-server --appendonly yes --slaveof redis-master 6379 启动容器,将它们作为 master 服务的从节点,同时开启了 AOF 持久化。
- 将容器的 6379 端口映射到主机的 6380 和 6381 端口,分别对应 slave1 和 slave2 服务。
所以上面文件中完成了一个简单的 Redis 主从复制架构,其中一个主节点和两个从节点。
在Docker Compose的配置文件中,ports关键字用于指定容器的端口映射规则,将容器内部的端口映射到主机的对应端口上。每一项端口映射规则由两部分组成,用冒号分隔:
- 冒号前面的部分表示主机(Host)的端口。
- 冒号后面的部分表示容器(Container)的端口。
例如,"6379:6379"表示将主机的6379端口映射到容器的6379端口上。这样,在主机上通过6379端口访问时,数据将转发到容器内部的6379端口。
在给出的代码片段中:
- "6379:6379"表示容器master的6379端口映射到主机的6379端口。
- "6380:6379"表示容器slave1的6379端口映射到主机的6380端口。
- "6381:6379"表示容器slave2的6379端口映射到主机的6381端口。
这些配置使得可以通过主机上的不同端口来访问不同容器的Redis服务,使得它们在同一主机上能够同时运行而不会端口冲突。
我们再解释的具体一点:
在Docker中,每个容器都运行在独立的网络命名空间中,拥有自己的IP地址和端口。但是,在同一台物理主机上运行多个容器时,可能会遇到端口冲突的问题,因为每个容器都可能使用相同的默认端口。
为了解决这个问题,Docker提供了端口映射功能,允许将容器内部的端口映射到主机的不同端口上。在给定的配置中,通过指定不同的主机端口,可以避免不同容器之间的端口冲突,使得它们可以同时运行在同一主机上。
例如,在上述配置中,容器master的6379端口映射到主机的6379端口上,容器slave1的6379端口映射到主机的6380端口上,容器slave2的6379端口映射到主机的6381端口上。这样一来,即使三个容器的Redis服务都使用相同的内部端口6379,它们在主机上对应的端口不同,就避免了端口冲突的问题。
第二步,我们创建三个容器作为 Redis 的哨兵节点,这些节点负责监控和管理 Redis 数据节点的状态。同样地,我们可以使用 Docker Compose 来管理这些哨兵节点容器,并将它们的配置放在另一个 YAML 文件中。在这个 YAML 文件中,我们定义了三个服务,分别代表三个哨兵节点。每个服务都使用 Redis 官方镜像,并且指定了容器名称、端口映射以及 Redis 服务器的启动命令。哨兵节点会监听默认的 Sentinel 端口 26379,并且配置为启动 Sentinel 模式。通过这样的配置,我们可以确保哨兵节点的正确启动和监控 Redis 数据节点的状态。
(具体步骤在后面一点)
尽管我们可以将所有容器的配置放在一个 YAML 文件中,但分开管理数据节点和哨兵节点的配置有助于保持清晰和组织有序。如果我们同时启动了这六个容器,可能会导致哨兵节点先启动完成,而数据节点后启动完成的情况发生。这可能会导致哨兵节点误认为数据节点已经挂掉,尽管对于整体系统运行不会有太大影响,但这可能会干扰日志观察和调试过程。因此,通过分开配置文件,我们可以更好地控制容器的启动顺序和管理方式,确保系统的稳定和可靠性。
💡 可以在Linux系统中使用文本编辑器编辑yml文件,也可以在Windows系统上使用VSCode编辑好yml文件,然后上传到Linux系统上。
在yml文件中配置Redis主节点和从节点的相关参数,确保它们能够正确地相互访问。
注意:
在 Docker 中,我们可以使用容器名称作为 IP 地址来实现容器之间的相互访问。这是因为 Docker 在容器网络中为每个容器分配了唯一的名称和 IP 地址。通过容器名称,我们可以轻松地在不同容器之间建立通信,而无需手动管理 IP 地址或进行复杂的网络配置。
例如,如果我们有一个名为 "redis-master" 的容器作为主节点,另一个名为 "redis-slave1" 的容器作为从节点,我们可以在从节点中使用 "redis-master" 作为主节点的地址,而无需知道其实际的 IP 地址。这样做不仅简化了配置,还增强了容器之间的可移植性和灵活性。
通过容器名称进行访问的好处之一是,即使容器的 IP 地址发生变化(例如容器重新启动或重新创建),我们也无需修改配置,因为容器名称始终保持不变。这种机制使得容器之间的通信更加稳定和可靠。
启动所有容器:
在yml文件所在目录中使用终端命令cd命令切换到该目录。
使用docker-compose up(加上 -d 表示后台启动)命令启动所有容器,这会根据docker-compose.yml文件中的配置创建并启动Redis主节点和从节点的容器。
如果在启动过程中发现配置有误,可以使用docker-compose down命令停止并删除刚刚创建的容器,然后重新操作。
查看运行日志:
执行上述操作时,确保工作目录在yml文件的同级目录中,这样才能顺利进行工作。
可以使用docker-compose logs命令查看容器的运行日志,以便及时了解容器的运行情况和可能存在的问题。
验证:
连接主节点:使用redis-cli -p 6379命令连接Redis主节点,确保能够成功连接到主节点。
连接从节点:使用redis-cli -p 6380或redis-cli -p 6381命令连接Redis从节点,分别连接到不同的从节点端口,以验证从节点的可用性和正确性。
编排 redis-sentinel 节点
当将redis-sentinel放到与redis相同的yml文件中进行容器编排时,我们可以将它们分成两组,主要是为了以下两方面考虑:
-
观察日志方便:
- 将redis主从节点和redis-sentinel节点放在同一个yml文件中,可以使得日志信息更加集中,方便观察和管理。
- 在同一个yml文件中配置所有的容器,可以减少日志文件的分散性,便于对整个集群的运行状态进行监控和分析。
-
确保redis主从节点启动之后再启动redis-sentinel:
- 将redis主从节点和redis-sentinel节点分组,可以确保在启动redis-sentinel之前,redis主从节点已经启动并且稳定运行。
- 这样做可以避免启动redis-sentinel节点时可能触发的额外选举过程,从而避免可能出现的混乱情况。因为在某些情况下,如果先启动了redis-sentinel节点,它可能会触发一次新的选举过程,导致不必要的干扰和混乱。
综上所述,将redis-sentinel节点与redis主从节点放在同一个yml文件中进行容器编排,分成两组的目的是为了方便日志管理和确保启动顺序的合理性,从而确保整个集群的稳定运行和故障转移功能的正常实现。
1、编写docker-compose.yml文件:
首先,在/root/redis-sentinel目录下创建docker-compose.yml文件。
注意,在同一个目录中只能存在一个docker-compose.yml文件,因此要将redis-sentinel节点的配置单独写在一个yml文件中。
也可以在Windows系统上使用VSCode编辑好yml文件,然后上传到Linux系统上。
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
networks:
default:
external:
name: redis-data_default
这段配置文件使用Docker Compose来定义了三个Redis哨兵节点的部署:
- sentinel1服务定义了一个Redis哨兵节点,容器名称为redis-sentinel-1,使用Redis 5.0.9的官方镜像。它指定了在容器启动时始终重启,并且通过指定command参数运行了redis-sentinel命令,并将/etc/redis/sentinel.conf作为配置文件。同时,通过volumes指定了将本地的sentinel1.conf文件映射到容器内的/etc/redis/sentinel.conf路径,以提供配置文件给Redis哨兵。最后,通过ports指定了将容器内的26379端口映射到主机的26379端口上,使得可以通过主机上的26379端口访问该哨兵节点。
- sentinel2和sentinel3服务也类似地定义了Redis哨兵节点,分别对应容器名称redis-sentinel-2和redis-sentinel-3。它们使用相同的Redis镜像、相同的重启策略、相同的命令以及相同的配置文件。不同的是,它们分别映射了不同的本地配置文件和端口,分别为sentinel2.conf、sentinel3.conf和26380、26381端口。
- 最后的networks部分定义了默认网络,并指定了外部网络的名称为redis-data_default,这表明这些容器将连接到名为redis-data_default的外部网络。
在这个配置中,我们定义了三个 Redis 哨兵节点的容器。每个容器使用相同的 Redis 镜像版本(redis:5.0.9)。容器名称分别为 redis-sentinel-1、redis-sentinel-2 和 redis-sentinel-3。它们都设置为始终重启(restart: always)以保证持续运行。
每个哨兵节点容器使用命令 redis-sentinel /etc/redis/sentinel.conf 启动 Redis Sentinel 服务,并通过卷挂载(volumes)将主机上的 sentinel.conf 文件映射到容器内的 /etc/redis/sentinel.conf 路径,从而提供配置文件。这样做的目的是让哨兵节点可以在运行过程中自动修改配置文件。
为了使得容器内部的 26379 端口(Redis Sentinel 默认端口)可从主机外部访问,我们将它们映射到主机上的不同端口。sentinel1 映射到主机的 26379 端口,sentinel2 映射到 26380 端口,sentinel3 映射到 26381 端口。这样一来,即使是在同一主机上运行多个哨兵节点容器,它们的端口也不会重复,确保了端口的唯一性。
3、创建配置文件:
在/root/redis-sentinel目录中创建三个配置文件sentinel1.conf、sentinel2.conf、sentinel3.conf。这三个文件的内容是完全相同的。都放到 /root/redis-sentinel/ 目录中。
配置文件用于定义redis-sentinel节点的相关参数和配置信息,确保它们能够正确地监控redis主从节点并执行故障转移操作。
bind 0.0.0.0
port 26379
sentinel monitor redis-master redis-master 6379 2
sentinel down-after-milliseconds redis-master 1000
这段配置文件是用于配置 Redis 哨兵节点的参数。具体来说:
- bind 0.0.0.0:指定哨兵节点监听所有网络接口的请求。
- port 26379:指定哨兵节点监听的端口号。
- sentinel monitor redis-master redis-master 6379 2:配置哨兵节点要监视的 Redis 主节点信息。这里指定了要监视的主节点名字为 redis-master,它的 IP 地址和端口号分别为 redis-master 和 6379。这里的 redis-master 是IP地址,但是使用docker能对其自动进行域名解析。这里的端口号注意得按照容器外的端口号进行编写。这里的 2 是法定票数,表示在主节点不可用的情况下,至少有多少个哨兵节点达成一致才认为主节点不可用。
- sentinel down-after-milliseconds redis-master 1000:心跳包的超时时间,配置哨兵节点在多少毫秒之后认定主节点不可用。这里设置为 1000 毫秒,即 1 秒钟。
这些配置项使得 Redis 哨兵节点能够监视指定的主节点,并在主节点不可用时执行相应的故障转移操作。
通过以上步骤,可以完成对redis-sentinel节点的编排和配置,确保其能够正确地监控和管理redis主从节点,并在需要时执行故障转移操作。将redis-sentinel节点的配置单独写在一个yml文件中,可以更好地管理和观察日志,同时确保在redis主从节点启动之后再启动redis-sentinel节点,避免可能出现的选举混乱情况。
还记得我们刚刚在配置文件最后加上了:
networks:
default:
external:
name: redis-data_default
如果没有加的话,启动哨兵节点之后会显示这样的保存日志:
在这段错误信息中,出现了一个致命的配置文件错误。哨兵节点无法解析 redis-master 这个主机名,因为它不知道该主机名指向哪个容器。实际上,redis-master 在这里相当于一个域名,Docker 会尝试进行域名解析,但由于在默认情况下每个容器位于自己的默认网络中,哨兵节点无法解析到 redis-master 的 IP 地址。
解决这个问题的方法是将所有的容器放在同一个网络中,这样它们就可以相互解析对方的主机名。通过 Docker Compose,我们可以使用 networks 关键字来定义容器使用的网络。我们可以为所有的 Redis 节点和哨兵节点指定一个自定义的网络,让它们处于同一个局域网中,从而解决主机名解析的问题。
如果没有设置正确,就会这样显示:
所以我们配置好相关的文件,再重启即可。
通过将所有容器放在同一个网络中,我们可以确保它们之间能够相互通信,解决了哨兵节点无法解析主机名的问题。
理解 sentinel monitor
sentinel monitor 命令用于在Redis Sentinel中监控一个新的主节点,并将其添加到监控列表中。
sentinel monitor <主节点名> <主节点ip> <主节点端口> <法定票数>
sentinel monitor命令的各个参数:
-
主节点名:这个参数是哨兵内部自定义的名称,用于标识要监控的主节点。通常可以使用与主节点相关的任何名称,以便于在哨兵节点中进行识别和管理。
-
主节点IP:指定部署Redis主节点的设备IP地址。在使用docker部署时,也可以直接写Docker容器的名称,因为Docker会自动将容器名解析为相应的容器IP地址。
-
主节点端口:指定Redis主节点的端口号。这个参数不需要解释,是指Redis主节点所监听的端口号,用于与客户端和其他节点进行通信。
-
法定票数:
- 法定票数是用于判断主节点是否挂掉的一个重要参数。在哨兵节点中,可能会出现一些特殊情况,例如某个哨兵节点自身的网络出现问题,无法正常访问到主节点。为了避免误判,采用投票的方式来确定主节点是否真的挂了是更稳妥的做法。
- 法定票数指的是需要多少个哨兵节点都认为主节点挂掉,即票数大于等于法定票数,才会真正认定主节点已经挂掉。这样做可以增加系统的稳定性和可靠性,避免误判导致不必要的故障转移操作。
理解 sentinel down-after-milliseconds
sentinel down-after-milliseconds是Redis Sentinel配置中的一个参数,用于设置哨兵节点判断主节点或从节点已经下线的超时时间。具体来说,它表示主节点或从节点在多长时间内没有响应心跳包后被判定为下线。
理解这个参数的重点在于心跳包的概念。Redis Sentinel中的节点之间通过心跳包来进行通信和监控。每个节点都会定期向其他节点发送心跳包,以确认彼此的存活状态。如果某个节点在指定的时间内没有收到另一个节点的心跳包,则认为该节点可能出现了故障。
因此,当设置了sentinel down-after-milliseconds参数后,如果某个节点在设定的毫秒数内没有收到其他节点的心跳包,就会被判定为故障节点,从而触发哨兵节点的故障转移流程。
总的来说,sentinel down-after-milliseconds参数用于控制节点之间的监控超时时间,确保在节点出现故障时能够及时进行故障转移,从而保证整个系统的稳定运行。
你会不会有一个疑问:既然内容相同, 为啥要创建多份配置文件?
创建多份配置文件的主要目的是为了避免配置修改混乱的情况,确保在Redis Sentinel运行时能够对配置进行动态修改并维持一致性。
具体原因包括:
-
避免混乱的配置修改:如果只有一份配置文件,当Redis Sentinel运行时需要修改配置时,可能会出现多个哨兵节点同时修改同一份文件的情况,造成配置混乱或冲突。通过创建多份配置文件,每个哨兵节点都可以独立地修改自己的配置文件,避免了冲突和混乱。
-
保证配置一致性:多份配置文件虽然内容相同,但是每个文件是独立的,分别供每个哨兵节点使用。这样即使某个哨兵节点对配置进行了修改,也不会影响其他哨兵节点的配置。因此,可以保证配置的一致性,避免了不同节点之间的配置不同步或不一致的情况。
-
提高系统稳定性:通过使用多份配置文件,可以降低配置修改时的风险,减少配置混乱和不一致带来的系统故障风险。每个哨兵节点都可以独立地管理自己的配置文件,有助于提高系统的稳定性和可靠性。
所以,创建多份配置文件可以有效地避免配置修改混乱的情况,保证配置的一致性,并提高系统的稳定性和可靠性。
3、启动所有容器:
使用docker-compose命令启动所有容器。如果发现之前的配置有误,可以使用docker-compose down命令停止并删除刚刚创建的容器,然后重新进行操作。
4、查看运行日志:
在操作过程中,需要确保工作目录在yml文件的同级目录中。启动容器后,可以查看容器的运行日志,确认容器是否正常启动。
通过观察日志,可以发现哨兵节点已经通过主节点认识到了对应的从节点,表明集群中各个节点之间的通信已经建立。
5、观察redis-sentinel的配置修改:
再次打开哨兵的配置文件,可以发现文件内容已经被自动修改了。会出现# Generated by CONFIG REWRITE的标识,说明配置已经被Redis Sentinel进行了自动修改。
对比之前创建的三份配置文件,可以发现它们之间存在差异,说明Redis Sentinel已经根据运行时的情况进行了配置的修改。
重新选举
redis-master 宕机之后
手动把 redis-master 干掉:
手动终止运行 redis-master 实例,模拟主节点的宕机情况。这个操作旨在测试哨兵节点对主节点宕机的感知和响应能力。
观察哨兵的日志:
观察哨兵节点的日志,以便查看哨兵节点对主节点宕机的感知和处理过程。哨兵节点会定期检查主节点的健康状态,并在发现主节点宕机后记录日志,并触发故障转移流程。
在 Redis Sentinel 中,+sdown 和 +odown 是两种不同的事件标志,表示主服务器的状态发生变化:
-
+sdown:表示主服务器被标记为主观下线(subjectively down),即有一个或多个 Sentinel 实例认为主服务器不可达。这个事件通常由某个 Sentinel 实例发起。
-
+odown:表示主服务器被标记为客观下线(objectively down),即超过了 quorum 数量的 Sentinel 实例认为主服务器不可达。这个事件通常由 Sentinel 之间进行协商达成一致后发起。
在我们的日志中,首先出现了 +sdown 事件,表示有一个 Sentinel 将主服务器标记为主观下线,接着出现了 +odown 事件,表示多数派 Sentinel 将主服务器标记为客观下线,并且指定了 quorum 数量。
主观下线和客观下线是Redis Sentinel在判断主节点是否下线时采取的两种不同的标准。主观下线是单个哨兵节点根据自身的观察判断主节点已经不可用,而客观下线则是需要多个哨兵节点达成一致意见后才能认定主节点确实已经下线。
- 主观下线 (Subjectively Down, SDown)是指哨兵节点主观地感知到主节点没有心跳了,因此将其判定为主观下线。在这种情况下,单个哨兵节点会在没有收到主节点的心跳包时,单方面认为主节点已经不可用,因此它会将主节点标记为主观下线状态。
- 客观下线 (Objectively Down, ODown)则是指需要多个哨兵节点达成一致意见,才能认定主节点确实已经下线了。在这种情况下,需要多个哨兵节点在经过一定的协商和确认后,共同认为主节点已经失效,才会将主节点标记为客观下线状态。这种做法可以有效地避免因为单个哨兵节点的误判而引起的不必要的故障转移操作。
接下来,哨兵们挑选出了一个新的 master:
在观察日志过程中,可以看到哨兵节点发现主节点已经主观下线 (sdown),随后多个哨兵节点达成一致意见,判断主节点宕机 (odown),票数达到法定票数。哨兵节点之间进行协商,选择新的主节点,并进行故障转移操作。这个过程涉及到多个哨兵节点之间的通信和协作,确保选出的新主节点是最适合的。
这条日志表示主节点发生切换,新的主节点的地址由 172.18.0.2:6379 变更为 172.18.0.4:6379。
除了主节点切换的日志外,还存在两条日志,表示在主节点切换后,从节点的主从关系也发生了变化。在这两条日志中,从节点 172.18.0.3:6379 和 172.18.0.2:6379 都成为了新的主节点 172.18.0.4:6379 的从节点。
此时, 对于 Redis 来说仍然是可以正常使用的。
6381变成主节点:
redis-master 重启之后
手动把 redis-master 启动起来:
手动重新启动 redis-master 实例,模拟主节点的重启过程,测试了主节点的重启情况,以验证哨兵节点对于主节点重启后的重新接管情况。
观察哨兵日志:
再次观察哨兵节点的日志,查看哨兵节点对于主节点重启的感知和处理过程。
在主节点重启后,哨兵节点会重新评估主节点的健康状态,并重新分配主从角色,此过程能在日志中体现出来。
使用 redis-cli 也可以进⼀步的验证这一点:
使用 redis-cli 工具连接到 Redis 实例,并检查实例的角色,验证主节点是否已经正确恢复。通过使用 redis-cli 工具,可以直接查看 Redis 实例的角色信息,以验证主节点是否已经成功恢复,并确认哨兵节点对于主从角色的正确管理。
可以看到刚才新启动的 redis-master 被当成了 slave。
结论
- 当 Redis 主节点发生宕机时,哨兵将会从当前的从节点中选择一个新的主节点,并进行自动故障转移。这个过程保证了系统在主节点宕机的情况下仍然能够保持可用性。通过将从节点提升为主节点,哨兵确保了数据的持久性和一致性,并且尽可能地减少了服务中断的时间。这种自动故障转移的机制对于维护 Redis 集群的稳定性和可靠性至关重要。
- 而当之前的 Redis 主节点重新启动后,哨兵会重新监控它,但是它只会被视为从节点而不是主节点。这样做的目的是确保重新启动的主节点不会对集群造成不必要的影响。通过作为从节点,重新启动的主节点将接受来自其他主节点的同步数据,并在需要时提供读取服务,同时避免了对于主节点角色的冲突和不一致性。这种策略维护了 Redis 集群的一致性和稳定性,确保了集群的正常运行。
选举原理
如果仔细观察日志,我们会发现第一个哨兵节点打印的日志很多很多,远远超过了其他两个哨兵,而且还包含一些特殊的语句:
假设当前环境如上所述,包括三个哨兵节点(sentinel1、sentinel2、sentinel3)、一个主节点(redis-master)以及两个从节点(redis-slave1、redis-slave2)。在这样的配置下,如果主节点发生故障,将会触发一系列的自动故障转移过程,以确保系统的可用性和稳定性。
首先,当主节点宕机时,哨兵节点会感知到主节点的故障,其中的一个哨兵节点可能会首先发现主观下线(SDown),并将这一情况通知给其他哨兵节点。其他哨兵节点随后也会检测到主节点的失效,并开始达成一致意见,确认主节点的客观下线(ODown)。这个过程可能会涉及到哨兵节点之间的通信和协商,以确定主节点是否真正宕机。
一旦多数哨兵节点达成共识,确认主节点宕机后,它们会开始选举出一个新的主节点。这个过程通常是由哨兵节点之间进行协商和投票来完成的,以确保选出的新主节点是最适合的。一旦新的主节点被选举出来,哨兵节点会协调将其他从节点切换到新的主节点,并重新建立主从关系。
综上,在主节点发生故障时,哨兵节点会自动协调并执行一系列的故障转移过程,以确保整个系统的稳定运行。这种自动化的故障处理机制大大提高了系统的可用性和可靠性,减少了人工干预的需要,从而提升了系统的运维效率。
主观下线
当 redis-master 宕机时,这意味着 redis-master 和三个哨兵之间的心跳包无法正常传递和响应。从三个哨兵的角度来看,这种情况表示主节点出现了严重的故障。在哨兵监控系统中,每个哨兵都独立地监测主节点的状态,并根据自己的观察结果对主节点进行判断。
由于无法与主节点进行通信,每个哨兵都会独立地将 redis-master 判定为主观下线(Subjectively Down,简称 SDown)。主观下线意味着哨兵节点认为主节点已经失效,并且在内部记录下这一状态。这个过程不需要其他哨兵节点的参与,每个哨兵独立做出决定。
通过主观下线状态,哨兵节点可以快速响应主节点的故障,立即采取相应的措施来保证 Redis 集群的可用性。随后的故障转移过程将由哨兵节点协调完成,确保从节点能够晋升为新的主节点,并在集群中接管主节点的角色。这种自动化的故障转移机制有助于确保 Redis 集群在主节点故障时仍能够保持高可用性和稳定性。
客观下线
在发生主节点故障的情况下,每个哨兵节点(例如 sentinel1、sentinel2、sentinel3)都会参与对主节点故障的投票过程。这个过程是哨兵集群中非常关键的一部分,它确保了对主节点状态的准确监控和故障转移的触发。
首先,当哨兵节点检测到主节点故障时,它会将主节点标记为主观下线(SDown)。这意味着哨兵节点个体认为主节点已经失效,并在内部记录下这一状态。每个哨兵节点都独立地监测主节点的状态,并根据自己的观察结果对主节点进行判断。
随后,哨兵节点将开始对主节点的故障进行投票。每个哨兵都有一个投票权,它们通过投票来达成对主节点故障的共识。在投票过程中,每个哨兵的投票都具有相同的权重,没有任何一个哨兵节点拥有主导权。
在配置文件中,我们可以设置一个参数来定义投票通过的法定票数。例如,在 sentinel monitor redis-master 172.22.0.4 6379 2 这行配置中,2 就是法定票数。这意味着在投票中,如果有至少两个哨兵节点(达到法定票数)投票认定主节点故障,那么主节点就会被判定为客观下线(ODown)。
客观下线表示主节点被系统确认为已经失效,不再可用。此时,哨兵节点将触发故障转移流程,选举出一个新的主节点,并协调其他从节点同步到新的主节点上,以确保集群的正常运行。这种自动化的故障转移机制是 Redis Sentinel 实现高可用性的关键组成部分之一。
选举出哨兵的 leader
在主节点被客观下线后,哨兵集群中会进入故障转移的阶段。在这个阶段,哨兵需要从剩余的从节点(slaves)中选举出一个新的主节点(master)。然而,并不是所有的哨兵都需要参与此过程。相反,哨兵集群中会选出一个代表,通常称为领导者(leader),由领导者负责主节点的选举和故障转移的进行。
1号哨兵推举自己成为leader:
紧接着,后两个哨兵也跟着投了1号哨兵:
哨兵集群中的领导者是通过一种分布式算法来选举产生的,通常使用的是 Raft 算法或者类似的共识算法。在选举过程中,每个哨兵节点都会向其他哨兵节点发送选票,并根据特定的规则进行投票和统计。
一旦选举出领导者,领导者将负责决定哪个从节点将升级为新的主节点。领导者会向其他哨兵节点发送指令,告知选中的从节点成为新的主节点,并指导其他从节点进行同步。这个过程确保了故障转移的顺利进行,并且只有一个哨兵负责领导和协调整个过程,避免了冲突和混乱的发生。
我们现在选举的过程涉及到 Raft 算法:
Raft 算法是一种被广泛应用于分布式系统中的共识算法,用于确保系统中的多个节点能够达成一致的决策。在 Redis 哨兵集群中,Raft 算法通常用于选举领导者(leader),以协调故障转移过程。
假设我们有三个哨兵节点:S1、S2 和 S3。在故障转移过程中,哨兵节点之间会相互发起选举,以下是大致的选举流程:
- 拉票请求发起:每个哨兵节点都会向其他所有哨兵节点发起拉票请求,表达自己的候选人意愿。例如,哨兵节点 A(S1)会给哨兵节点 B(S2)和哨兵节点 C(S3)发送拉票请求,哨兵节点 B(S2)会给哨兵节点 A(S1)和哨兵节点 C(S3)发送拉票请求,以此类推。
- 投票响应:
- 收到拉票请求的哨兵节点会根据自身的情况作出投票决定,并回复投票响应。
- 每个哨兵节点只有一票,并且只能投给一个候选人。如果收到的是第一次拉票请求,那么响应方将投票给拉票方。如果已经给其他候选人投过票,那么就不再投票。
- 选举结果确认:在一轮投票结束后,如果有一个候选人收到了超过半数的选票,那么它将自动成为领导者。例如,如果 S1 和 S2 都投给了 S3,那么 S3 将成为领导者。如果没有候选人获得半数以上的选票,则会重新进行选举。
- 领导者选举完成:
- 新选出的领导者负责挑选一个从节点升级为新的主节点。例如,如果 S3 成为了领导者,它将选择一个从节点升级为新的主节点。
- 其他哨兵节点会观察到新的主节点的出现,从而确认选举过程已经结束
总的来说,Raft 算法的核心理念是“先下手为强”,即率先发起拉票请求的候选人更有可能成为领导者。选举的结果可能受到网络延迟的影响,因此选举过程中会有一定的随机性。在 Redis 哨兵集群中,确保哨兵节点数量为奇数有助于降低出现平票的可能性,从而减少额外的选举开销。这一过程确保了集群在主节点故障时能够快速有效地进行故障转移。
leader 挑选出合适的 slave 成为新的 master
挑选规则:
-
比较优先级: 在选举新的主节点时,首先会比较每个从节点的优先级。这个优先级是在配置文件中通过配置项(如 slave-priority 或 replica-priority)指定的。优先级较高(数值较小)的从节点将被优先提升为新的主节点。例如,如果有两个从节点 A 和 B,其中 A 的优先级更高,则 A 将优先被选为新的主节点。
-
比较复制偏移量: 如果有多个优先级相同的从节点,则会比较它们的复制偏移量。复制偏移量表示从节点已经复制的数据量,通常越大表示复制得越多。具有更高复制偏移量的从节点将被优先提升为新的主节点。这样可以确保新的主节点具有最新的数据。例如,如果有两个从节点 A 和 B,它们的优先级相同,但 A 的复制偏移量更大,则 A 将被优先选择为新的主节点。
-
比较运行ID: 如果有多个优先级和复制偏移量相同的从节点,则会比较它们的运行ID。运行ID是一个唯一标识符,通常是由 Redis 在启动时生成的。具有较小运行ID的从节点将被优先提升为新的主节点。例如,如果有两个从节点 A 和 B,它们的优先级和复制偏移量都相同,但 A 的运行ID更小,则 A 将被优先选择为新的主节点。
选举完成后,新的主节点需要执行以下操作:
- 执行从节点断开操作(slave no one): 领导者会指定选定的从节点执行从节点断开操作,使其成为新的主节点。这个操作会导致选定的从节点停止复制其他从节点,并开始接受客户端的写入操作。
- 指定其他从节点依附于新主节点(slave of ): 领导者会指定其他剩余的从节点依附于这个新的主节点,以确保整个集群的一致性。这样,新的主节点将成为集群的中心,并负责处理客户端的读写请求。
一些注意事项:
• 哨兵节点数量: 哨兵节点不能只有一个,因为如果唯一的哨兵节点挂了,将会影响系统的可用性。建议至少使用三个哨兵节点,以确保高可用性。
• 奇数个哨兵节点: 最好将哨兵节点配置为奇数个。这样做有利于选举领导者,因为得票超过半数的概率更高,从而降低了出现平票的可能性。
• 哨兵节点的角色: 哨兵节点不负责存储数据,它们的主要作用是监控和管理 Redis 主从节点。实际的数据存储仍然由 Redis 主从节点负责。
• 提高可用性: 哨兵节点和主从复制结合解决了提高系统的可用性的问题,但无法解决数据丢失的问题。在极端情况下,可能会发生写入数据丢失的情况,因此需要结合其他机制来确保数据的持久性和一致性。
• 存储容量限制: 哨兵节点和主从复制不能提高数据的存储容量。当需要存储的数据接近或超过服务器的物理内存时,单纯依靠这种结构将难以满足需求。为了扩展存储容量,需要引入集群架构,如 Redis Cluster,以实现数据的分片和分布式存储。