Redis3官方文档(15)
——高可用(上)
——高可用(上)
Redis Sentinel为Redis提供高可用解决方案。实际上这意味着,使用Sentinel你可以创建无需人工干预就可以抵御一定的故障的Redis部署结构。
Redis Sentinel还提供其他的附属功能,如监控,通知,以及作为客户端的配置提供者。
下面是在宏观层面上的Sentinel的能力完整列表:
- 监控(Monitoring)。Sentinel不断检查你的主从实例是否运转正常。
- 通知(Notification)。Sentinel可以通过API来通知系统管理员,或者其他计算机程序,被监控的Redis实例出了问题。
- 自动故障转移(Automatic failover)。如果一台主服务器运行不正常,Sentinel会开始一个故障转移过程,将从服务器提升为主服务器,配置其他的从服务器使用新的主服务器,使用Redis服务器的应用程序在连接时会收到新的服务器地址通知。
- 配置提供者(Configuration provider)。Sentinel充当客户端服务发现的权威来源:客户端连接到Sentinel来询问某个服务的当前Redis主服务器的地址。当故障转移发生时,Sentinel会报告新地址。
分布式特性(Distributed nature)
Redis Sentinel是一个分布式系统:
Sentinel本身被设计成运行于配置成多Sentinel进程相互协作。多个Sentinel进程相互协作的优势是:
- 当大多数Sentinel认为指定的主服务器不再可用这个事实时才进行故障检测。这降低了误判的可能性。
- Sentinel可以在即使不是所有Sentienl进程可用的情况下运转,使得系统对于故障更健壮。毕竟故障转移系统自身存在单点故障也没什么意思。
Sentinel实例,Redis实例(主服务器和从服务器),以及连接到Snetinel和Redis的客户端的总和,也是一个有特定特点的大型分布式系统。这篇文档我们将循序渐进地介绍各种概念,从理解Sentinel的基本属性所需的基本信息,到理解Sentinel如何运转的更复杂信息。
1快速开始(Quick Start)
获取Sentinel(Obtaining Sentinel)
当前版本的Sentinel被称为Sentinel 2。使用了更强大和简单的算法来重写最初的Sentinel实现(本文后面会解释)。
稳定版本的Redis Sentinel被打包在Redis 2.8和Redis 3.0中,这是最新的两个Redis稳定版本。
新的开发在不稳定的分支中进行,新的特性一旦稳定了就会合并回2.8和3.0分支。
Redis 2.6自带的Sentinel 1版本,已经被弃用,不应该再使用。
运行Sentinel(Running Sentinel)
如果你使用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间就没法通信,没法协调,也不会执行故障转移。
部署前须知(Fundamentals before to deploy)
- 你需要至少三个Sentinel实例来达到健壮的部署结构。
- 三个Sentinel实例需要部署在彼此独立的故障发生方式的主机或者虚拟机上面。例如,不同的物理服务器或虚拟主机运行在不同的可用区域(zone)。
- Sentinel+Redis组成的分布式系统不保证已被确认的写操作在故障期间被保存,因为Redis使用的是异步复制。但是,有一些部署Sentinel的方式可以使得丢失操作的时间窗口限定到某个值,也有一些不那么安全的部署方式。
- 你需要客户端支持Sentinel。流行的客户端库都提供Sentinel支持,但不是全部。
- 如果你不在开发环境,可以的话最好在生产环境中不断地进行测试,是不存在安全的高可用部署的。不然错误的配置让你后悔都来不及了(凌晨三点主服务器停止工作了)。
- Sentinel,Docker,或者其他形式的网络地址转换(Network Address Translation)或端口映射(Port Mapping)的各种混合场景要引起注意:Docker执行端口重映射,破坏了Sentinel对其他Sentinel进程和主服务器的从服务器列表的自动发现。查看后文关于Sentinel和Docker的章节获取更多信息。
配置Sentinel(Configuring Sentinel)
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
为了说得再清楚一点,我们一行一行地来看看这些配置选项是什么意思:
第一行告诉Redis监控一个叫做mymaster的主服务器,地址为127.0.0.1,端口为6379,仲裁人数为2。除了参数quorum,一切都显而易见:
- quorum是判定主服务器不可达所需要的Sentinel的数量,以标记服务器真正出现故障,从而最终启动一次故障转移的过程。
- quorum仅用于检测故障。要真正执行一次故障转移,必须要选举和授权其中一个Sentinel作为故障转移的领导者。这只会发生在获得大多数Sentienl进程的选票时。
- 例如,如果你有5个Sentinel进程,某台主服务器的仲裁人数设置为2,那么:
- 如果有2个Sentinel同时认为主服务器不可达,那么其中一个Sentinel就会试图触发一次故障转移。
- 如果有至少3个Sentinel可达(reachable),故障转移将会被授权,并且真正启动。
实际上也就是说,如果大多数Sentinel进程不能相互对话,Sentinel是不会启动故障转移的(也就是说故障转移不会发生在少数派分割中)。
其他选项(Other Sentinel options)
其他的选项基本上都是这样的形式:
sentinel
它们的作用如下:
down-after-milliseconds表示实例不可达(没有响应我们的PING,或者响应一个错误)以至于Sentinel认为其下线(down)的毫秒数。
parallel-syncs设置在一次故障转移之后,被配置为同时使用新主服务器的从服务器数量。这个数字越小,完成故障转移过程需要的时间就越多,如果从服务器配置为服务旧数据,你可能不太希望所有的从服务器同时从新的主服务器重同步,尽管复制过程通常不会阻塞从服务器,但是在重同步过程中仍然会有一段停下来的时间来加载来自于主服务器的大量数据。设置这个选项的值为1可以确保每次只有一个从服务器不可用。
其他的选项将在本文的剩余篇幅里介绍,Redis发行版本中自带的示例sentinel.conf文件中也有详细的文档。
所有的配置参数可以在运行时用SENTINEL SET命令修改。请看下文中运行时重新配置Sentinel这一部分获取更多的信息。
样例部署 (Example Sentinel deployments)
现在你熟悉了Sentinel的基本信息,你可能想知道你应该把你的Sentinel进程部署在哪里,你需要多少个Sentinel进程等等。这一部分我们展示几个样例部署。
我们使用图形格式中的ASCII字符形式向你展示配置样例,不同的符号含义如下:
我们将运行的主体写在格子里面:
不同的格子被直线连接,以展示其可以相互通话:
网络分割(network partition)以被斜线中断的直线表示:
还要注意:
- 主服务器(masters)被称为M1,M2,M3,。。。,Mn。
- 从服务器(slaves)被称为R1,R2,R3,。。。,Rn(R代表replica)。
- Sentinels被称为S1,S2,S3,。。。,Sn。
- 客户端被称为C1,C2,C3,。。。,Cn。
- 当实例由于Sentinel的操作变更角色时,我们将其放入方括号中,所以[M1]表示实例当前由于Sentinel的介入成为主服务器。
注意,我们不会展示只使用两个Sentinel的设置,因为Sentinel总是需要和大多数通话来启动一次故障转移。
例1:just two Sentinels, DON'T DO THIS
- 在这个设置中,如果主服务器M1故障,R1将被提升为主服务器,因为两个Sentinel可以就故障达成一致(显然由于quorum设置为1),并且也可以授权进行故障转移,因为大多数是2。显而易见这看起来能工作,但是瞅瞅下一点来看看为什么这样的设置不靠谱。
- 如果运行M1的格子停止工作,那么S1也会停止工作。运行在另一个格子里面的S2将不能授权一次故障转移,于是系统变得不可用。
注意,需要大多数(majority)来决定不同的故障转移的秩序,然后传播最新的配置给所有的Sentinel。也要注意,在上面配置中的单边的故障转移能力,如果没有达成一致将非常危险:
上面的配置中我们以完全对称的方式创建了两个主服务器(假设S2可以在没有授权的情况下进行故障转移)。客户端可能不确定的写入到两边,当分割恢复时没有办法知道哪个配置是正确的以防止永久的脑裂条件(permanent split brain condition)。
所以,请务必部署至少3个Sentinel实例到3个不同的格子中。
例2:basic setup with three boxes
这是一个非常简单的设置,可以非常方便简单的调整额外的安全性。这基于3个格子,每一个格子都运行一个Redis进程和一个Sentinel进程。
如果主服务器M1故障了,S2和S3将会就故障判断达成一致并授权进行故障转移,使得客户端可以继续访问。
在每一个Sentinel的设置中,由于Redis的异步复制,总是有丢失写的风险,因为一个应答的(acknowledged)写操作可能无法到达一个被提升为主服务器的从服务器。在上面的设置中,如果客户端和老的主服务器被分割在一起,则还有一个更高的风险,像下面这样:
在这种情况下,网络分割隔绝了老的主服务器M1,于是从服务器R2被提升为主服务器。但是,客户端,例如C1,和老的主服务器处于同一个分割中,可能继续往旧的服务器上写入数据。这些数据可能会永远都丢失,因为当分割恢复时,主服务器会被重新配置为新主服务器的从服务器,并丢弃其数据集。
这个问题可以通过使用Redis复制特性得以缓解,当主服务器检测到不能将写入传输到指定数量的从服务器时允许停止接收写请求。
min-slaves-to-write 1 min-slaves-max-lag 10
在上述配置下(请查看Redis发行包中的自描述redis.conf样例文件获取更多信息),一个Redis实例,当作为主服务器时,如果不能写入到至少一个从服务器,则会停止接收写入。由于复制是异步的,不能写入实际上意味着从服务器断开连接了,或者在超过指定的max-lag秒数内没有发送异步确认给我们。
使用这个配置,上面例子中的老的Redis主服务器M1将在10秒后变得不可用。当分割恢复时Sentinel的配置将会统一到新的配置,客户端C1可以获取到有效的配置并且继续与新主服务器交互。
但是天下没有免费的午餐,这种改进在当两个从服务器都下线时,主服务器会停止接收写入。这需要进行权衡。
例3:Sentinel in the client boxes
有时候我们只有两个Redis格子可用,一个用于主服务器,一个用于从服务器。例2中的配置在这种情况下不能适用,所以我们看下面这个配置,Sentinel部署在和客户端一起:
在这个配置中,Sentinel和客户端是一样的:如果一个主服务器对大多数客户端可达。这里的C1,C2,C3是通用的客户端,并不意味着C1表示一个连接到Redis的单个客户端。有可能是一个应用服务器,Rail应用,或者类似其他的。
如果运行M1和S1的格子故障了,发生故障转移没有什么问题。但是很容易看到不同的网络分割下会有不同的行为表现。例如,如果客户端和Redis服务器之间的网络断开了,Sentienl就不能运转,因为Redis主服务器和从服务器可能都不可用了。
注意,如果C3和M1被一起分割了(在上面的网络描述中不太可能,但是在不同的布局中是有可能的,或者因为软件层的故障),我们就碰到了例2中描述的问题,不同之处在于,这里我们无法打破对称,因为只有一个主服务器和从服务器,所以主服务器在被其主服务器断开时不能停止接受查询,否则主服务器在从服务器故障期间将永远不可用。
这是一个有效的设置,但是例2中的设置拥有其优势,例如Redis的高可用系统运行于Redis同一个格子中易于管理,以及在主服务器被分割到少数时还可以接受写入的能力。
例4:Sentinel client side with less than three clients
如果没有足够的三个格子在客户端侧(例如三个服务器),例3中的设置无法被使用。这次我们需要看看下面这个混合设置:
这个类似于例3中的设置,但是我们在4个可用的格子中运行了4个Sentinel。如果主服务器M1变得不可用,其他的3个Sentinel将会执行故障转移。
理论上这个设置可行,移除C2和S4运行的格子,并设置quorun为2。但是,我们不太想只有Redis侧的高可用而应用层没有高可用。
Sentinel, Docker, NAT, and possible issues
此处待补充。
2快速教程(A quick tutorial)
本文档下面的部分,将会逐步介绍Sentinel API,配置,语义的全部细节。但是为了那些想尽快试一把的人,这一部分将是一个介绍如何配置并与之交互的3个Sentinel实例的教程。
这里我们假定这些实例分别运行于5000,5001,5002端口。我们同时也假定我们拥有一个运行于6379的主服务器Redis实例,以及一个运行于6380端口的从服务器。我们将在教程中使用IPv4环回地址127.0.0.1来模拟运行于你自己的PC上。
三个Sentinel的配置文件应该看起来如下:
port 5000 sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 60000 sentinel parallel-syncs mymaster 1
其它两个配置文件是完全一样的,除了它们使用5001和5002作为端口号。
上面的配置中需要注意几点:
- 主服务器集合被称为mymaster。它用于标识主服务器及其从服务器。因为每个主服务器集合有一个不同的名称,所以Sentinel可以同时监控不同的主服务器和从服务器集合。
- quorum被设置为2(sentinel monitor配置指令的最后一个参数)。
- down-after-milliseconds的值是5000毫秒,也就是5秒。所以只要我们在这个时间内没有收到任何我们发出的ping的回复(reply),主服务器的故障就会被检测到。
一旦你启动了这三个Sentinel,你将会看到日志中的一些信息,像:
+monitor master mymaster 127.0.0.1 6379 quorum 2
这是一个Sentinel事件,你可以通过订阅/发布来收到这类事件,如果你SUNSCRIBE指定事件的名字。
Sentinel在检测和故障转移过程中会产生和记录不同的事件。
询问主服务器状态(Asking Sentinel about the state of a master)
开启Sentinel之路最显而易见的一件事情,就是检车其监控的主服务器是否运转正常:
$ redis-cli -p 5000 127.0.0.1:5000> sentinel master mymaster 1) "name" 2) "mymaster" 3) "ip" 4) "127.0.0.1" 5) "port" 6) "6379" 7) "runid" 8) "953ae6a589449c13ddefaee3538d356d287f509b" 9) "flags" 10) "master" 11) "link-pending-commands" 12) "0" 13) "link-refcount" 14) "1" 15) "last-ping-sent" 16) "0" 17) "last-ok-ping-reply" 18) "735" 19) "last-ping-reply" 20) "735" 21) "down-after-milliseconds" 22) "5000" 23) "info-refresh" 24) "126" 25) "role-reported" 26) "master" 27) "role-reported-time" 28) "532439" 29) "config-epoch" 30) "1" 31) "num-slaves" 32) "1" 33) "num-other-sentinels" 34) "2" 35) "quorum" 36) "2" 37) "failover-timeout" 38) "60000" 39) "parallel-syncs" 40) "1"
你可以看到打印出了关于主服务器的很多信息。我们对其中一部分很感兴趣:
- num-other-sentinels的值是2,所以我们可以知道Sentinel已经检测到这台主服务器的其他两个Sentinel。如果你检查日志的话,你会看到产生了+sentinel事件。
- flags的值是master。如果主服务器下线(down),我们将会看到flagde为s_down或者o_down。
- num-slaves正好为1,说明Sentinel检测到有一个从服务器连接到了主服务器。
为了探索这个实例的更多信息,你可能得试试下面两个命令:
SENTINEL slaves mymaster SENTINEL sentinels mymaster
第一个命令将会提供连接到主服务器的从服务器的类似的信息,第二个命令则是关于其它Sentinel。
获取当前主服务器的地址(Obtaining the address of the current master)
我们前面已经提到过,Sentinel还充当客户端的配置提供者,客户端通常连接到主服务器和从服务器的集合。由于可能的故障转移或者重配置,客户端无法知道谁是某个实例集合的当前活跃主服务器,所以Sentinel暴露了API来查询这样的问题:
127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster 1) "127.0.0.1" 2) "6379"
测试故障转移(Testing the failover)
此时,我们的Sentinel部署已经准备好测试了。我们可以干掉我们的主服务器,然后检查配置是否发生变化。要这么多我们只需要:
redis-cli -p 6379 DEBUG sleep 30
这个命令或使得我们的主服务器不再可达,休眠30秒。这模拟了由于某些原因而挂起的主服务器。
如果你检查Sentinel的日志,你应该会可以看到很多动作:
- 每一个Sentinel检测到主服务器下线(down)伴随着一个+sdowm事件。
- 这个事件稍后升级为+odown,这意味着多个Sentinel就主服务器不可达(reachable)达成了一致。
- Sentienl投票选举了其中一个Sentinel来开启第一次错误转移尝试。
- 故障转移发生了。
如果你再次询问mymaster的当前主服务器地址是什么,最终这次我们会得到一个不同的回复:
127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster 1) "127.0.0.1" 2) "6380"
目前为止一切顺利。此时你可以去尝试创建你自己的Sentinel部署,或者你也可以阅读更多来了解所有的Sentinel命令和内部实现原理。
===============================================================================
大家好,我是阮威。华中科技大学,计算机软件专业硕士。毕业后加入腾讯,先后在腾讯电子商务部和无线游戏产品部工作,现供职于欢聚时代基础产品部。IT男,至今。欢迎大家收听我的公众账号。