Keepalived脑裂的解决和预防

描述

在实际生产环境中,我们可以从以下几个方面来防止裂脑问题的发生:

同时使用串行电缆和以太网电缆连接,同时用两条心跳线路,这样一条线路坏了,另一个还是好的,依然能传送心跳消息。

当检测到裂脑时强行关闭一个心跳节点(这个功能需特殊设备支持,如Stonith、feyce)。相当于备节点接收不到心跳消患,通过单独的线路发送关机命令关闭主节点的电源。

做好对裂脑的监控报警(如邮件及手机短信等或值班).在问题发生时人为第一时间介入仲裁,降低损失。例如,百度的监控报警短倍就有上行和下行的区别。报警消息发送到管理员手机上,管理员可以通过手机回复对应数字或简单的字符串操作返回给服务器.让服务器根据指令自动处理相应故障,这样解决故障的时间更短.

当然,在实施高可用方案时,要根据业务实际需求确定是否能容忍这样的损失。对于一般的网站常规业务.这个损失是可容忍的。

多节点集群中,可以通过增加仲裁的机制,确定谁该获得资源,这里面有几个参考的思路:

          1.增加一个仲裁机制。例如设置参考的IP,当心跳完全断开的时候,2个节点各自都ping一下参考的IP,不同则表明断点就出现在本段,这样就主动放弃竞争,让能够ping通参考IP的一端去接管服务。        

          2.通过第三方软件仲裁谁该获得资源,这个在阿里有类似的软件应用

启用磁盘锁。正在服务一方锁住共享磁盘,脑裂发生的时候,让对方完全抢不走共享的磁盘资源。但使用锁磁盘也会有一个不小的问题,如果占用共享盘的乙方不主动解锁,另一方就永远得不到共享磁盘。现实中介入服务节点突然死机或者崩溃,另一方就永远不可能执行解锁命令。后备节点也就截关不了共享的资源和应用服务。于是有人在HA中涉及了“智能”锁,正在服务的一方只在发现心跳线全部断开时才启用磁盘锁,平时就不上锁了

报警报在服务器接管之前,给人员处理留足够的时间就是1分钟内报警了,但是服务器不接管,而是5分钟之后接管,接管的时间较长。数据不会丢失,但就是会导致用户无法写数据。报警后,不直接自动服务器接管,而是由人员接管。 

检查处理脚本(供参考)

注:

1.脚本无法解决服务器主备之间因为防火墙,配置等产生的脑裂问题(安装配置时注意检查,keepalived服务正常运行后再加入检查处理脚本),脚本只能处理自身网络问题产生的脑裂,必须保证网关服务器稳定

2.备机获取vip有2种情况:A主机keepalived服务发生故障vip漂移(非脑裂);B主机通信异常vip漂移(脑裂)。

脚本使用机制(引入第三方仲裁),步骤:

1.查看是否可获取vip,未获取:判断keepalived服务运行状态,运行中不操作,未运行重新启动,结束脚本。

2.服务器周期性地ping一下网关(第三方仲裁ip地址),如果ping通:判断keepalived服务运行状态,运行中不操作,未运行重新启动,结束脚本;如果ping不通则认为自身有问题,累计次数达到阈值,关闭keepalived。

脚本执行方式:

1.借助keepalived提供的vrrp_script及track_script实现(脚本内keepalived服务如果被关闭,脚本将不在执行,重新启动不生效),配置:

 

...
vrrp_script check_local {
script "/usr/local/src/check_gateway.sh 192.168.1.198" #192.168.1.198 需要检查的vip
interval 10 #10秒一次
...
track_script {
check_local
}

 

 2.linux定时任务(查看 tailf /var/spool/mail/root)

crontab -e

 

#1分1次 延迟10秒实现(时间自定义) keepalived服务脑裂脚本
* * * * * sleep 10; bash /usr/local/src/check_gateway.sh 192.168.1.198

 

脚本内容(根据实际环境修改网关ip,是否重新启动keepalived服务):

vim /usr/local/src/check_gateway.sh

 

#!/bin/bash
export PATH=$PATH:/usr/sbin
#脑裂检查及控制:第三方仲裁机制,使用ping网关ip方式
#循环次数
CHECK_TIME=3
#虚拟ip
VIP=$1
#网关ip(根据实际环境修改)
GATEWAY=192.168.1.8
#本机网卡
eth=enp2s0
#服务器和网关通信状态  0=失败,1=成功
keepalived_communication_status=1
#是否获取vip状态 0=失败,1=成功
get_vip_status=1
#keepalived服务状态 0=未运行,1=运行中
keepalived_service_status=1
#服务状态运行中字符串
active_status_str='active (running)'
echo "开始执行脚本 check_gateway.sh $VIP;时间:"
date
#查看是否获取vip状态
function check_get_vip_status() {
  #通过ip add命令查看ip信息,搜索$VIP,统计行数,是否等于1
  if [ $(ip add | grep "$VIP" | wc -l) -eq 1 ]; then
    get_vip_status=1
  else
    get_vip_status=0
  fi
  return $get_vip_status
}
 
#检查通信状态
function check_keepalived_status() {
  #检测$VIP 能否ping通$GATEWAY:使用$eth网络设备(-I $eth),发送数据包5(-c 5),源地址$VIP询问目的地[vip] $GATEWAY [网关地址 公用参考ip](-s $VIP $GATEWAY) 日志不保存 >/dev/null 2>&1
  /sbin/arping -I $eth -c 5 -s $VIP $GATEWAY >/dev/null 2>&1
  #判断上一步执行结果 等于0成功
  if [ $? = 0 ]; then
    keepalived_communication_status=1
  else
    keepalived_communication_status=0
  fi
  return $keepalived_communication_status
}
 
#检查keepalived服务状态
function check_keepalived_service_status() {
  #通过systemctl status keepalived.service命令查看keepalived服务状态,搜索$active_status_str,统计行数,是否等于1
  if [ $(systemctl status keepalived.service | grep "$active_status_str" | wc -l) -eq 1 ]; then
    keepalived_service_status=1
  else
    keepalived_service_status=0
  fi
  return $keepalived_service_status
}
 
#循环执行
#判断$CHECK_TIME 不等于 0
while [ $CHECK_TIME -ne 0 ]; do
  #执行check_get_vip_status获取get_vip_status
  check_get_vip_status
  #未获取vip
  if [ $get_vip_status = 0 ]; then
    #修改CHECK_TIME值 结束循环
    CHECK_TIME=0
    #检查服务状态 执行check_keepalived_service_status获取keepalived_service_status
    if [ $keepalived_service_status = 0 ]; then
      echo "执行脚本 check_gateway.sh $VIP;启动keepalived服务"
      systemctl start keepalived.service
    fi
 
    echo "执行脚本 check_gateway.sh $VIP;执行结果:未获取vip,无需处理,脚本执行结束,时间:"
    date
    #正常运行程序并退出程序
    exit 0
  fi
  #$CHECK_TIME  = $CHECK_TIME-1
  let "CHECK_TIME -= 1"
  #执行check_keepalived_status获取keepalived_communication_status
  check_keepalived_status
  #判断 $keepalived_communication_status = 1 通信成功
  if [ $keepalived_communication_status = 1 ]; then
    #修改CHECK_TIME值 结束循环
    CHECK_TIME=0
    #检查服务状态 执行check_keepalived_service_status获取keepalived_service_status
    check_keepalived_service_status
    if [ $keepalived_service_status = 0 ]; then
      echo "执行脚本 check_gateway.sh $VIP;启动keepalived服务"
      systemctl start keepalived.service
    fi
 
    echo "执行脚本 check_gateway.sh $VIP;GATEWAY=$GATEWAY,执行结果:通信正常,无需处理,脚本执行结束,时间:"
    date
    #正常运行程序并退出程序
    exit 0
  fi
  #通信失败&&连续3次
  if [ $keepalived_communication_status -eq 0 ] && [ $CHECK_TIME -eq 0 ]; then
    #关闭keepalived
    echo "执行脚本 check_gateway.sh $VIP;关闭keepalived服务"
    systemctl stop keepalived.service
    echo "执行脚本 check_gateway.sh $VIP;GATEWAY=$GATEWAY,执行结果:通信失败&&连续3次 关闭keepalived,脚本执行结束,时间:"
    date
    #非正常运行程序并退出程序
    exit 1
  fi
  sleep 3
done

 

 

  
      审核编辑:彭静
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分