• Redis M/S + Keepalived 主从备份高可用


    目录

    高可用

    高可用是指服务器可以正常访问的时间,衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999% 等等)。而在 Redis 的语境中,高可用的含义似乎要更宽泛一些,除了保证提供正常服务(如:主从分离、快速容灾技术等),还需要考虑数据容量的扩展、数据安全不会丢失等。

    Redis 的高可用

    在 Redis 中,实现高可用的技术主要包括持久化、复制、哨兵和集群:

    • 持久化:即将数据存储在硬盘,保证数据不会因进程退出而丢失,主要作用是数据备份。
    • 复制:主要实现了数据的多机备份以及对于读操作的负载均衡和简单的故障恢复。缺陷是故障恢复无法自动化、写操作无法负载均衡、存储能力受到单机的限制。
    • 哨兵:在复制的基础上,哨兵实现了自动化的故障恢复。缺陷是写操作无法负载均衡、存储能力受到单机的限制。
    • 集群:通过集群,Redis 解决了写操作无法负载均衡以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。

    本文主要讨论通过 Redis 的 “复制” 技术支撑的高可用方案。

    Redis 主从复制配置

    Redis 主从复制实际上就是将 MASTER 节点的数据,复制到其他 SLAVE 节点去进行存储。

    修改关键配置:

    • MASTER
    bind 0.0.0.0
    port 6379
    slave-serve-stale-data yes
    slave-read-only no
    ...
    • SLAVE
    bind 0.0.0.0
    port 6379
    slave-serve-stale-data yes
    slave-read-only no
    slaveof 172.16.81.140 6379
    ...

    可以看见,Redis 主从配置中最主要的一个项目就是 slaveof,他指定了 SLAVE 节点与 MASTER 节点的从属关系。

    重启 Redis Daemon 之后,查看配合是否有生效:

    • MASTER
    $ redis-cli
    127.0.0.1:6379> INFO
    ...
    # Replication
    role:master
    connected_slaves:1
    slave0:ip=172.18.22.202,port=6379,state=online,offset=108458,lag=0
    master_repl_offset:108458
    repl_backlog_active:1
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:22541
    repl_backlog_histlen:85918
    • SLAVE
    $ redis-cli
    127.0.0.1:6379> INFO
    ...
    # Replication
    role:slave
    master_host:172.18.22.204
    master_port:6379
    master_link_status:up
    master_last_io_seconds_ago:1
    master_sync_in_progress:0
    slave_repl_offset:108654
    slave_priority:100
    slave_read_only:0
    connected_slaves:0
    master_repl_offset:0
    repl_backlog_active:0
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:0
    repl_backlog_histlen:0

    可以看见 MASTER 和 SLAVE 当前是互相认证的:在 MASTER 上可以查看分别由哪些 SLAVE,状态如何,复制(Replication)的配置如何;在 SLAVE 上可以查看自己的 MASTER 是谁,状态如何,自己的权重是多少,是否只读。如果是只读的话,SLAVE 将无法写入任何数据:

    127.0.0.1:6379> SET test2 123
    (error) READONLY You can't write against a read only slave.

    下面进行一次同步测试:

    • MASTER
    $ redis-cli
    127.0.0.1:6379> SET test1 123
    OK
    • SLAVE
    $ redis-cli
    127.0.0.1:6379> GET test1
    "123"

    Redis 主从切换(手动方式)

    当 MASTER 宕机时,最简单的恢复方式就是使用手动切换的方式,手动的将一台从节点切换成主节点。手动方式显然是不推荐的,但我们不放了解一下。

    1. 关闭 MASTER。
    systemctl stop redis
    1. 手动将 SLAVE 设置成主节点。
    $ redis-cli
    127.0.0.1:6379> slaveof no one
    127.0.0.1:6379> INFO
    ...
    # Replication
    role:master
    connected_slaves:0
    master_repl_offset:1177
    repl_backlog_active:0
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:0
    repl_backlog_histlen:0
    1. 新的数据写入到 SLAVE。
    127.0.0.1:6379> SET test4 123
    OK
    127.0.0.1:6379> SET test5 123
    OK
    127.0.0.1:6379> SET test6 123
    OK

    下面为执行数据恢复的步骤:

    1. 持久化 SLAVE 的数据。
    127.0.0.1:6379> save
    OK
    1. 拷贝从节点的 dump.rdb 文件到主节点中。
    scp /var/lib/redis/dump.rdb root@<master>:/var/lib/redis/dump.rdb
    1. 重启 MASTER 和 SLAVE,发现 MASTER 和 SLAVE 的数据又保持了同步,而且身份角色也恢复到了初始状态。这是因为:当 Redis 重启时,手动执行的主从切换设置将会失效,还原为初始状态,因为我们在上面已写入了 SLAVE 的配置文件。

    SLAVEOF 指令

    SLAVEOF 指令指定了当前 Redis 实例是从属于某个 MASTER 的 SLAVE。如果这个指令在配置文件中写死,那么实例重启后就永远是 SLAVE,除非有哨兵将它提升为 MASTER,或手动执行指令 SLAVEOF NO ONE。

    在本文讨论的 M/S 场景中,则需要脚本或手动执行。而在哨兵模式的场景中,这个指令会被哨兵动态地从配置文件中添加或删除,它的存在与否最好交由哨兵决定。需要注意的是,该指令不应该写死在 “子文件” 中,因为子文件中写死的指令是无法被哨兵移除的,这将导致 SLAVE 每次重启后都是 SLAVE。这个问题很难排查。

    Redis M/S + Keepalived

    M/S + Keepalived 是一个非常经典的 Redis 高可用方案,是哨兵模式出现之前的主流方案。现在常见与双节点的 Redis 高可用需求场景(哨兵模式需要三节点)。此方案使用了 Redis 原生的主从复制机制结合 Keepalived 的 VRRP 技术:Redis M/S 提供数据持久化和备份策略,Keepalived 提供了健康检查、监控告警、故障切换以及统一的 VIP 访问接口

    优点

    • 高可靠性。双机主备架构、数据持久化以及备份策略。
    • 秒级切换。
    • 故障切换对应用透明。
    • 部署简单,维护成本低。

    缺点

    • Redis 主从切换需要自定义脚本实现。
    • Keepalived 存在主从脑裂风险。

    故障的 3 种情况

    1. Keepalived 挂了,同时 Redis 也挂了,这样的话 VIP 飘走之后,是不需要进行 Redis 数据同步的,因为 Redis 已经挂了,你也无法去 MASTER 上同步,会损失已经写在 MASTER 上但还没同步到 SLAVE 上面的这部分数据。

    2. Keepalived 挂了,Redis 没挂,这时 VIP 飘走后,Redis 的 MASTER/SLAVE 还是老的对应关系。默认情况下,会把数据写入 Redis SLAVE 中,而不会同步到 MASTER 上去,这时就要借助监控脚本反转 Redis 的身份关系了。并且需要预留一点时间里进行数据同步,然后切换主从关系。

    3. Keepalived 没挂,Redis 挂了,这时根据监控脚本检测到 Redis 挂了,马上降低 Keepalived Master 的优先级,导致 VIP 飘走,情况和第二种一样,也是需要进行数据同步,然后 Redis 主从切换。

    主节点配置 keepalived.conf

    ! Configuration File for keepalived
    
    global_defs {
       notification_email {
         acassen@firewall.loc
         failover@firewall.loc
         sysadmin@firewall.loc
       }
       notification_email_from Alexandre.Cassen@firewall.loc
       smtp_server 192.168.200.1
       smtp_connect_timeout 30
       router_id redis01
    }
    
    vrrp_script chk_redis {
        script "/etc/keepalived/script/redis_check.sh"
        interval 2
    }
    
    vrrp_instance VI_1 {
        state MASTER
        interface eno16777984
        virtual_router_id 51
        priority 100
        advert_int 1
        authentication {
            auth_type PASS
            auth_pass 1111
        }
    
        track_script {
            chk_redis
        }
        virtual_ipaddress {
            172.16.81.139
        }
        
        notify_master /etc/keepalived/script/redis_master.sh
        notify_backup /etc/keepalived/script/redis_backup.sh
        notify_fault  /etc/keepalived/script/redis_fault.sh  
        notify_stop   /etc/keepalived/script/redis_stop.sh
    }
    • notify_master:Keepalived 切换为主节点时执行的脚本。
    • notify_backup:Keepalived 切换为从节点时执行的脚本。
    • notify_fault:Keepalived 进程故障时执行的脚本。
    • notify_stop:keepalived 进程停止前执行的脚本。
    • nopreempt:设置不抢占,这里只能设置在 state 为 backup 的节点上,而且这个节点的优先级必须别另外的高。

    从节点配置 keepalived.conf

    ! Configuration File for keepalived
    
    global_defs {
       notification_email {
         acassen@firewall.loc
         failover@firewall.loc
         sysadmin@firewall.loc
       }
       notification_email_from Alexandre.Cassen@firewall.loc
       smtp_server 192.168.200.1
       smtp_connect_timeout 30
       router_id redis02
    }
    
    vrrp_script chk_redis {
        script "/etc/keepalived/script/redis_check.sh"
        interval 2
    }
    
    vrrp_instance VI_1 {
        state BACKUP
        interface eno16777984
        virtual_router_id 51
        priority 99
        advert_int 1
        authentication {
            auth_type PASS
            auth_pass 1111
        }
    
        track_script {
            chk_redis
        }
        virtual_ipaddress {
            172.16.81.139
        }
        
        notify_master /etc/keepalived/script/redis_master.sh
        notify_backup /etc/keepalived/script/redis_backup.sh
        notify_fault  /etc/keepalived/script/redis_fault.sh  
        notify_stop  /etc/keepalived/script/redis_stop.sh
    }

    redis_check.sh

    #!/bin/bash
    
    CHECK=`/usr/local/bin/redis-cli PING`
    if [ "$CHECK" == "PONG" ] ;then
          echo $CHECK
          exit 0
    else 
          echo $CHECK
          service keepalived stop # 确保让出 MASTER
          exit 1
    fi
    

    keepalived 会根据该监控脚本的返回码来调整优先级:

    • 如果脚本返回码为 0,并且 weight > 0,则优先级相应的增加;
    • 如果脚本返回码为非 0,并且 weight < 0,则优先级相应的减少;
    • 其他情况,原本配置的优先级不变,即配置文件中 priority 对应的值。

    NOTE:

    • 优先级不会不断的提高或者降低;
    • 可以编写多个检测脚本并为每个检测脚本设置不同的 weight(在配置中列出就行);
    • 不管提高优先级还是降低优先级,最终优先级的范围是在 [1, 254],不会出现优先级小于等于 0 或者优先级大于等于 255 的情况;
    • 配置 nopreempt ,避免正常情况下做无谓的切换

    redis_master.sh

    #!/bin/bash
    REDISCLI="/usr/local/redis/bin/redis-cli -a 123456"
    LOGFILE="/var/log/keepalived-redis-state.log"
    
    sleep 15
    
    echo "[master]" >> $LOGFILE
    date >> $LOGFILE
    echo "Being master...." >>$LOGFILE 2>&1
    echo "Run SLAVEOF cmd ...">> $LOGFILE
    
    $REDISCLI SLAVEOF 172.16.81.141 6379 >>$LOGFILE  2>&1 # 先同步数据
    if [ $? -ne 0 ];then
        echo "data rsync fail." >>$LOGFILE 2>&1
    else
        echo "data rsync OK." >> $LOGFILE  2>&1
    fi
    
    sleep 10 # 延迟 10 秒以后待数据同步完成后再取消同步状态 
    
    echo "Run SLAVEOF NO ONE cmd ...">> $LOGFILE
    
    $REDISCLI SLAVEOF NO ONE >> $LOGFILE 2>&1            # 切换为 MASTER
    if [ $? -ne 0 ];then
        echo "Run SLAVEOF NO ONE cmd fail." >>$LOGFILE 2>&1
    else
        echo "Run SLAVEOF NO ONE cmd OK." >> $LOGFILE  2>&1
    fi
    

    redis_backup.sh

    #!/bin/bash 
    REDISCLI="/usr/local/redis/bin/redis-cli -a 123456"
    LOGFILE="/var/log/keepalived-redis-state.log"
    
    echo "[backup]" >> $LOGFILE
    date >> $LOGFILE
    
    echo "Being slave...." >>$LOGFILE 2>&1
    sleep 15 # 延迟 15 秒待数据被对方同步完成之后再切换主从角色 
    echo "Run SLAVEOF cmd ...">> $LOGFILE
    
    $REDISCLI SLAVEOF 172.16.81.141 6379 >>$LOGFILE  2>&1    # 切换为 BACKUP
    

    redis_fault.sh

    #!/bin/bash 
    
    LOGFILE=/var/log/keepalived-redis-state.log
    
    echo "[fault]" >> $LOGFILE
    date >> $LOGFILE
    

    redis_stop.sh

    #!/bin/bash 
    
    LOGFILE=/var/log/keepalived-redis-state.log
    
    echo "[stop]" >> $LOGFILE
    date >> $LOGFILE
    

    相关阅读:

  • 相关阅读:
    http编程中的get和post混合使用方式
    SQLServer实现作业依赖(非步骤)
    SQLServer实现两个库的字段长度自动更新
    Python+SQLite数据库实现服务端高并发写入
    sqlite数据库相关使用
    sqlite语法
    VBA关键字总结
    VS2005 .net2.0 TreeView.设置SelectedNodeStyle控制TreeView中选定节点的外观的
    SQLSERVER 2005 如何给sa用户设置空密码?
    解决超过远程连接数而无法连接服务器的问题 踢出已断开用户
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13309220.html
Copyright © 2020-2023  润新知