Redis 哨兵(Sentinel)

简介

https://redis.io/docs/manual/sentinel/ (opens in a new tab)

吹哨人巡查监控后台 master 主机是否故障,如果故障了根据投票数自动将某一个从库转换为新主库,继续对外服务。

作用

无人值守运维

  • 监控 redis 运行状态,包括 master 和 slave;
  • 当 master 宕机后,能自动将 slave 切换成新的 master。

功能

  • 主从监控:监控主从 redis 库运行是否正常。

  • 消息通知:哨兵可以将挂账转移的结果发送给客户端。

  • 故障转移:如果 master 异常,则会进行主从切换,将其中一个 slave 作为新 master。

  • 配置中心:客户端通过连接哨兵来获得当前 Redis 服务的主节点地址。

操作步骤

Redis Sentinel 架构

Redis 哨兵示意图

  • 3 个哨兵:自动监控和维护集群,不存放数据,只是吹哨人。
  • 1 主 2 从:用于数据读取和存放。

案例步骤

  • /myredis 目录下新建或者拷贝 sentinel.conf 文件,文件名绝不能错

  • 重点参数项说明

    # 服务监听地址,用于客户端连接,默认本机地址
    bind
    
    # 是否以后台 daemon 方式运行
    daemonize
    
    # 安全保护模式
    protected-mode
    
    # 端口
    port
    
    # 日志文件路径
    logfile
    
    # pid 文件路径
    pidfile
    
    # 工作目录
    dir
    
    # 设置要监控的 master 服务器
    # quorum 表示确认客观下线的最少的哨兵数量,同意故障迁移的法定票数
    # sentinel monitor <master-name> <ip> <redis-port> <quorum>
    #
    # Tells Sentinel to monitor this master, and to consider it in O_DOWN
    # (Objectively Down) state only if at least <quorum> sentinels agree.
    #
    # Note that whatever is the ODOWN quorum, a Sentinel will require to
    # be elected by the majority of the known Sentinels in order to
    # start a failover, so no failover can be performed in minority.
    #
    # Replicas are auto-discovered, so you don't need to specify replicas in
    # any way. Sentinel itself will rewrite this configuration file adding
    # the replicas using additional configuration options.
    # Also note that the configuration file is rewritten when a
    # replica is promoted to master.
    #
    # Note: master name should not include special characters or spaces.
    # The valid charset is A-z 0-9 and the three characters ".-_".
    
    # 我们知道,网络是不可靠的,有时候一个sentinel会因为网络堵塞而误以为一个master redis已经死掉了,
    # 在sentinel集群环境下需要多个sentinel互相沟通来确认某个master是否真的死了,quorum这个参数是进
    # 行客观下线的一个依据,意思是至少有quorum个sentinel认为这个master有故障,才会对这个master进行
    # 下线以及故障转移。因为有的时候,某个sentinel节点可能因为自身网络原因,导致无法连接master,而此时# master并没有出现故障,所以,这就需要多个sentinel都一致认为该master有问题,才可以进行下一步操
    # 作,这就保证了公平性和高可用。
    sentinel monitor <master-ip> <ip> <redis-port> <quorum>
    
    # master 设置了密码,连接 master 服务的密码
    sentinel auth-pass <master-name> <password>
    
    # 指定多少毫秒之后,主节点没有应答哨兵,此时哨兵主观上认为主节点下线
    sentinel down-after-milliseconds <master-name> <milliseconds>
    
    # 表示允许并行同步的 slave 个数,当 master 挂了后,哨兵会选出新的 master,此时,剩余的 slave 会向新的 master 发起同步数据
    sentinel parallel-syncs <master-name> <nums>
    
    # 故障转移的超时时间,进行故障转移时,如果超过设置的毫秒,表示故障转移失败
    sentinel failover-timeout <master-name> <milliseconds>
    
    # 配置当某一事件发生时所需要执行的脚本
    sentinel notification-script <master-name> <script-path>
    
    # 客户端重新配置主节点参数脚本
    sentinel client-reconfig-script <master-name> <script-path>
  • 案例哨兵 sentinel 文件通用配置

    • sentinel26379.conf

      bind 0.0.0.0
      daemonize yes
      protected-mode no
      port 26379
      logfile "/myredis/sentinel26379.log"
      pidfile /var/run/redis-sentinel26379.pid
      dir /myredis
      sentinel monitor <master-name> <master-ip> <master-port> 2
      sentinel auth-pass <master-name> <password>
    • sentinel26380.conf

      bind 0.0.0.0
      daemonize yes
      protected-mode no
      port 26380
      logfile "/myredis/sentinel26380.log"
      pidfile /var/run/redis-sentinel26380.pid
      dir "/myredis"
      sentinel monitor <master-name> <master-ip> <master-port> 2
      sentinel auth-pass <master-name> <password>
    • sentinel26381.conf

      bind 0.0.0.0
      daemonize yes
      protected-mode no
      port 26381
      logfile "/myredis/sentinel26381.log"
      pidfile /var/run/redis-sentinel26381.pid
      dir "/myredis"
      sentinel monitor <master-name> <master-ip> <master-port> 2
      sentinel auth-pass <master-name> <password>
  • 先启动一主二从 3 个 redis 实例,测试正常的主从复制

    • 架构说明

      Redis主从复制案例

      序号说明
      1169 机器上新建 redis6379.conf 配置文件,由于要配合本次案例,请设置 masterauth 项访问密码为 111111,不然后续可能报错 master_link_status:down
      2172 机器上新建 redis6380.conf 配置文件,设置好 replicaof
      3173 机器上新建 redis6381.conf 配置文件,设置好 replicaof
    • redis.conf

      • redis6379.conf

        masterauth "111111"

        6379 后续可能会变成从机,需要设置访问新主机的密码, 请设置 masterauth 项访问密码为111111,不然后续可能报错 master_link_status:down

      • redis6380.conf

        replicaof 192.168.111.169 6379
        masterauth "111111"
      • redis6381.conf

        replicaof 192.168.111.169 6379
        masterauth "111111"
    • 3 台不同的虚拟机实例,启动三步真实机器实例并连接

      • redis-cli -a 111111 -p 6379
      • redis-cli -a 111111 -p 6380
      • redis-cli -a 111111 -p 6381
    • 启动 3 个哨兵,完成监控

      redis-server /path/to/sentinel26379.conf --sentinel
      redis-server /path/to/sentinel26380.conf --sentinel
      redis-server /path/to/sentinel26381.conf --sentinel
    • 启动 3 个哨兵,监控后再测试一次主从复制并且查看主从关系 info replication

    • 原有的 master 宕机

      • 手动关闭 6379 的 redis 服务,模拟 master 挂了。

      • 数据正常,选取 6381 为新的 master;当 6379 重新上线后,身份为 slave。具体选举过程查看各个 redis 服务的日志文件。

      • 注意

        • 数据正常,但当 6379 关闭连接后,两台机器会出现如下两个问题

          127.0.0.1:6379> info replication
          Error: Server closed the connection
          not connected>
          
          127.0.0.1:6380> get k2
          "v2"
          127.0.0.1:6380> get k2
          Error: Broken pipe
          not connected>

          注意一定要看 info replicationmaster_link_status 状态为 up

        • 了解 Broken Pipe

          学习进度解释
          认识 Broken Pipepipe 是管道的意思,管道里面是数据流,通常是从文件或网络套接字读取的数据。当该管道从另一端突然关闭时,会发送数据突然中断,即是 broken,对于 socket 来说,可能是网络被拔出或另一端的进程奔溃。
          解决问题其实当该异常产生的时候,对于服务端来说,并没有多少影响。因为可能是某个客户端突然中止了进程导致了该错误。
          总结 Broken Pipe这个异常是客户端读取超时关闭了连接,这时候服务端再向客户端已经断开的连接写数据时就发生了 broken pipe 异常!

          broken pipe 的意思是对端的管道已经断开,往往发生在远端把这个读/写管道关闭了,你无法在对这个管道进行读写操作。从 tcp 的四次挥手来讲,远端已经发送了 FIN 序号*告诉你我这个管道已经关闭,这时候,如果你继续往管道里写数据,第一次,你会收到一个远端发送的 RST 信号,如果你继续往管道里写 write 数据,操作系统就会给你发送 SIGPIPE 的信号,并且将 errno 设置为 Broken Pipe(32),如果你的程序默认没有对 SIGPIPE 进行处理,那么程序会中断退出。一般情况下,可以用 signal(SIGPIPE,SIG_IGN)忽略这个信号,这样的话程序不会退出,但是 write 会返回 -1 并且将 errno 置为 Broken pipe(32) 。broken pipe 只会出现在对端已经关闭的管道里写数据的情况下(在收到对端的 RST 序号后第一次写不会出现 broke pipe,而是 write 返回 -1,这时候正确的作法应该是本地也 close 这个管道,如果继续 write,那么就会出现这个错误。)

    • 对比配置文件(sentinel26379.conf redis6379.conf redis6381.conf)

      • 文件的内容,在运行期间会被 sentinel 动态进行修改;
      • Master-Slave 切换后,master_redis.conf、slave_redis.conf 和 sentinel.conf 的内容都会发生改变,即 master_redis.conf 中会多一行 slaveof 的配置,sentinel.conf 的监控目标会随之调换。

备注

  • 生产都是不同机房不同服务器,很少出现 3 个哨兵全挂掉的情况;
  • 可以同时监控多个 master,一行一个。

哨兵运行流程和选举原理

当一个主从配置中的 master 失效之后,sentinel 可以选举出一个新的 master 用于自动接替原 master 的工作,主从配置中的其他 redis 服务器自动指向新的 master 同步数据一般建议 sentinel 采取奇数台,防止某一台 sentinel 无法连接到 master 导致误切换。

运行流程,故障切换

三个哨兵监控一主二从,正常运行

Redis 哨兵示意图

SDown 主观下线(Subjectively Down)

  • SDOWN(主观不可用)是单个 sentinel 自己主观上检测到的关于 master 的状态,从 sentinel 的角度来看,如果发送了 PING 心跳后,在一定时间内没有收到合法的回复,就达到了 SDOWN 的条件;

  • sentinel 配置文件中的 down-after-milliseconds 设置了主观判断下线的时间长度;

  • 说明

    所谓主观下线(Subjectively Down, 简称 SDOWN)指的是单个 Sentinel 实例对服务器做出的下线判断,即单个sentinel认为某个服务下线(有可能是接收不到订阅,之间的网络不通等等原因)。主观下线就是说如果服务器在 [sentinel down-after-milliseconds] 给定的毫秒数之内没有回应 PING 命令或者返回一个错误消息, 那么这个 Sentinel 会主观的(单方面的)认为这个 master 不可用。

    # sentinel down-after-milliseconds <master-name> <milliseconds>
    # Number of milliseconds the master (or any attached replica or sentinel) shouldbe # unreachable (as in, not acceptable reply to PING, cntinuously, for thespecified # period) in order to consider it in S_DOWN state (Subjectively DOWN).
    
    # Default is 30 seconds.
    sentinel down-after-milliseconds mymaster 30000

    表示 master 被当前 sentinel 示例认定失效的间隔时间,这个配置其实就是进行主观下线的一个依据

    master 在多长时间内一直没有给 Sentine 返回有效信息,则认定该 master 主观下线。也就是说如果多久没联系上 redis-server,认为这个 redis-server 进入到失效(SDWON)状态。

ODown 客观下线(Objectively Down)

  • ODWON 需要一定数量的 sentinel,多个哨兵达成一致意见才能认为一个 master 主观上已经宕机。

  • 说明

    master-name 是对某个特 master + slave 组合的一个区分标识(一套 sentinel 可以监听多组 master + slave 这样的组合)

    # sentinel monitor <master-name> <ip> <redis-port> <quorum>
    # 
    # Tells Sentinel to monitor this master, and to consiter it in O_DWON (Objectively 
    # DOWN) state only if at least <quorum> sentinels agree.
    # 
    # Note tahst whatever is the ODOWN quorum, a Sentinel will require to 
    # be elected by the majority of the known Sentinels in order to 
    # start a failover, so no failover can be performed in minority.
    #
    # Replicas are auto- discovered.so you don' t need to specify replicas in 
    # any way. Sentinel itself will rewrite this configuration file adding 
    # the replicas using additional confiquration options.
    # Also note that the confiquration file is rewritten when a 
    # replica is promoted to master.
    # 
    # Note: master name should not include special characters or spaces.
    # The valid charset is A- Z O 9 and the three characters ".
    sentinel monitor mymaster 127.0.0.1 6379 2

    quorum这个参数是进行客观下线的一个依据,法定人数/法定票数。

    意思是至少有 quorum 个 sentinel 认为这个 master 有故障才会对这个 master 进行下线以及故障转移。因为有的时候,某个 sentinel 节点可能因为自身网络原因导致无法连接 master,而此时 master 并没有出现故障,所以这就需要多个 sentinel 都一致认为该 master 有问题,才可以进行下一步操作,这就保证了公平性和高可用。

选举出领导者哨兵(哨兵中选出兵王)

  • 当主节点被判断客观下线以后,各个哨兵节点会进行协商,先选举出一个领导者哨兵节点并由该领导者节点,也即被选举出的领导者哨兵节点进行 failover(故障迁移)。

  • 如何选择哨兵领导者

    Raft 算法

    Redis 哨兵 Raft 算法选举领导者

    监视该主节点的所有哨兵都有可能被选为领导者,选举使用的算法是 Raft 算法;Raft 算法的基本思路是先到先得:即在一轮选举中,哨兵 A 向 B 发送成为领导者的申请,如果 B 没有同意过其他哨兵,则会同意 A 成为领导者。

由兵王开始推送故障切换流程并选出一个新 master

  • 新主登基

    • 某个 Slave 被选中成为新的 Master

      从下线的主服务的所有从服务里面挑选一个从服务,将其转成主服务,选择条件依次为:

      • 选择优先级靠前的(优先级在 redis.conf 中 slave-priority 100)
      • 选择偏移量最大的(偏移量是指获得原主数据最多的)
      • 选择 runid 最小的从服务(每个 redis 实例启动后都会随机生成一个 40 位的 runid)
    • 选出新 master 的规则,剩余 slave 节点健康前提下

  • 群臣俯首

    • 一朝天子一朝臣,换个码头重头拜
    • 执行 slaveof no one 命令让选出来的从节点成为新的主节点,并通过 slaveof 命令让其他节点成为其从节点
    • sentinel leader 会对选举出的新 master 执行 slaveof no one 操作,将其提升为 master 节点
    • sentinel leader 向其他 slave 发送命令,让剩余的 slave 成为新的 master 节点的 slave
  • 旧主拜服

    • 老 master 回来也认怂
    • 将之前已下线的老 master 设置为新选出的新 master 的从节点,当老 master 重新上线后,它会成为新 master 的从节点
    • sentinel leader 会让原来的 master 降级为 slave 并恢复正常工作。

上述的 failover 操作均由 sentinel 自己独自完成,完全无需人工干预。

哨兵使用建议

  • 哨兵节点的数量应为多个,哨兵本身应该集群,保证高可用
  • 哨兵节点的数量应该是奇数
  • 各个哨兵节点的配置应一致
  • 如果哨兵节点部署在 Docker 等容器里面,尤其要注意端口的正确映射
  • 哨兵集群 + 主从复制,并不能保证数据零丢失(在主从机器切换的时候 Redis 会有几秒断开)