非集群 Redis 的高可用性
Redis Sentinel 在不使用 Redis Cluster
时为 Redis 提供高可用性。
Redis Sentinel 还提供其他附带任务,例如监控、通知并充当客户端的配置提供程序。
这是宏观层面的 Sentinel 能力的完整列表(即大图):
监测。Sentinel 会不断检查您的主实例和副本实例是否按预期工作。
通知。Sentinel 可以通过 API 通知系统管理员或其他计算机程序,其中一个受监控的 Redis 实例出现问题。
自动故障转移。如果 master 没有按预期工作,Sentinel 可以启动一个故障转移过程,其中一个副本被提升为 master,其他额外的副本被重新配置为使用新的 master,并且使用 Redis 服务器的应用程序被告知要使用的新地址连接时。
配置提供者。Sentinel 充当客户端服务发现的权威来源:客户端连接到 Sentinel 以请求负责给定服务的当前 Redis 主服务器的地址。如果发生故障转移,Sentinels 将报告新地址。
Sentinel 作为分布式系统
Redis Sentinel 是一个分布式系统:
Sentinel 本身设计为在有多个 Sentinel 进程一起协作的配置中运行。多个 Sentinel 进程协作的优点如下:
当多个 Sentinel 同意给定主节点不再可用的事实时,将执行故障检测。这降低了误报的概率。
即使并非所有 Sentinel 进程都在工作,Sentinel 也能正常工作,从而使系统能够抵御故障。毕竟,拥有一个本身就是单点故障的故障转移系统没有任何乐趣。
Sentinel、Redis 实例(master 和 replica)以及连接到 Sentinel 和 Redis 的客户端的总和,也是一个具有特定属性的更大的分布式系统。在本文档中,将逐步介绍概念,从了解 Sentinel 基本属性所需的基本信息开始,到更复杂的信息(可选),以便了解 Sentinel 的工作原理。
哨兵快速入门
获得哨兵
当前版本的 Sentinel 称为Sentinel 2。它是使用更强大且更易于预测的算法(在本文档中解释)对初始 Sentinel 实现的重写。
Redis Sentinel 的稳定版本从 Redis 2.8 开始发布。
新的开发是在不稳定的分支中进行的,有时一旦新特性被认为是稳定的,它们就会被反向移植到最新的稳定分支中。
随 Redis 2.6 提供的 Redis Sentinel 版本 1 已弃用,不应使用。
运行哨兵
如果您正在使用redis-sentinel
可执行文件(或者如果您有一个带有该名称的符号链接到redis-server
可执行文件),您可以使用以下命令行运行 Sentinel:
redis-sentinel /path/to/sentinel.conf
否则,您可以直接使用redis-server
在 Sentinel 模式下启动它的可执行文件:
redis-server /path/to/sentinel.conf --sentinel
两种方式的工作方式相同。
但是,在运行 Sentinel 时必须使用配置文件,因为系统将使用该文件来保存当前状态,以便在重新启动时重新加载。如果没有给出配置文件或配置文件路径不可写,Sentinel 将简单地拒绝启动。
默认情况下,Sentinel 会侦听 TCP 端口 26379 的连接,因此要使 Sentinel 工作,必须打开服务器的端口 26379才能接收来自其他 Sentinel 实例的 IP 地址的连接。否则 Sentinel 无法交谈,也无法就该做什么达成一致,因此永远不会执行故障转移。
部署前需要了解的有关 Sentinel 的基本知识
您需要至少三个 Sentinel 实例才能进行稳健的部署。
三个 Sentinel 实例应放置在被认为以独立方式发生故障的计算机或虚拟机中。例如,不同的物理服务器或虚拟机在不同的可用区域上执行。
Sentinel + Redis 分布式系统不保证在故障期间保留已确认的写入,因为 Redis 使用异步复制。然而,有一些方法可以部署 Sentinel,使丢失写入的窗口仅限于某些时刻,而还有其他不太安全的方法来部署它。
您的客户需要 Sentinel 支持。流行的客户端库支持 Sentinel,但不是全部。
如果您不时不时在开发环境中进行测试,那么没有任何 HA 设置是安全的,或者如果可以的话,在生产环境中如果它们可以工作则更好。您可能有一个错误配置,只有在为时已晚(凌晨 3 点,您的 master 停止工作时)才会变得明显。
Sentinel、Docker 或其他形式的网络地址转换或端口映射应小心混合:Docker 执行端口重新映射,破坏其他 Sentinel 进程的 Sentinel 自动发现和主服务器的副本列表。查看本文档后面 关于*Sentinel 和 Docker的部分以获取更多信息。*
配置哨兵
Redis 源代码分发包含一个名为的文件,该文件sentinel.conf
是一个自记录的示例配置文件,可用于配置 Sentinel,但典型的最小配置文件如下所示:
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5
您只需要指定要监控的主服务器,为每个分离的主服务器(可能有任意数量的副本)赋予不同的名称。无需指定自动发现的副本。Sentinel 将使用有关副本的附加信息自动更新配置(以便在重新启动时保留这些信息)。每次在故障转移期间将副本提升为主副本以及每次发现新的 Sentinel 时,也会重写配置。
上面的示例配置基本上监控了两组 Redis 实例,每组由一个主实例和未定义数量的副本组成。一组实例被调用mymaster
,另一个被调用resque
。
sentinel monitor
语句的参数的含义如下:
sentinel monitor <master-group-name> <ip> <port> <quorum>
为了清楚起见,让我们逐行检查配置选项的含义:
第一行用于告诉 Redis 监控一个名为mymaster的 master ,它位于地址 127.0.0.1 和端口 6379,法定人数为 2。除了quorum参数之外,一切都很明显:
因此,例如,如果您有 5 个 Sentinel 进程,并且给定主进程的法定人数设置为 2,则会发生以下情况:
实际上,这意味着在故障期间,如果大多数 Sentinel 进程无法通信(即少数分区中没有故障转移),则 Sentinel 永远不会启动故障转移。
其他哨兵选项
其他选项几乎总是采用以下形式:
sentinel <option_name> <master_name> <option_value>
并用于以下目的:
down-after-milliseconds
是以毫秒为单位的时间,Sentinel 开始认为它已关闭,因此实例不应到达(不回复我们的 PING 或回复错误)。
parallel-syncs
设置在故障转移后可以重新配置以同时使用新主服务器的副本数。数字越小,完成故障转移过程所需的时间就越长,但是如果将副本配置为服务旧数据,您可能不希望所有副本同时与主服务器重新同步。虽然复制过程对于副本来说大部分是非阻塞的,但有时它会停止从主服务器加载批量数据。您可能希望通过将此选项设置为值 1 来确保一次只能访问一个副本。
其他选项在本文档的其余部分进行了描述,并记录在sentinel.conf
Redis 发行版附带的示例文件中。
配置参数可以在运行时修改:
有关更多信息,请参阅 *在运行时重新配置 Sentinel*部分
。
示例 Sentinel 部署
现在您已经了解了 Sentinel 的基本信息,您可能想知道应该将 Sentinel 进程放置在哪里,需要多少个 Sentinel 进程等等。本节展示了一些示例部署。
我们使用 ASCII 艺术来以图形 格式向您展示配置示例,这就是不同符号的含义:
+--------------------+
| This is a computer |
| or VM that fails |
| independently. We |
| call it a "box" |
+--------------------+
我们在框内写下它们正在运行的内容:
+-------------------+
| Redis master M1 |
| Redis Sentinel S1 |
+-------------------+
不同的盒子用线连接,表明它们可以说话:
+-------------+ +-------------+
| Sentinel S1 |---------------| Sentinel S2 |
+-------------+ +-------------+
网络分区显示为使用斜线的中断行:
+-------------+ +-------------+
| Sentinel S1 |------ // ------| Sentinel S2 |
+-------------+ +-------------+
另请注意:
请注意,我们永远不会显示仅使用两个 Sentinel 的设置,因为 Sentinel 总是需要与大多数人交谈才能开始故障转移。
示例 1:只有两个哨兵,不要这样做
+----+ +----+
| M1 |---------| R1 |
| S1 | | S2 |
+----+ +----+
Configuration: quorum = 1
请注意,需要多数才能订购不同的故障转移,然后将最新配置传播到所有 Sentinel。另请注意,在没有任何协议的情况下,在上述设置的单侧进行故障转移的能力将非常危险:
+----+ +------+
| M1 |----//-----| [M1] |
| S1 | | S2 |
+----+ +------+
在上述配置中,我们以完全对称的方式创建了两个主节点(假设 S2 可以在未经授权的情况下进行故障转移)。客户端可能会无限期地向双方写入,并且无法了解分区何时修复哪种配置是正确的,以防止永久的脑裂状况。
所以请始终在三个不同的盒子中部署至少三个哨兵。
示例 2:三个盒子的基本设置
这是一个非常简单的设置,其优点是易于调整以提高安全性。它基于三个盒子,每个盒子都运行一个 Redis 进程和一个 Sentinel 进程。
+----+
| M1 |
| S1 |
+----+
|
+----+ | +----+
| R2 |----+----| R3 |
| S2 | | S3 |
+----+ +----+
Configuration: quorum = 2
如果主 M1 发生故障,S2 和 S3 将就故障达成一致,并能够授权故障转移,使客户端能够继续。
在每个 Sentinel 设置中,由于 Redis 使用异步复制,因此总是存在丢失一些写入的风险,因为给定的确认写入可能无法到达被提升为 master 的副本。然而,在上述设置中,由于客户端被旧主服务器分区,风险更高,如下图所示:
+----+
| M1 |
| S1 | <- C1 (writes will be lost)
+----+
|
/
/
+------+ | +----+
| [M2] |----+----| R3 |
| S2 | | S3 |
+------+ +----+
在这种情况下,网络分区隔离了旧的主 M1,因此副本 R2 被提升为主。但是,与旧主控位于同一分区中的客户端(如 C1)可能会继续向旧主控写入数据。这些数据将永远丢失,因为当分区恢复时,master 将被重新配置为新 master 的副本,丢弃其数据集。
可以使用以下 Redis 复制功能缓解此问题,如果 master 检测到它不再能够将其写入传输到指定数量的副本,则允许停止接受写入。
min-replicas-to-write 1
min-replicas-max-lag 10
使用上述配置(有关更多信息,请参阅 Redis 发行版中的自我注释redis.conf
示例),Redis 实例在充当主服务器时,如果无法写入至少 1 个副本,它将停止接受写入。由于复制是异步的,因此无法写入实际上意味着副本要么断开连接,要么在超过指定max-lag
秒数的时间内没有向我们发送异步确认。
使用此配置,上例中的旧 Redis master M1 将在 10 秒后变得不可用。当分区恢复时,Sentinel 配置将收敛到新的配置,客户端 C1 将能够获取有效配置并继续使用新的 master。
然而,没有免费的午餐。有了这个细化,如果两个副本都关闭了,master 将停止接受写入。这是一个权衡。
示例 3:客户端框中的 Sentinel
有时我们只有两个可用的 Redis 盒子,一个用于主服务器,一个用于副本。示例 2 中的配置在这种情况下不可行,因此我们可以采用以下方法,将 Sentinel 放置在客户端所在的位置:
+----+ +----+
| M1 |----+----| R1 |
| | | | |
+----+ | +----+
|
+------------+------------+
| | |
| | |
+----+ +----+ +----+
| C1 | | C2 | | C3 |
| S1 | | S2 | | S3 |
+----+ +----+ +----+
Configuration: quorum = 2
在此设置中,Sentinels 的观点与客户端相同:如果大多数客户端都可以访问主服务器,那很好。这里的 C1、C2、C3 是通用客户端,并不代表 C1 标识连接到 Redis 的单个客户端。它更像是应用服务器、Rails 应用程序或类似的东西。
如果运行 M1 和 S1 的机器发生故障,故障转移将毫无问题地发生,但是很容易看出不同的网络分区会导致不同的行为。例如,如果客户端和 Redis 服务器之间的网络断开连接,Sentinel 将无法设置,因为 Redis 主服务器和副本都将不可用。
请注意,如果 C3 被 M1 分区(在上面描述的网络中几乎不可能,但更可能在不同的布局下,或者由于软件层的故障),我们会遇到与示例 2 中描述的类似的问题,不同之处在于这里我们没有办法打破对称性,因为只有一个副本和一个master,所以master在与它的副本断开连接时不能停止接受查询,否则在副本故障期间master将永远不可用。
所以这是一个有效的设置,但示例 2 中的设置具有优势,例如 Redis 的 HA 系统与 Redis 本身在相同的盒子中运行,这可能更易于管理,并且能够限制时间量少数分区的master可以接收写入。
示例 4:少于三个客户端的 Sentinel 客户端
如果客户端中的盒子少于三个(例如三个 Web 服务器),则无法使用示例 3 中描述的设置。在这种情况下,我们需要采用如下混合设置:
+----+ +----+
| M1 |----+----| R1 |
| S1 | | | S2 |
+----+ | +----+
|
+------+-----+
| |
| |
+----+ +----+
| C1 | | C2 |
| S3 | | S4 |
+----+ +----+
Configuration: quorum = 3
这与示例 3 中的设置类似,但这里我们在可用的四个框中运行四个 Sentinel。如果主 M1 变得不可用,其他三个 Sentinel 将执行故障转移。
从理论上讲,这种设置可以移除运行 C2 和 S4 的盒子,并将仲裁设置为 2。但是,如果我们的应用层没有高可用性,我们不太可能希望 Redis 端的 HA。
Sentinel、Docker、NAT 和可能的问题
Docker 使用一种称为端口映射的技术:在 Docker 容器中运行的程序可能会使用与程序认为正在使用的端口不同的端口公开。这对于在同一服务器上同时使用相同端口运行多个容器很有用。
Docker 不是唯一发生这种情况的软件系统,还有其他网络地址转换设置可以重新映射端口,有时不是端口而是 IP 地址。
重新映射端口和地址会以两种方式与 Sentinel 产生问题:
Sentinel 对其他 Sentinel 的自动发现不再起作用,因为它基于hello消息,其中每个 Sentinel 宣布他们正在侦听连接的端口和 IP 地址。但是,Sentinel 无法了解地址或端口是否被重新映射,因此它会宣布其他 Sentinel 连接的信息不正确。
Redis master的输出中以类似的方式列出副本 INFO
:该地址是由 master 检查 TCP 连接的远程对等方检测到的,而端口是由副本自己在握手期间通告的,但是端口可能是错误的出于与第 1 点相同的原因。
由于 Sentinel 使用 master INFO
输出信息自动检测副本,因此检测到的副本将无法访问,并且 Sentinel 永远无法对 master 进行故障转移,因为从系统的角度来看没有好的副本,所以目前没有办法使用 Sentinel 监控一组使用 Docker 部署的主实例和副本实例,除非您指示 Docker 映射端口 1:1。
对于第一个问题,如果您想使用 Docker 和转发端口(或任何其他重新映射端口的 NAT 设置)运行一组 Sentinel 实例,您可以使用以下两个 Sentinel 配置指令来强制 Sentinel 宣布一个特定的 IP 和端口集:
sentinel announce-ip <ip>
sentinel announce-port <port>
请注意,Docker 能够在主机网络模式下运行(查看--net=host
选项以获取更多信息)。这应该不会产生任何问题,因为在此设置中未重新映射端口。