Redis Sentinel翻译文档(2015.12.25)
[toc]
Redis Sentinel介绍
Redis Sentinel 为Redis提供了高可用的实现。通俗来说就是你可以部署一套无需人为干预即可防灾的Redis环境。
RS同时为客户端提供了其他诸如监控,通知的功能。
从全局来说RS的功能如下:
- 监控: RS时刻监控主从是否在正常工作。
- 通知: 当某个Redis实例出现问题,RS可以通知系统管理员或者通过API通知其他程序。
- 自动切换: 如果一个主实例失效,RS会启动一个失效转移(从升级为主)的进程,其他的从节点将重新跟随新的主节点。连接到RedisServer的会被通知切换到新的地址。
- 配置提供者: RS充当了客户端服务自动发现的提供者:连接到Sentinal的客户端,Sentianl会响应最新的主节点地址给客户端,并且当发生转移的时候会发送通知。
分布式的Redis Sentinel
Redis sentinel是一个分布式的系统。
它运行在有多个Sentinel协调工作的环境中,多个Sentinel协调工作的好处如下:
- 当一个master不可用后,多个Sentinel共同决定后才执行故障检测,减少了错误判断的几率。
- 即使有失效Sentinel节点,系统依然健壮的工作。没有故障转移的单点系统毕竟毫无意义。
所有的Sentinel,Redis实例(主从)以及连接到Sentinel和Reids的客户端,同时组成了一个大型的分布式系统。本文将逐步从基本配置使得可以理解Sentinel的基本属性讲起,到更多的综合介绍(可选的)以便理解Sentinel是如何工作的。
快速开始
获取Sentinel
目前的Sentinel版本是Sentinel 2,用了更健壮和简单的预测算法重写了最初的版本。
Redis Sentinel的稳定版本嵌入在Redis2.8和3.0版本中。这两个都是目前最新的版本。新的版本正在不稳定的分支上进行开发,当确认这些新特性已经稳定后会尽快移植到2.8以及3.0。
Redis Sentinel1,嵌入在Redis2.6中,已经不推荐使用。
启动Sentinel
如果你在使用redis-sentinel的可执行文件(或者redis-server),你可以用下面的命令来启动redis-sentinal
redis-sentinel /path/to/sentinel.conf
同时你可以用redis-server以sentinel模式启动
redis-server /path/to/sentinel.conf --sentinel
这两种方式是一样的。
但是这两种方式都必须显示声明配置文件,这个文件将在重启的时候被用来保存快照以便重新加载。如果未提供配置文件或者提供的配置文件不可写,系统将直接拒绝启动。
系统运行中默认绑定的监听端口是26379,所以为了Sentinel正常工作,机器的26379端口必须开启以便接收其他Sentinel实例的连接。不然Sentinel就无法应答请求,导致故障转移无法进行。
部署Sentinel的基本要求
- 一个健壮的环境你需要至少三个Sentinel节点。
- 安装在电脑或者虚拟机下的三个实例应该在不同的方式下失效,举个例子,用多个不同的物理机或者用虚拟机不同的可用区域。
- Redis+Sentinel的分布式系统在Redis异步复制期间,不能保证保留在失败期间收到的写请求。
- 你需要Sentinel客户端的支持,大部分流行的客户端都有,但并不是所有。
- 你必须经过大量的测试来确认HA是安全可用的。否则当你发现时已经迟了。
- Sentinel和Docker混合使用的时候应该注意:Docker执行的Sentinel重映射,破坏了Sentinel的自动发现。稍后会详细讲解。
配置Sentinel
Redis源文件包含了一个可以启动sentinel叫做sentinel.conf的文件,但是一个典型的最小配置如下:
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
你只需要指定监控的master,给每个master(可能包含不定的slave)一个不同的名称。slave是自动发现的,所以无需指定。当追加新的节点后,Sentinel会自动更新配置。当故障期间slave升级为master或者一个新的sentinel被发现后,也会自动更新配置。重启后将会使用新的配置。
sentinel monitor声明的含义如下:
sentinel monitor <master-group-name> <ip> <port> <quorum>
为了清晰起见,让我们逐行分析分析文件:
第一行是用来告诉Redis监控一个叫做mymaster的master,ip地址为127.0.0.1以及端口是6379,quorum为2。除了quorum(票数)以外,一切都显而易见:
- quorum是需要确认master失效的Setinel的数量,为了确认master已经失效(访问网络阻塞引起的误报),而后启动一个failover的进程(但是并不会马上开始,需要其他Sentinel的同意)。
- quorum仅仅用来确认是否故障。为了故障转移,多个Sentinel需要选举出新的master。
举例来说,如果你有五个Sentinel进程,而且quorum设置了2,表现如下:
- 如果两个Sentinel同时同意了master失效,两个中的一个将开始进程故障转移。
- 如果至少有三个Sentinel是可用的,failover将被通过并且真正开始启动。
例如,集群中有5个sentinel,票数被设置为2,当2个sentinel认为一个master已经不可用了以后,将会触发failover,但是,进行failover的那个sentinel必须先获得至少3个sentinel的授权才可以实行failover。
如果票数被设置为5,要达到ODOWN状态,必须所有5个sentinel都主观认为master为不可用,要进行failover,那么得获得所有5个sentinel的授权。这就是说大多数。
实际上这是说在故障期间,如果多数Sentinel不能响应,将无法启动故障转移的进程。
其他的Sentinel选项
其他选项基本如下:
sentinel <option_name> <master_name> <option_value>
用途如下:
- down-after-milliseconds是Sentinel询问一个实例是否宕掉(包含不能响应Ping或者响应错误)的超时时间。
- parallel-syncs设置了故障转移时候同时重新同步新的master的slave数量。这个数量越小,故障转移花费的时间将越长。但是如果你希望从同时提供数据,就不能将所有的slave同时去复制数据。可以通过将这个值设为 1 来保证每次只有一个slave处于不能处理命令请求的状态。
其他配置项在sentinel.conf中都有很详细的解释。
所有的配置都可以在运行时用命令SENTINEL SET command动态修改。
其他说明(文档未发现,来自别的文档)
Sentinel的“仲裁会”
前面我们谈到,当一个master被sentinel集群监控时,需要为它指定一个参数,这个参数指定了当需要判决master为不可用,并且进行failover时,所需要的sentinel数量,本文中我们暂时称这个参数为票数
不过,当failover主备切换真正被触发后,failover并不会马上进行,还需要sentinel中的大多数sentinel授权后才可以进行failover。
当ODOWN时,failover被触发。failover一旦被触发,尝试去进行failover的sentinel会去获得“大多数”sentinel的授权(如果票数比大多数还要大的时候,则询问更多的sentinel)
这个区别看起来很微妙,但是很容易理解和使用。例如,集群中有5个sentinel,票数被设置为2,当2个sentinel认为一个master已经不可用了以后,将会触发failover,但是,进行failover的那个sentinel必须先获得至少3个sentinel的授权才可以实行failover。
如果票数被设置为5,要达到ODOWN状态,必须所有5个sentinel都主观认为master为不可用,要进行failover,那么得获得所有5个sentinel的授权。
配置版本号
为什么要先获得大多数sentinel的认可时才能真正去执行failover呢?
当一个sentinel被授权后,它将会获得宕掉的master的一份最新配置版本号,当failover执行结束以后,这个版本号将会被用于最新的配置。因为大多数sentinel都已经知道该版本号已经被要执行failover的sentinel拿走了,所以其他的sentinel都不能再去使用这个版本号。这意味着,每次failover都会附带有一个独一无二的版本号。我们将会看到这样做的重要性。
而且,sentinel集群都遵守一个规则:如果sentinel A推荐sentinel B去执行failover,B会等待一段时间后,自行再次去对同一个master执行failover,这个等待的时间是通过failover-timeout配置项去配置的。从这个规则可以看出,sentinel集群中的sentinel不会再同一时刻并发去failover同一个master,第一个进行failover的sentinel如果失败了,另外一个将会在一定时间内进行重新进行failover,以此类推。
redis sentinel保证了活跃性:如果大多数sentinel能够互相通信,最终将会有一个被授权去进行failover.
redis sentinel也保证了安全性:每个试图去failover同一个master的sentinel都会得到一个独一无二的版本号。
配置传播
一旦一个sentinel成功地对一个master进行了failover,它将会把关于master的最新配置通过广播形式通知其它sentinel,其它的sentinel则更新对应master的配置。
一个faiover要想被成功实行,sentinel必须能够向选为master的slave发送SLAVE OF NO ONE命令,然后能够通过INFO命令看到新master的配置信息。
当将一个slave选举为master并发送``SLAVE OF NO ONE```后,即使其它的slave还没针对新master重新配置自己,failover也被认为是成功了的,然后所有sentinels将会发布新的配置信息。
新配在集群中相互传播的方式,就是为什么我们需要当一个sentinel进行failover时必须被授权一个版本号的原因。
每个sentinel使用##发布/订阅##的方式持续地传播master的配置版本信息,配置传播的##发布/订阅##管道是:sentinel:hello。
因为每一个配置都有一个版本号,所以以版本号最大的那个为标准。
举个栗子:假设有一个名为mymaster的地址为192.168.1.50:6379。一开始,集群中所有的sentinel都知道这个地址,于是为mymaster的配置打上版本号1。一段时候后mymaster死了,有一个sentinel被授权用版本号2对其进行failover。如果failover成功了,假设地址改为了192.168.1.50:9000,此时配置的版本号为2,进行failover的sentinel会将新配置广播给其他的sentinel,由于其他sentinel维护的版本号为1,发现新配置的版本号为2时,版本号变大了,说明配置更新了,于是就会采用最新的版本号为2的配置。
这意味着sentinel集群保证了第二种活跃性:一个能够互相通信的sentinel集群最终会采用版本号最高且相同的配置。
其他细节
sentinel对于不可用有两种不同的看法,一个叫主观不可用(SDOWN),另外一个叫客观不可用(ODOWN)。SDOWN是sentinel自己主观上检测到的关于master的状态,ODOWN需要一定数量的sentinel达成一致意见才能认为一个master客观上已经宕掉,各个sentinel之间通过命令SENTINEL is_master_down_by_addr来获得其它sentinel对master的检测结果。
从sentinel的角度来看,如果发送了PING心跳后,在一定时间内没有收到合法的回复,就达到了SDOWN的条件。这个时间在配置中通过is-master-down-after-milliseconds参数配置。
当sentinel发送PING后,以下回复之一都被认为是合法的:
PING replied with +PONG.
PING replied with -LOADING error.
PING replied with -MASTERDOWN error.
其它任何回复(或者根本没有回复)都是不合法的。
从SDOWN切换到ODOWN不需要任何一致性算法,只需要一个gossip协议:如果一个sentinel收到了足够多的sentinel发来消息告诉它某个master已经down掉了,SDOWN状态就会变成ODOWN状态。如果之后master可用了,这个状态就会相应地被清理掉。
正如之前已经解释过了,真正进行failover需要一个授权的过程,但是所有的failover都开始于一个ODOWN状态。
ODOWN状态只适用于master,对于不是master的redis节点sentinel之间不需要任何协商,slaves和sentinel不会有ODOWN状态。
slave自动发现机制
虽然sentinel集群中各个sentinel都互相连接彼此来检查对方的可用性以及互相发送消息。但是你不用在任何一个sentinel配置任何其它的sentinel的节点。因为sentinel利用了master的发布/订阅机制去自动发现其它也监控了统一master的sentinel节点。
通过向名为__sentinel__:hello的管道中发送消息来实现。
同样,你也不需要在sentinel中配置某个master的所有slave的地址,sentinel会通过询问master来得到这些slave的地址的。
每个sentinel通过向每个master和slave的发布/订阅频道__sentinel__:hello每秒发送一次消息,来宣布它的存在。
每个sentinel也订阅了每个master和slave的频道__sentinel__:hello的内容,来发现未知的sentinel,当检测到了新的sentinel,则将其加入到自身维护的master监控列表中。
每个sentinel发送的消息中也包含了其当前维护的最新的master配置。如果某个sentinel发现
自己的配置版本低于接收到的配置版本,则会用新的配置更新自己的master配置。
在为一个master添加一个新的sentinel前,sentinel总是检查是否已经有sentinel与新的sentinel的进程号或者是地址是一样的。如果是那样,这个sentinel将会被删除,而把新的sentinel添加上去。
Sentinel部署示例
前面你已经了解了Sentinel的基本信息,你可能希望了解你应该如何部署Sentinel以及Sentinel应该部署多少个等等问题。下面我们将会用一些例子来讲解。
这个环节我们将用文本图像来表示(ASCII art,这个挺好玩)
// 这个代表一个计算机
+--------------------+
| 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 |
+-------------+ +-------------+
除此以外:
- master叫做M1,M2,M3,...,Mn.
- slaves叫做R1,R2,R3....Rn.
- Sentinels叫做S1...Sn.
- clients叫做C1...Cn.
- 当一个实例被Sentinel改变状态后,我们把他放入中括号。
因为Sentinel需要多数同意,所以将不会示范仅仅两个Sentinel的设置。
例子1:只有两个Sentinel,不要这么做!
+----+ +----+
| M1 |---------| R1 |
| S1 | | S2 |
+----+ +----+
Configuration: quorum = 1
- 这个设置下,如果master M1挂掉,当两个Sentinel都正常的情况下,R1将会升级为主。这样看起来似乎是没有问题的,但是请看下面。
- 如果M1的那台机器停止工作了,那么S1也就挂了。S2无法得到大多数的授权而导致系统不可用。
会演变成下面的情况,造成数据不一致:
+----+ +------+
| M1 |----//-----| [M1] |
| S1 | | S2 |
+----+ +------+
所以至少使用三个sentinel。
例子2:三个Sentinel
+----+
| M1 |
| S1 |
+----+
|
+----+ | +----+
| R2 |----+----| R3 |
| S2 | | S3 |
+----+ +----+
Configuration: quorum = 2
M1挂了以后,S2和S3将同意failover。
Slave复制过程中,已经收到的写请求有丢失的风险。但是上面的风险更大,如下所示:
+----+
| M1 |
| S1 | <- C1 (writes will be lost)
+----+
|
/
/
+------+ | +----+
| [M2] |----+----| R3 |
| S2 | | S3 |
+------+ +----+
M1变成了一个隔离的环境,R2升级为了主。但是C1的请求将会丢失。当M1恢复以后,将丢弃原来的数据,成为新的Master(R2)的从。
因为redis采用的是异步复制,在这样的场景下,没有办法避免数据的丢失。然而,你可以通过以下配置来配置redis3和redis1,减弱这种情况。
min-slaves-to-write 1
min-slaves-max-lag 10
通过上面的配置,当一个redis是master时,如果它不能向至少一个slave写数据(上面的min-slaves-to-write指定了slave的数量),它将会拒绝接受客户端的写请求。由于复制是异步的,master无法向slave写数据意味着slave要么断开连接了,要么不在指定时间内向master发送同步数据的请求了(上面的min-slaves-max-lag指定了这个时间)。
例子3:Sentinel安装在客户端机上
有时候我们仅仅只有两台Redis机器,一个主一个从,那么我们不能按照例2的方式部署,所以我们可以调整如下:
+----+ +----+
| M1 |----+----| R1 |
| S1 | | | S2 |
+----+ | +----+
|
+------------+------------+
| | |
| | |
+----+ +----+ +----+
| C1 | | C2 | | C3 |
| S1 | | S2 | | S3 |
+----+ +----+ +----+
Configuration: quorum = 2
如上所示,可见的Sentinel和客户端数量一致,如果一个主可以被大多数的客户端访问,那么他是可用的。客户端只是普通的Redis应用。
但是可能会C1不可用导致系统无法继续工作。省略余下翻译。
例4:混合型
+----+ +----+
| M1 |----+----| R1 |
| S1 | | | S2 |
+----+ | +----+
|
+------+-----+
| |
| |
+----+ +----+
| C1 | | C2 |
| S3 | | S4 |
+----+ +----+
Configuration: quorum = 3
Sentinel,Docker,NAT以及其他可能的问题
Docker用了一个端口映射的技术,运行在Docker中的程序可能用了一个我们想象之外的端口,这个对于同时在一台机器运行多个公用端口的应用很有用处。
Docker不是唯一会导致这种情况的,NAT一样,甚至包括Ip都会被重新映射。
这导致Sentinel的两个问题:
- 无法自动发现其他Sentinel,因为他们基于监听固定IP端口来发送广播,但是他们并不能理解remapped以后的IP和端口,所以会错报无法连接的信息。
- 对于从库来说也有同样的问题。
你需要用到下面的配置:
sentinel announce-ip <ip>
sentinel announce-port <port>