【目标】
掌握解决主从复制故障的解决方案
掌握哨兵监控的搭建
掌握哨兵监控机制及故障的自动转移
【理论知识】
哨兵监控架构设计
主观和客观下线
Leader选举流程
故障迁移流程
【实际操作】
主从模式中,当主节点宕机之后,从节点是可以作为主节点顶上来继续提供服务,但是需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工干预。
在 Redis 2.8 版本开始,引入了哨兵(Sentinel)这个概念,在主从复制的基础上,哨兵实现了自动化故障恢复。如上图所示,哨兵模式由两部分组成,哨兵节点和数据节点:
Redis Sentinel 是分布式系统中监控 Redis 主从服务器,并提供主服务器下线时自动故障转移功能的模 式。
监控(Monitoring):Sentinel 会不断地检查你的主服务器和从服务器是否运作正常;
提醒(Notification):当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或 者其他应用程序发送通知;
自动故障迁移(Automatic failover):当一个主服务器不能正常工作时, Sentinel 会开始一次自动故 障迁移操作。
配置提供者(Configuration provider):为客户端提供服务发现功能,客户端连接 Sentinel 获取当前 Master 节点的相关信息,当发生 failover 时,Sentinel 将新通知客户端。
哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都有;
主从可以自动切换,系统更健壮,可用性更高;
Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。当被监控的某个 Redis 服务器出现问 题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
IP | 角色 |
---|---|
192.168.10.101 | Master |
192.168.10.102 | Slave |
192.168.10.103 | Slave |
三个节点分别创建 sentinel.conf 并添加以下配置。
vi /usr/local/redis/conf/sentinel.conf
# 放行所有 IP 限制
bind 0.0.0.0
# 进程端口号
port 26379
# 后台启动
daemonize yes
# 日志记录文件
logfile "/usr/local/redis/log/sentinel.log"
# 进程编号记录文件
pidfile /var/run/sentinel.pid
# 指示 Sentinel 去监视一个名为 mymaster 的主服务器 2为制裁权重值
sentinel monitor mymaster 192.168.10.101 6379 2
# 访问主节点的密码
sentinel auth-pass mymaster 123456
# Sentinel 认为服务器已经断线所需的毫秒数
sentinel down-after-milliseconds mymaster 10000
# 若 Sentinel 在该配置值内未能完成 failover 操作,则认为本次 failover 失败
sentinel failover-timeout mymaster 180000
先启动 3 个 Redis 服务
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis.conf
再启动 3 个 Sentinel 服务
/usr/local/redis/bin/redis-sentinel /usr/local/redis/conf/sentinel.conf
查看日志
tail -f /usr/local/redis/log/sentinel.log
接下来我们了解一些 Sentinel 中的关键名词,然后系统讲解下哨兵模式的工作原理。
Sentinel 内部有 3 个定时任务,分别是:
所谓主观下线(Subjectively Down, 简称 SDOWN)指的是单个 Sentinel 实例对服务器做出的下线判 断,即单个 Sentinel 认为某个服务下线(有可能是接收不到订阅,之间的网络不通等等原因)。 主观下线就是说如果服务器在给定的毫秒数之内, 没有返回 Sentinel 发送的 PING 命令的回复, 或者返 回一个错误, 那么 Sentinel 会将这个服务器标记为主观下线(SDOWN)。
客观下线(Objectively Down, 简称 ODOWN)指的是多个 Sentinel 实例在对同一个服务器做出 SDOWN 判断,并且通过命令互相交流之后,得出的服务器下线判断,然后开启 failover。 只有在足够数量的 Sentinel 都将⼀个服务器标记为主观下线之后, 服务器才会被标记为客观下线 (ODOWN)。只有当 Master 被认定为客观下线时,才会发生故障迁移。
仲裁指的是配置文件中的 quorum 选项。某个 Sentinel 先将 Master 节点标记为主观下线,然后会将这 个判定通过 sentinel is-master-down-by-addr 命令询问其他 Sentinel 节点是否也同样认为该 addr 的 Master 节点要做主观下线。
最后当达成这一共识的 Sentinel 个数达到前面说的 quorum 设置的值 时,该 Master 节点会被认定为客观下线并进行故障转移。 quorum 的值一般设置为 Sentinel 个数的二分之一加 1,例如 3 个 Sentinel 就设置为 2
先启动 3 个 Redis 服务
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis.conf
再启动 3 个 Sentinel 服务
/usr/local/redis/bin/redis-sentinel /usr/local/redis/conf/sentinel.conf
查看日志
tail -f /usr/local/redis/log/sentinel.log
kill -9 杀死 Redis 主节点,down-after-milliseconds 时间满⾜以后,观察 Sentinel 日志是否会发起 重新选举操作,内容如下。
# ----------主节点主观下线(单个 Sentinel 实例对服务器做出的下线判断)----------
+sdown master mymaster 192.168.10.101 6379
# ----------客观下线(多个 Sentinel 实例在对同一个服务器做出 SDOWN 判断)--------
+odown master mymaster 192.168.10.101 6379 #quorum 2/2
# ----------启动新的选举流程,记录选举次数----------
+new-epoch 1
# ----------开启 failover 故障转移----------
+try-failover master mymaster 192.168.10.101 6379
# ----------投票选举一个领导人去做故障转移这件事----------
+vote-for-leader b3daf6027253fcce3c185afc4f61787eb1bc91b9
1885ea63a07b7c959aeb915ebda88414155514f3e voted for b3daf6027253fcce3c185afc4f61787eb1bc91b9
116793d6b724c527bd2c9865fa9dff7bc1a1ece18 voted for
b3daf6027253fcce3c185afc4f61787eb1bc91b9 1
# ----------对 192.168.10.101 6379 进行故障转移----------
+elected-leader master mymaster 192.168.10.101 6379
# ----------查找环境中(主从环境)合适的 Slave----------
+failover-state-select-slave master mymaster 192.168.10.101 6379
# ----------已经找到环境中(主从环境)合适的 Slave----------
+selected-slave slave 192.168.10.103:6379 192.168.10.103 6379 @mymaster 192.168.10.101 6379
# ----------向 Slave 发送 “slaveof no one” 指令(关闭复制功能),此时 Slave 已经完成⻆色转换,此 Slave 即为 Master----------
* +failover-state-send-slaveof-noone slave 192.168.10.103:6379 192.168.10.103 6379 @ mymaster 192.168.10.101 6379
# ----------等待其他 Sentinel 确认 Slave 的升级----------
* +failover-state-wait-promotion slave 192.168.10.103:6379 192.168.10.103 6379 @ mymaster 192.168.10.101 6379
# ----------确认完毕,提升 192.168.10.103:6379 为主节点----------
+promoted-slave slave 192.168.10.103:6379 192.168.10.103 6379 @mymaster 192.168.10.101 6379
# ----------开始对 Slaves 进行 reconfig 操作----------
+failover-state-reconf-slaves master mymaster 192.168.10.101 6379
# --------向指定的 Slave 发送 “slaveof” 指令,告知此 Slave 跟随新的 Master-------
* +slave-reconf-sent slave 192.168.10.102:6379 192.168.10.102 6379 @ mymaster 192.168.10.101 6379
# ----------192.168.10.101 6379 客观下线----------
-odown master mymaster 192.168.10.101 6379
# ----------此 Slave 正在执行 slaveof + SYNC 过程----------
* +slave-reconf-inprog slave 192.168.10.102:6379 192.168.10.102 6379 @ mymaster 192.168.10.101 6379
# ----------此 Slave 同步完成,可以继续下一个 Slave 的 reconfig 操作----------
* +slave-reconf-done slave 192.168.10.102:6379 192.168.10.102 6379 @ mymaster 192.168.10.101 6379
# ----------故障转移结束----------
+failover-end master mymaster 192.168.10.101 6379
# ----------故障转移成功后,各个 Sentinel 实例开始监控新的 Master----------
+switch-master mymaster 192.168.10.101 6379 192.168.10.103 6379
* +slave slave 192.168.10.102:6379 192.168.10.102 6379 @ mymaster192.168.10.103 6379
* +slave slave 192.168.10.101:6379 192.168.10.101 6379 @ mymaster192.168.10.103 6379
+sdown slave 192.168.10.101:6379 192.168.10.101 6379 @ mymaster192.168.10.103 6379
此时再查看 192.168.10.102 和 192.168.10.103 的环境,发现 103 已切换为主节点,且从节点只有 102 是因为 101 还未重启,重启以后将作为从节点加入到环境中。
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.10.102,port=6379,state=online,offset=137591,lag=1
master_replid:1f9f9c105cb08669a2e45c6499d5f1dddc72bebd
master_replid2:27bbf26c1a482a3d2533e121739468fa1950f8cf
master_repl_offset:137877
second_repl_offset:30906
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:137877
重启 192.168.10.101 以后,Sentinel 日志如下:
# ----------⻆色转换为 Slave----------
* +convert-to-slave slave 192.168.10.101:6379 192.168.10.101 6379@ mymaster 192.168.10.103 6379
查看 192.168.10.101 主从信息如下:
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:192.168.10.103
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:379722
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:1f9f9c105cb08669a2e45c6499d5f1dddc72bebd
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:379722
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:379271
repl_backlog_histlen:452
再次查看 192.168.10.103 主从信息,如下则表示一次主从切换成功完成。
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.10.102,port=6379,state=online,offset=397866,lag=1
slave1:ip=192.168.10.101,port=6379,state=online,offset=397866,lag=0
master_replid:1f9f9c105cb08669a2e45c6499d5f1dddc72bebd
master_replid2:27bbf26c1a482a3d2533e121739468fa1950f8cf
master_repl_offset:397866
second_repl_offset:30906
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:397866
以下是原来的 192.168.10.101 的配置文件,文件末尾发生了如下改变,添加只读配置,复制配置。
# 从节点只读
replica-read-only yes
# Generated by CONFIG REWRITE
pidfile "/var/run/redis.pid"
user default on #8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 ~* +@all
replicaof 192.168.10.103 6379
以下是原来的 192.168.10.102 的配置文件,文件末尾发生了如下改变,复制节点改为了 103。
replica-read-only yes
replicaof 192.168.10.103 6379
# Generated by CONFIG REWRITE
pidfile "/var/run/redis.pid"
user default on #8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 ~* +@all
以下是原来的 192.168.10.103 的配置文件,文件末尾发生了如下改变,已经移除了复制配置。
# Generated by CONFIG REWRITE
2 pidfile "/var/run/redis.pid"
3 user default on #8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca1
2020c923adc6c92 ~* +@al
基于 Sentinel 自动发现机制,添加一个新 Sentinel 是非常简单的。你只需要启动配置 sentinel monitor mymaster 监视当前活动主服务器的新 Sentinel,10s 内 Sentinel 将获得其他 Sentinel 列表及被监控 Master 的所有 Slave 信息。
建议一个一个添加,等待其他 Sentinel 已经能和新 Sentinel 通信,再添加下一个。因为新增 Sentinel 有可能失败,一个一个添加 Sentinel 能有效保证大多数 Sentinel 都是正常的。一般每 30s 添加一个 Sentinel 是比较常见的。
添加结束后,可用命令 SENTINEL MASTER mastername 检查所有 Sentinel 是否已经完全获取到所有 Master 的信息。
Sentinel 不会完全清除已经添加过的 Sentinel 信息,即使被删除的 Sentinel 很长时间无法访问,因为 要尽量减少其他 Sentinel 的配置版本的更新。
因此为了移除一个 Sentinel,在没有网络隔离的情况下应遵循以下步骤:
1. 停止要删除的 Sentinel 进程。
2. SENTINEL RESET * 向所有其他 Sentinel 实例发送命令(*如果您只想重置一个主机,则可以使用确切的主机名代替)。一个接一个,在两次实例之间至少等待30秒。
3. 执行命令 SENTINEL MASTER mastername 检查每个 Sentinel 显示的 Sentinel 数量是否一致。
Sentinel 不会完全清除指定 Master 的 Slave,即使 Slave 长时间无法访问。在故障转移后,一旦旧 Master 再次可用,会自动成为新 Master 的 Slave,此时,新 Master 和新 Slave 会组成新的主从复制 架构。
若想从 Sentinel 监控的 Master/Slave 列表里永久删除一个 Slave,在停止 Slave 进程后,你需要向所 有 Sentinel 发送命令 SENTINEL RESET mastername,重置 mastername 所有状态信息。
故障迁移一致性
这里用到了分布式一致性的算法Raft共识算法,就是怎样选举Sentinel节点为领头节点
1)Sentinel自动故障迁移使用Raft算法来选举领头(leader)Sentinel ,从而确保在一个给定的周期 (epoch)里,只有一个领头产生。
2)这表示在同一个周期中, 不会有两个 Sentinel 同时被选中为领头,并且各个 Sentinel 在同一个节点 中只会对一个领头进行投票。
3)更高的配置节点总是优于较低的节点,因此每个 Sentinel 都会主动使用更新的节点来代替自己的配 置。
简单来说,我们可以将Sentinel配置看作是一个带有版本号的状态。一个状态会以最后写入者胜出(lastwrite-wins)的方式(也即是,最新的配置总是胜出)传播至所有其他Sentinel。
Redis Sentinel 严重依赖系统时间:为了确认实例是否可用,他会记录最后一次回复 PING 的时间,并和 当前时间比较推断距离最后一次回复过去了多久。
然而,若系统时间发生非正常改变,或者系统非常繁忙,或进程由于某些原因阻塞,Sentinel 可能会出现一些问题。
TITL 模式是一种特殊的“保护”模式,Sentinel 在发生一些意料之外问题时,会进入这个模式,降低对系 统时间的依赖。
Sentinel 所做的就是记录之前的中断调用时间,并和当前调用时间对比:
如果两次调用时间之间的差距为负值(系统时钟调整到之前某个时间值), 或者非常大(超过 SENTINEL_TILT_TRIGGER 时间,默认为 2s), 那么 Sentinel 进入 TILT 模式。
如果 Sentinel 已经进入 TILT 模式, 那么 Sentinel 延迟退出 TILT 模式的时间。
当处于 TITL 模式,Sentinel 或持续监控所有状态,但:
停止处理请求。
当有实例向这个 Sentinel 发送 SENTINEL is-master-down-by-addr 命令时,Sentinel 返回负 值:因为这个 Sentinel 所进行的下线判断已经不再准确。
如果 TILT 可以正常维持 30 秒钟(SENTINEL_TILT_PERIOD 时长,默认为 30s), 那么 Sentinel 退 出 TILT 模式,TITL 模式是 Sentinel 的被动模式。