热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

使用keepalived实现redis的故障切换

线上的redis环境一直是单点,确实挺危险的。刚开始想用redis的Sentinel来做,可看了半天发现这东西也不靠谱,还挺麻烦的样子,只能暂时抛弃,换成自己熟悉的keepalived来做。有了方案后剩下的就是查询相关资料了,google了一堆的资料,发现大家的做法普遍是

线上的redis环境一直是单点,确实挺危险的。刚开始想用redis的Sentinel来做,可看了半天发现这东西也不靠谱,还挺麻烦的样子,只能暂时抛弃,换成自己熟悉的keepalived来做。 有了方案后剩下的就是查询相关资料了,google了一堆的资料,发现大家的做法普遍是

线上的redis环境一直是单点,确实挺危险的。刚开始想用redis的Sentinel来做,可看了半天发现这东西也不靠谱,还挺麻烦的样子,只能暂时抛弃,换成自己熟悉的keepalived来做。

有了方案后剩下的就是查询相关资料了,google了一堆的资料,发现大家的做法普遍是:

场景:CentOS A —>Keepalived Master && Redis Master
     CentOS B—>Keepalived Backup && Redis Backup
1.正常情况CentOS A 提供服务, CentOS B 备份
2.如果CentOS A 挂了,CentOS B 升级为Master;
     CentOS A 恢复正常后,CentOS A 抢占为 Master,CentOS B 降级为 Slave.
3.如果CentOS A 挂,重复上述过程;如果 CentOS B 挂,请自行修复CentOS B.
  可以看到上述步骤2中,一个是当CentOS A 挂了再起来后再次抢占为Master,会照成业务的再次切换,影响线上业务的稳定性;另一个就是可以看到网上大家的做法一般是当CentOS A恢复抢占为Master后,为了保持Redis 主从数据的一致性,会让CentOS A 的 Redis 变成从先跟 CentOS B的Redis 同步数据,同步了一个固定时间后,然后把CentOS A的Redis 变为主,同样CentOS B的Redis 也是先sleep了某一个固定时间等待CentOS A 同步完成后,再变为从,其他状态变化参考这个过程,不多讲。看完后,我深深的震撼了,我靠,这些哥们确信自己真的能精确的控制这些过程吗?脚本中sleep的时间真的可以定下来吗?最简单的Redis 主从复制,主挂了,立即切换到从,都不能保证数据完整(主的数据还没来得及进行持久化,保存到硬盘),加了个keepalived就行了吗?

我的观点是搞运维的要尽量把架构搞的简单点,不要给自己挖坑,过于复杂的架构不仅难维护,而且容易出问题,出了问题还要背黑锅。所以简单的梳理了下我的需求和我想要得到的效果:

场景:
        CentOS A —>Keepalived Master && Redis Master
        CentOS B—>Keepalived Backup && Redis Backup
1.正常情况CentOS A 提供服务,CentOS B  备份
2.如果CentOS A 挂了,CentOS B 升级为Master;
  CentOS A 恢复正常后, 不抢占,变为slave.
3.如果CentOS B挂了,CentOS A 再升级为主

大家知道keepalived会有四种状态的变化,每种状态变化时,都可以调用一个脚本,如下:

当进入Master状态时会呼叫notify_master
当进入Backup状态时会呼叫notify_backup
当发现异常情况时进入Fault状态呼叫notify_fault
当Keepalived程序终止时则呼叫notify_stop
    进入Master和Backup这两种状态很容易理解了,就是分别变为主和从;进入Fault这个稍微要注意下,这个是什么意思呢,简单的说就是keepalived发现自己有问题了,既然有问题那他就不可能再去参与Master竞选了,你得修好他才行,进入这种状态一般是keepalived自身出问题了,或者keepalived检测的网卡出问题不通了,再或者就是我们自己写的检测业务的脚本返回错误;进入Stop 这个就容易理解了,执行/etc/init.d/keepalived stop 就会进入这个状态。
    我想要的是上诉步骤2中,当CentOS A 挂了后(Keepalived检测的网卡出问题或者Redis挂了),CentOS A 上的Keepalived也立即stop了,这样一个是流程清晰,另一个是可以避免“脑裂”情况的发生,当然了事后要手动把CentOS A 上的Redis 和 Keepalived 起来;同理上诉步骤3中CentOS B挂了后,也是立即停止Keepalived进程,事后手动启动CentOS B的Redis 和 Keepalived。

这么做有什么好处呢,那就是流程清晰,易于理解,便于维护。让我们梳理下这个流程,看看有多简单:

1.正常情况CentOS A 为Master提供服务,CentOS B 为 Slave 备份。
2.然后出问题了,那就是CentOS A上的 Redis  挂了,Keepalived检测到 Redis挂了后,就进入 Fault状态,调用redis_fault.sh脚本关闭Keepalived ,接着Keepalived进入stop状态了;此时CentOS B升级为Master,调用redis_master.sh脚本将Redis升级为主。
3.然后上去修复CentOS A,启动Redis、Keepalived,由于设置不抢占,这个时候CentOS A的状态就只能是Slave了,调用redis_backup.sh脚本把Redis变为从,跟CentOS B上的Redis同步数据;这个时候CentOS B 继续是主,当然Redis 也是主。
4.天有不测风云啊,CentOS A 刚修好,CentOS B的Redis 又挂了。这时只剩CentOS A 了,义不容辞的也就成为Master了,调用redis_master.sh脚本,将自己的Redis升级为主;CentOS B的 Redis挂的时候,同样会进入Fault状态,调用redis_fault.sh脚本关闭Keepalived ,接着Keepalived进入stop状态。
5.一把泪,只有登录CentOS B查看相关日志后,手动启动Redis、Keepalived。这个时候CentOS A 继续把持Master老大哥地位不放,所以状态不变;CentOS B就只能乖乖的进入Slave状态,调用redis_backup.sh脚本,把Redis搞成CentOS A 上Redis的从。
6.重复上述流程。

流程搞清楚了,剩下的就是Keepalived 配置 以及进入每种状态时呼叫的脚本了,不多说,这个就直接上配置吧.

主:

[root@puppet ~]# cat /etc/keepalived/keepalived.conf
#全局定义
global_defs {
   #运行keepalived的机器的一个标示
   router_id redis-ha
}
vrrp_script chk_redis {
    script "/etc/keepalived/scripts/redis_check.sh"
    interval 1
}
#vrrp实例配置
vrrp_instance VI_1 {
    state BACKUP
    nopreempt
    interface eth0
    virtual_router_id 58
    priority 180
    #检查间隔,默认1s
    advert_int 1
    #在切换到MASTER状态后,延迟进行gratuitous ARP 请求
    garp_master_delay 1
    authentication {
        auth_type PASS
        auth_pass KJj23576hYgu23IP
    }
    #设置额外的监控,里面的任意一个网卡出现问题,都会进入FAULT状态
    track_interface {
       eth0
    }
    track_script {
        chk_redis
    }
    virtual_ipaddress {
        192.168.188.132
    }
    notify_master /etc/keepalived/scripts/redis_master.sh
    notify_backup /etc/keepalived/scripts/redis_backup.sh
    notify_fault  /etc/keepalived/scripts/redis_fault.sh
    notify_stop   /etc/keepalived/scripts/redis_stop.sh
}
[root@puppet ~]# cat /etc/keepalived/scripts/redis_master.sh
#!/bin/bash
REDISCLI="/usr/bin/redis-cli"
LOGFILE="/var/log/keepalived-redis-state.log"
echo "[master]" >> $LOGFILE
date >> $LOGFILE
echo "Being master...." >> $LOGFILE 2>&1
echo "Run SLAVEOF NO ONE cmd ..." >> $LOGFILE
$REDISCLI SLAVEOF NO ONE >> $LOGFILE 2>&1
[root@puppet ~]# cat /etc/keepalived/scripts/redis_backup.sh
#!/bin/bash
REDISCLI="/usr/bin/redis-cli"
LOGFILE="/var/log/keepalived-redis-state.log"
echo "[backup]" >> $LOGFILE
date >> $LOGFILE
echo "Being slave...." >> $LOGFILE 2>&1
echo "Run SLAVEOF cmd ..." >> $LOGFILE
$REDISCLI SLAVEOF 192.168.188.131 6379 >> $LOGFILE  2>&1
[root@puppet ~]# cat /etc/keepalived/scripts/redis_fault.sh
#!/bin/bash
LOGFILE=/var/log/keepalived-redis-state.log
echo "[fault]" >> $LOGFILE
date >> $LOGFILE
/etc/init.d/keepalived stop
[root@puppet ~]# cat /etc/keepalived/scripts/redis_stop.sh
#!/bin/bash
LOGFILE=/var/log/keepalived-redis-state.log
echo "[stop]" >> $LOGFILE
date >> $LOGFILE

从:

[root@agent ~]# cat /etc/keepalived/keepalived.conf
#全局定义
global_defs {
   #运行keepalived的机器的一个标示
   router_id redis-ha
}
vrrp_script chk_redis {
    script "/etc/keepalived/scripts/redis_check.sh"
    interval 1
}
#vrrp实例配置
vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 58
    priority 120
    #检查间隔,默认1s
    advert_int 1
    #在切换到MASTER状态后,延迟进行gratuitous ARP 请求
    garp_master_delay 1
    authentication {
        auth_type PASS
        auth_pass KJj23576hYgu23IP
    }
    #设置额外的监控,里面的任意一个网卡出现问题,都会进入FAULT状态
    track_interface {
       eth0
    }
    track_script {
        chk_redis
    }
    virtual_ipaddress {
        192.168.188.132
    }
    notify_master /etc/keepalived/scripts/redis_master.sh
    notify_backup /etc/keepalived/scripts/redis_backup.sh
    notify_fault  /etc/keepalived/scripts/redis_fault.sh
    notify_stop   /etc/keepalived/scripts/redis_stop.sh
}
[root@agent ~]# cat /etc/keepalived/scripts/redis_master.sh
#!/bin/bash
REDISCLI="/usr/bin/redis-cli"
LOGFILE="/var/log/keepalived-redis-state.log"
echo "[master]" >> $LOGFILE
date >> $LOGFILE
echo "Being master...." >> $LOGFILE 2>&1
echo "Run SLAVEOF NO ONE cmd ..." >> $LOGFILE
$REDISCLI SLAVEOF NO ONE >> $LOGFILE 2>&1
[root@agent ~]# cat /etc/keepalived/scripts/redis_backup.sh
#!/bin/bash
REDISCLI="/usr/bin/redis-cli"
LOGFILE="/var/log/keepalived-redis-state.log"
echo "[backup]" >> $LOGFILE
date >> $LOGFILE
echo "Being slave...." >> $LOGFILE 2>&1
echo "Run SLAVEOF cmd ..." >> $LOGFILE
$REDISCLI SLAVEOF 192.168.188.130 6379 >> $LOGFILE  2>&1
[root@agent ~]# cat /etc/keepalived/scripts/redis_fault.sh
#!/bin/bash
LOGFILE=/var/log/keepalived-redis-state.log
echo "[fault]" >> $LOGFILE
date >> $LOGFILE
/etc/init.d/keepalived stop
[root@agent ~]# cat /etc/keepalived/scripts/redis_stop.sh
#!/bin/bash
LOGFILE=/var/log/keepalived-redis-state.log
echo "[stop]" >> $LOGFILE
date >> $LOGFILE

注意:

    从配置里可以看到这种方案有一个坑,就是 CentOS A 上的Keepalived刚启动的时候,状态切换会先切换到Backup,然后参加竞选后才会到Master。这样就会出现问题,当切换到Backup时,会执行redis_backup.sh,跟 CentOS B 的Redis 同步数据,如果这时CentOS B上的Redis数据不全或者为空,悲剧就发生了,这肯定不是我想要的结果了。
    所以正常的启动流程是:先启动CentOS  A 的 Redis,接着启动Keepalived;然后等个几秒,启动 CentOS B 的Redis,接着启动Redis。

说了这么多,不做测试,一切都是白搭,下面让我们跑下测试流程,看看日志结果:

1.分别启动CentOS A的Redis、Keepalived(注意顺序)
[root@puppet ~]# /etc/init.d/redis start
启动 :                                                    [确定]
[root@puppet ~]# /etc/init.d/keepalived start
正在启动 keepalived:                                      [确定]
查看日志输出如下:
[root@puppet ~]# tail -f /var/log/keepalived-redis-state.log
[backup]
2014年 11月 18日 星期二 12:48:51 CST
Being slave....
Run SLAVEOF cmd ...
OK
[master]
2014年 11月 18日 星期二 12:48:55 CST
Being master....
Run SLAVEOF NO ONE cmd ...
OK
可以看到先进入Backup状态,然后后成为Master。这里由于CentOS B上的Redis、Keepalived还没有启动,所以在CentOS A 进入Backup状态时,执行redis_backup.sh,并不会连接上CentOS B的Redis执行数据同步,因此可以放心CentOS A 上Redis 数据的完整性。
接着,启动CentOS B的Redis 、Keepalived.
[root@agent ~]# /etc/init.d/redis status
redis-server 已停
[root@agent ~]# /etc/init.d/redis start
启动 :                                                    [确定]
You have new mail in /var/spool/mail/root
[root@agent ~]# /etc/init.d/keepalived start
正在启动 keepalived:                                      [确定]
[root@agent ~]# tail -f /var/log/keepalived-redis-state.log
[backup]
2014年 11月 18日 星期二 12:14:37 CST
Being slave....
Run SLAVEOF cmd ...
OK
可以看到CentOS B的Redis已经起来,并且已经成为CentOS A Redis的从了。
2.模拟故障,CentOS A的 Redis down 了
[root@puppet ~]# /etc/init.d/redis stop
停止 redis-server:                                        [确定]
可以看到CentOS A的Keepalived先进入fault,接着进入stop状态
[root@puppet ~]# tail -f /var/log/keepalived-redis-state.log
[fault]
2014年 11月 18日 星期二 12:57:44 CST
[stop]
2014年 11月 18日 星期二 12:57:44 CST
CentOS B 升级为Master,同时Redis也变为主
[root@agent ~]# tail -f /var/log/keepalived-redis-state.log
[master]
2014年 11月 18日 星期二 12:16:44 CST
Being master....
Run SLAVEOF NO ONE cmd ...
OK
3.再手动启动CentOS A的Redis 、Keepalived,CentOS A的Redis变为CentOS B Redis的从,并不会进行抢占
[root@puppet ~]# /etc/init.d/redis start
启动 :                                                    [确定]
[root@puppet ~]# /etc/init.d/keepalived start
正在启动 keepalived:                                      [确定]
[root@puppet ~]# tail -f /var/log/keepalived-redis-state.log
[backup]
2014年 11月 18日 星期二 13:00:06 CST
Being slave....
Run SLAVEOF cmd ...
OK
4.这个时候CentOS B 上的Redis 挂 了,CentOS A立即升级为Master,并且Redis也变为 Master角色
[root@agent ~]# /etc/init.d/redis stop
停止 redis-server:                                        [确定]
You have new mail in /var/spool/mail/root
CentOS B的Keepalived先进入fault,接着进入stop状态
[root@agent ~]# tail -f /var/log/keepalived-redis-state.log
[fault]
2014年 11月 18日 星期二 13:02:07 CST
[stop]
2014年 11月 18日 星期二 13:02:07 CST
CentOS A 升级为Master,同时Redis变为主
[root@puppet ~]# tail -f /var/log/keepalived-redis-state.log
[master]
2014年 11月 18日 星期二 13:02:08 CST
Being master....
Run SLAVEOF NO ONE cmd ...
OK
5.再启动CentOS B 的Redis、Keepalived,启动后Redis变为从跟CentOS  A 的 Redis 同步;Cent OS  A 的状态不变
[root@agent ~]# /etc/init.d/redis start
启动 :                                                    [确定]
[root@agent ~]# /etc/init.d/keepalived start
正在启动 keepalived:                                      [确定]
[root@agent ~]# tail -f /var/log/keepalived-redis-state.log
[backup]
2014年 11月 18日 星期二 13:04:45 CST
Being slave....
Run SLAVEOF cmd ...
OK
6.如果再有故障,重复上面的过程。

自言自语:

Keepalived中可以玩的地方还是挺多的,例如心跳时间、自定义检测脚本等等,我这里为了维护的方便只写出最简单的配置,具体可以参见官网文档;同时Redis主从的切换过程也可以加入报警功能,例如我就在redis_master.sh中加入了短信报警的功能,这样可以及时知道那一台Redis出问题了。这次写博客没有大片大片的写安装文档、没有写配置流程,真是个奇迹,不过预计以后写博客的时间也是越来越少了,各种忙碌中...

参考资料:

郭东:http://heylinux.com/archives/1942.html

oschina: http://my.oschina.net/guol/blog/182491

其他:《Keepalived 权威指南》

推荐阅读
  • 本文介绍了Linux系统中正则表达式的基础知识,包括正则表达式的简介、字符分类、普通字符和元字符的区别,以及在学习过程中需要注意的事项。同时提醒读者要注意正则表达式与通配符的区别,并给出了使用正则表达式时的一些建议。本文适合初学者了解Linux系统中的正则表达式,并提供了学习的参考资料。 ... [详细]
  • Ubuntu 9.04中安装谷歌Chromium浏览器及使用体验[图文]
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 成功安装Sabayon Linux在thinkpad X60上的经验分享
    本文分享了作者在国庆期间在thinkpad X60上成功安装Sabayon Linux的经验。通过修改CHOST和执行emerge命令,作者顺利完成了安装过程。Sabayon Linux是一个基于Gentoo Linux的发行版,可以将电脑快速转变为一个功能强大的系统。除了作为一个live DVD使用外,Sabayon Linux还可以被安装在硬盘上,方便用户使用。 ... [详细]
  • CEPH LIO iSCSI Gateway及其使用参考文档
    本文介绍了CEPH LIO iSCSI Gateway以及使用该网关的参考文档,包括Ceph Block Device、CEPH ISCSI GATEWAY、USING AN ISCSI GATEWAY等。同时提供了多个参考链接,详细介绍了CEPH LIO iSCSI Gateway的配置和使用方法。 ... [详细]
  • 本文介绍了在无法联网的情况下,通过下载rpm包离线安装zip和unzip的方法。详细介绍了如何搜索并下载合适的rpm包,以及如何使用rpm命令进行安装。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 本文介绍了使用CentOS7.0 U盘刻录工具进行安装的详细步骤,包括使用USBWriter工具刻录ISO文件到USB驱动器、格式化USB磁盘、设置启动顺序等。通过本文的指导,用户可以轻松地使用U盘安装CentOS7.0操作系统。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 【shell】网络处理:判断IP是否在网段、两个ip是否同网段、IP地址范围、网段包含关系
    本文介绍了使用shell脚本判断IP是否在同一网段、判断IP地址是否在某个范围内、计算IP地址范围、判断网段之间的包含关系的方法和原理。通过对IP和掩码进行与计算,可以判断两个IP是否在同一网段。同时,还提供了一段用于验证IP地址的正则表达式和判断特殊IP地址的方法。 ... [详细]
  • centos安装Mysql的方法及步骤详解
    本文介绍了centos安装Mysql的两种方式:rpm方式和绿色方式安装,详细介绍了安装所需的软件包以及安装过程中的注意事项,包括检查是否安装成功的方法。通过本文,读者可以了解到在centos系统上如何正确安装Mysql。 ... [详细]
  • CentOS7.8下编译muduo库找不到Boost库报错的解决方法
    本文介绍了在CentOS7.8下编译muduo库时出现找不到Boost库报错的问题,并提供了解决方法。文章详细介绍了从Github上下载muduo和muduo-tutorial源代码的步骤,并指导如何编译muduo库。最后,作者提供了陈硕老师的Github链接和muduo库的简介。 ... [详细]
  • 本文介绍了iOS开发中检测和解决内存泄漏的方法,包括静态分析、使用instruments检查内存泄漏以及代码测试等。同时还介绍了最能挣钱的行业,包括互联网行业、娱乐行业、教育行业、智能行业和老年服务行业,并提供了选行业的技巧。 ... [详细]
  • 在Windows10系统上使用VMware创建CentOS虚拟机的详细步骤教程
    本文详细介绍了在Windows10系统上使用VMware创建CentOS虚拟机的步骤,包括准备条件、安装VMware、下载CentOS ISO文件、创建虚拟机并进行自定义配置、设置虚拟机的ISO与网络、进行安装和配置等。通过本文的指导,读者可以轻松地创建自己的CentOS虚拟机并进行相应的配置和操作。 ... [详细]
author-avatar
黑_猫__警_长
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有