环境说明

    系统: centos 7.4
    软件: keepalived: 1.3.5  nginx: 1.10.2  tcpdump工具
    主机:192.168.9.222  192.168.9.223 
    vip地址: 192.168.9.151

keepalived说明

  Keepalived软件主要是通过VRRP协议实现高可用功能的。VRRP是Virtual Router RedundancyProtocol(虚拟路由器冗余协议)的缩写,VRRP出现的目的就是为了解决静态路由单点故障问题的,它能够保证当个别节点宕机时,整个网络可以不间断地运行。

1、keepalived服务的三个重要功能

  管理LVS负载均衡软件
  实现LVS集群节点的健康检查中
  作为系统网络服务的高可用性(failover)

1.2、Keepalived高可用故障转移原理

  keepalived高可用服务对主机之间的故障切换转移是通过vrrp (虚拟路由冗余协议)来实现的, 当keepalived主正常工作时,主节点会不停的备节点发送(多播)心跳信息证明还存活,当主web发送故障就无法发送心跳信息,这里keeplaived会将资源vip切换到备节点,但当主节点又活过来之后,备节点会释放自己的资源给主节点,恢复原来的角色。

1.3、keepalived工作原理

keepalived是通过vrrp协议进行通信的,我们首先需要先了解一下vrrp协议的信息
    1)vrrp 虚拟路由冗余协议,vrrp最早是为了解决路由单机故障而出现;
    2)vrrp是通过一种竟选协议机制来将路由任务交给某台vrrp rs的;
    3)vrrp是通过多播的方式实现高可用对之间通信;
    4)备节点可以有多个通过优先级竞选,但一般keepalived系统运维工作都是一对;避免竞争产生的问题;
    5)vrrp使用了加密协议加密数据,但keepalived官方目前还是推荐用明文的方式配置认证类型和密码

2、安装及配置

2.1、keepalived安装

# 安装keepalived   这里两台机器都需要安装   
# 实验基于 keepalived+nginx
Host# yum -y install keepalived libnl3-devel ipset-devel nginx 

# 查看安装的相关包
Host# rpm -ql keepalived
/etc/keepalived
/etc/keepalived/keepalived.conf
/etc/sysconfig/keepalived
/usr/bin/genhash
/usr/lib/systemd/system/keepalived.service
/usr/libexec/keepalived
/usr/sbin/keepalived

默认配置说明

Host # cat 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 LVS_DEVEL          # 用于标识本节点的名称
   vrrp_skip_check_adv_addr 默认是不跳过检查。检查收到的VRRP通告中的所有地址可能会比较耗时,设置此命令的意思是,如果通告与接收的上一个通告来自相同的master路由器,则不执行检查(跳过检查)。   
   vrrp_strict              #严格执行VRRP协议规范,此模式不支持节点单播
   vrrp_garp_interval 0     # 接口发送ARP之间的延迟
   vrrp_gna_interval 0      # 
}

vrrp_instance VI_1 {
    state MASTER            # 状态有两个 MASTER 主 | BACKUP 从  
    interface eth0          # 对外的网卡接口,ifconfig 或者ip addr show可查看
    virtual_router_id 51    # 虚拟路由id,每个节点设置必须一样,相同的ID为一组
    priority 100            # 优先级
    advert_int 1            # 主往从发送多播消息的间隔时长
    authentication {        # 认证信息
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {     # 虚拟ip 可以多个,但建议一个就OK了
        192.168.200.16
        192.168.200.17
        192.168.200.18
    }
}

virtual_server 192.168.200.100 443 {    # 虚拟服务器地址 IP 对外提供服务的端口
    delay_loop 6        # 健康检查时长 单位秒
    lb_algo rr          # 负载均衡算法 一般是 rr但 wlc 
    lb_kind NAT         # 负载均衡转发规则,一般用dr,nat调度器会有瓶颈问题
    persistence_timeout 50  # http服务会话时长 单位秒
    protocol TCP        # 协议 tcp

    real_server 192.168.201.100 443 {   # 真实的对外提供服务的地址跟IP
        weight 1    # 权重 权重越高转发优先级越高
        SSL_GET {        # HTTP_GET | SSL_GET | TCP_CHECK
            url {
                path /index.html
                digest e93e7f6cfbc7c343707f21e2f681dd31
            }
            connect_timeout 3   # 服务连接端口
            nb_get_retry 3      # 服务连接失败重试次数
            delay_before_retry 3    # 重试连接间隔 单位 秒
        }
    }
}

相关拓展:关于HTTP_GET | SSL_GET | TCP_CHECK 用法

2.2、配置

真实使用配置 
! 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 keepalived
   vrrp_skip_check_adv_addr
   vrrp_strict
   vrrp_garp_interval 0
   vrrp_gna_interval 0
}

vrrp_instance VI_1 {
    state MASTER
    interface ens160
    virtual_router_id 222
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 123456
    }
    virtual_ipaddress {
        192.168.9.151
    }
}

virtual_server 192.168.9.151 80 {
    delay_loop 60
    lb_algo rr
    lb_kind DR
    persistence_timeout 50
    protocol TCP

    real_server 192.168.9.222 8080 {       两台真实主机
        weight 1
        HTTP_GET {
            url {
              path /
              digest 0b03c354bbc6af44b42712a6f6497dc8
            }
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }
    }
    real_server 192.168.9.223 8080 {
        weight 1
        HTTP_GET {
            url {
              path /
              digest 78d47efe7fe7916ee20e034bfe24c5b7
            }
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }
    }
}

keepalived]# genhash -s 192.168.9.222 -p 8080 -u /index.html
MD5SUM = 0b03c354bbc6af44b42712a6f6497dc8

将这个获取到的值填到 digest xxxxxxxxxxxxxx中

Host# scp keepalived.conf root@192.168.9.223:/etc/keepalived/
备节点保需要更改一下接口地址、优先级、状态为BACKUP备节点 其它跟主节点保持一致
vrrp_instance VI_1 {
    state BACKUP
    interface ens160
    virtual_router_id 222
    priority 90

两边都启动nginx跟keepalived   systemctl start keepalive  systemctl start nginx

启动Keepalived 查看接口地址
keepalived]# ip addr show | grep en
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
    inet 192.168.9.222/24 brd 192.168.9.255 scope global ens160
    inet 192.168.9.151/32 scope global ens160

2.3、检查

此时这里可以看到主通过Http-get获取这个地址是否有效,
Host# tcpdump -i ens160 dst 192.168.9.223 and port 8080

keepalived实现服务高可用

此时我们在查看 nginx的日志,发现这里每秒也有一条记录在查询。
keepalived实现服务高可用

这里delay_loop 6 太短了 我们修改一下改成60秒一次
keepalived实现服务高可用

此时通过tcpdump可以抓取到主一直在往这个组播地址 每次一秒发送一个包

Host# tcpdump -i ens160 dst 224.0.0.18

keepalived实现服务高可用

3、脑裂问题

  当两台主机互相无法感知对方的存在,那么就会认为主已经挂掉, 此时通过自身的调用机制会将vip等资源都弄过来,这样当两个主机都同时认为自己是主,那么有可能会在某一时刻同时写给数据库会造成死锁的现象,也有可能会产生其它的资源争用,更主要的是两个一样的ip在同一个局域网内它们两都可能上不了网无法提供服务。

3.1、脑裂产生的原因

1)心跳线坏了
2)IP设置问题
3)心跳线之间的设备故障 (正常来说,两个keepalived是用网线直连的方式)
4)仲裁的机器出问题了
5)iptables的设置问题,端口没有放出来
6)心跳网卡设置的不对,软件bug等
7)同一个keepalived的 virtual_route_id两端设置参数不一样也会有脑裂问题的发生

3.2、脑裂解决办法

1)同时使用串行电缆和网线连接,同时使用两个心跳线路(这种方式最廉价,也是最有效的)
2)使用stonith,feyce设备检测当有设备脑裂时强制重启机器,或者断电(需要采购设备成本)
3)做好脑裂的解决,发现问题第一时间介入仲裁,但如果使用人工干预的方式的话 会造成一定的损失,如果可容忍那就可以采用这种方式(可以写脚本,但人工干预会很慢,比如凌晨4点半。)

3.3、脑裂解决方案

3.3.1、使用zabbix监控

  此处略过..待更新

3.3.2、脑裂脚本

先附上脚本 
 Host# vim check_nginx.sh 
#!/bin/bash
#
count=`ps -C nginx --no-heading | wc -l`
time=`date "+%Y-%m-%d %H-%M"`

if [ ${count} == 0 ];then

        echo "${time} -  keepalived fail " > /tmp/keepalive_status.txt
        /usr/bin/systemctl stop keepalived
else
        echo "${time} - keepalive ok" > /tmp/keepalive_status.txt
fi

# 这里是主节点 在vrrp_instance前面增加 有这个vrrp_script 那一定就会有 执行 track_script

vrrp_script chk_http_port {
        script "/etc/keepalived/check_nginx.sh"
        interval 1
        weight -15
        fall 3
}

vrrp_instance VI_1 {
    state MASTER
    interface ens160
    virtual_router_id 222
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 123456
    }
    virtual_ipaddress {
        192.168.9.151
    }

    track_script {
        chk_http_port
    }
}

#这里是从节点 配置信息一致

vrrp_script chk_http_port {
    script "/etc/keepalived/check_nginx.sh"
        interval 2
    weight -15
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens160
    virtual_router_id 222
    priority 90
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 123456
    }
    virtual_ipaddress {
        192.168.9.151
    }
    track_script {
        chk_http_port
    }

重启服务,检查日志 tail -f /var/log/messages
keepalived实现服务高可用

测试关闭 systemctl stop nginx
keepalived实现服务高可用

从节点,查看IP地址就自动切换过来了。
keepalived实现服务高可用

附一个V2版本脚本 
keepalived]# cat check_nginx.sh 
#!/bin/bash
#
# 检查nginx是否正常,如果不正常,先重启两次,最后还是不行就直接干掉keepalived
counts(){
    count=`ps -C nginx --no-heading | wc -l`
    for i in {1..2};do
        if [ ${count} == 0 ];then
            systemctl restart nginx
            sleep 2
        fi
    done 
}

counts
if [ counts == 0 ];then
    /usr/bin/systemctl stop keepalived
fi

3.3.3、邮件提醒

notify_master 当切换成主时发送邮件
notify_backup 切换成备时也发送邮件
notify_fault 失败时

notify脚本

#!/bin/bash
# 
# description: An example of notify script
# 
vip=172.16.100.1
contact='root@localhost'
notify() {
  mailsubject="`hostname` to be $1: $vip floating"
  mailbody="`date '+%F %H:%M:%S'`: vrrp transition, `hostname` changed to be $1"
  echo $mailbody | mail -s "$mailsubject" $contact
}
case "$1" in
  master)
    notify master
    /etc/rc.d/init.d/nginx start
    exit 0
  ;;
  backup)
    notify backup
    /etc/rc.d/init.d/nginx stop
    exit 0
  ;;
  fault)
    notify fault
    /etc/rc.d/init.d/nginx stop
    exit 0
  ;;
  *)
    echo 'Usage: `basename $0` {master|backup|fault}'
    exit 1
  ;;
esac

将这个加到 track_script 下边如
    track_script {
        chk_http_port
    }

  notify_master "/etc/keepalived/notify.sh master"  
  notify_backup "/etc/keepalived/notify.sh backup"  
  notify_fault "/etc/keepalived/notify.sh fault"  

FAQ

缺少这个组件: https://bugzilla.redhat.com/show_bug.cgi?id=1477572
libipset.so.3: cannot open shared object file: No such file or directory
解决: yum -y install libnl3-devel ipset-devel