环境说明

	系统: 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 特点

  • Keepalived 是LVS 的扩展项目,因此它们之间具备良好的兼容性
  • 通过对服务器池对象的健康检查,实现对失效机器/服务的故障隔离
  • 实现LVS集群节点的健康检查中
  • 负载均衡器之间的失败切换fail over 是通过VRRPv2 ( Virtual Router Redundancy Protocol) stack 实现的, VRRP 当初被设计出来的目的就是为了解决静态路由器的单点故障问题
  • 为了更好的性能,我们通常会将整套系统内所有主机的iptables 都停用;
  • Keepalived 产生的VIP 就是我们整个系统对外的IP ,如果最外端的防火墙采用的是路由模式,那我们就映射此内网IP 为公网IP

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

2、配置参数详解

参数 /etc/keepalived/keepalived.conf 有三段: global_defs, vrrp_instance, virtual_server 分别说明

2.1、global_defs

global_defs {
   notification_email {     # 指定当keepalived出现问题时,发送邮件给哪些用户
     acassen@firewall.loc    # user1
     failover@firewall.loc
     sysadmin@firewall.loc
   }
   
   notification_email_from admin@example.com  # 发送邮件时,邮件的来源地址
   smtp_server 192.168.200.1  # smtp服务器地址  端口默认25
   smtp_connect_timeout 30   # 指定smtp服务器连接的超时时间。单位是秒
   # 从这往上是配置邮件信息的

   router_id LVS_DEVEL          # 用于标识本节点的名称
   vrrp_skip_check_adv_addr  # 默认是不跳过检查。检查收到的VRRP通告中的所有地址可能会比较耗时,设置此命令的意思是,如果通告与接收的上一个通告来自相同的master路由器,则不执行检查(跳过检查)。   
   vrrp_strict              # 严格执行VRRP协议规范,此模式不支持节点单播, 下列情况将会阻止启动Keepalived:1. 没有VIP地址。2. 单播邻居。3. 在VRRP版本2中有IPv6地址
   vrrp_garp_interval 0     # 接口发送ARP之间的延迟
   vrrp_gna_interval 0      # 在一个接口发送的两个免费ARP之间的延迟。可以精确到毫秒级。默认是0
}

# 组播地址
vrrp_mcast_group4 224.0.0.18 # 指定发送VRRP组播消息使用的IPV4组播地址。如不指定 默认是224.0.0.18
vrrp_mcast_group6 ff02::12   # 指定发送VRRP组播消息所使用的IPV6组播地址。默认是ff02::12

# 如果vrrp进程或check进程超时,可以用下面的4个选项。可以使处于BACKUP状态的VRRP实例变成MASTER状态,即使MASTER实例依然在运行。因为MASTER或BACKUP系统比较慢,不能及时处理VRRP数据包。
vrrp_priority <-20 -- 19>:   # 设置VRRP进程的优先级。
checker_priority <-20 -- 19>  # 设置checker进程的优先级。
vrrp_no_swap:                # vrrp进程不能够被交换。
checker_no_swap				  # checker进程不能够被交换。

2.2、VRRPD配置

在主配置中直接配置, VRRPD的配置包括如下子块

  1. vrrp_script
# 作用:添加一个周期性执行的脚本。脚本的退出状态码会被调用它的所有的VRRP Instance记录。
# 注意:至少有一个VRRP实例调用它并且优先级不能为0.优先级范围是1-254.
vrrp_script <SCRIPT_NAME> {
          ...
    }

# 选项说明:
    scrip "/path/to/somewhere"      # 指定要执行的脚本的路径。
    interval <INTEGER>              # 指定脚本执行的间隔。单位是秒。默认为1s。
    timeout <INTEGER>               # 指定在多少秒后,脚本被认为执行失败。
    weight <-254 --- 254>           # 调整优先级。默认为2.
    rise <INTEGER>                  # 执行成功多少次才认为是成功。
    fall <INTEGER>                  # 执行失败多少次才认为失败。
    user <USERNAME> [GROUPNAME]     # 运行脚本的用户和组。
    init_fail                       # 假设脚本初始状态是失败状态。

weight: 
    1. 如果脚本执行成功(退出状态码为0),weight大于0,则priority增加。
    2. 如果脚本执行失败(退出状态码为非0),weight小于0,则priority减少。
    3. 其他情况下,priority不变。
  1. vrrp_sync_group
# 作用:将所有相关的VRRP实例定义在一起,作为一个VRRP Group,如果组内的任意一个实例出现问题,都可以实现Failover。
 vrrp_sync_group VG_1 {
    group {
     inside_network     # vrrp instance name
     outside_network    # vrrp instance name
     ...
    }
    ...
}

2.3、vrrp_instance

# 语法:
vrrp_instance VI_1 {
    state MASTER            # 状态有两个 MASTER 主 | BACKUP 从  
    interface eth0          # 对外的网卡接口,ifconfig 或者ip addr show可查看
    virtual_router_id 51    # 虚拟路由id,每个节点设置必须一样,相同的ID为一组,范围是0-255
    priority 100            # 优先级
    advert_int 1            # 主往从发送多播消息的间隔时长, 通知间隔,实际部署时可以设置小一点,减少延时
    authentication {        # 认证信息
        auth_type PASS|AH  # 指定认证方式。PASS简单密码认证(推荐),AH:IPSEC认证(不推荐)
        auth_pass 1111     # 指定认证所使用的密码。最多8位
    }
    virtual_ipaddress {     # 虚拟ip 可以多个,但建议一个就OK了
        192.168.200.16
        192.168.200.17
        192.168.200.18
    }
}

nopreempt: # 设置为不抢占。默认是抢占的,当高优先级的机器恢复后,会抢占低优先级的机器成为MASTER,而不抢占,则允许低优先级的机器继续成为MASTER,即使高优先级的机器已经上线。如果要使用这个功能,则初始化状态必须为BACKUP。
preempt_delay:# 设置抢占延迟。单位是秒,范围是0---1000,默认是0.发现低优先级的MASTER后多少秒开始抢占。

# 说明:  如果username和groupname没有指定,则以默认的script_user所指定的用户和组。
1. notify_master /path/to_master.sh [username [groupname]]
    # 作用:当成为MASTER时,以指定的用户和组执行脚本。
2. notify_backup /path/to_backup.sh [username [groupname]]
    # 作用:当成为BACKUP时,以指定的用户和组执行脚本。
3. notify_fault "/path/fault.sh VG_1" [username [groupname]]
    # 作用:当该同步组Fault时,以指定的用户和组执行脚本。
4. notify /path/notify.sh [username [groupname]]
    # 作用:在任何状态都会以指定的用户和组执行脚本。
    # 说明:该脚本会在 notify_*脚本后执行。
    notify可以使用3个参数,如下:
    $1:可以是GROUP或INTANCE,表明后面是组还是实例。
    $2:组名或实例名。
    $3:转换后的目标状态。有:MASTER、BACKUP、FAULT。
5. notify_stop /path/to_master.sh [username [groupname]]
	# 作用: 当停止VRRP时执行的脚本
5. smtp_alert:当状态发生改变时,发送邮件。
6. global_tracking:所有的VRRP实例共享相同的tracking配置。

2.4、virtual_server

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

    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    # 重试连接间隔 单位 秒
        }
    }
}

sorry_server <IPADDR> <PORT>    # 添加一个备用服务器。当所有的RS都故障时。
sorry_server_inhibit            # 将inhibit_on_failure指令应用于sorry_server指令。
2.4.1、real_server
virtual_server VIP port {
	...
	real_server rip port {
	   ....
	}
}

weight <INT>         #给服务器指定权重。默认是1.
inhibit_on_failure   #当服务器健康检查失败时,将其weight设置为0,而不是从Virtual Server中移除。
notify_up <STRING>   #当服务器健康检查成功时,执行的脚本。
notify_down <STRING> #当服务器健康检查失败时,执行的脚本。
uthreshold <INT>     #到这台服务器的最大连接数。
lthreshold <INT>     #到这台服务器的最小连接数。

# 健康检查   三种 : HTTP_GET | SSL_GET | TCP_CHECK
HTTP_GET | SSL_GET {
    url {
        path <STRING>         # 指定要检查的URL的路径。如path / or path /mrtg2
        digest <STRING>       # 摘要。计算方式  genhash -s 172.17.100.1 -p 80 -u /index.html
        status_code <INT>     # 状态码。
    }
    # 这些参数都是通用的
    nb_get_retry <INT>        # get尝试次数。
    delay_before_retry <INT>  # 在尝试之前延迟多长时间。

    connect_ip <IP ADDRESS>   # 连接的IP地址。默认是real server的ip地址。
    connect_port <PORT>       # 连接的端口。默认是real server的端口。
    bindto <IP ADDRESS>       # 发起连接的接口的地址。
    bind_port <PORT>          # 发起连接的源端口。
    connect_timeout <INT>     # 连接超时时间。默认是5s。
    fwmark <INTEGER>          # 使用fwmark对所有出去的检查数据包进行标记。
    warmup <INT>              # 指定一个随机延迟,最大为N秒。可防止网络阻塞。如果为0,则关闭该功能。
} 

TCP_CHECK {
    connect_ip <IP ADDRESS>   # 连接的IP地址。默认是real server的ip地址。
    connect_port <PORT>       # 连接的端口。默认是real server的端口。
    bindto <IP ADDRESS>       # 发起连接的接口的地址。
    bind_port <PORT>          # 发起连接的源端口。
    connect_timeout <INT>     # 连接超时时间。默认是5s。
    fwmark <INTEGER>          # 使用fwmark对所有出去的检查数据包进行标记。
    warmup <INT>              # 指定一个随机延迟,最大为N秒。可防止网络阻塞。如果为0,则关闭该功能。
    retry <INIT>              # 重试次数。默认是1次。
    delay_before_retry <INT>  # 默认是1秒。在重试之前延迟多少秒。
}

# 两者的区别
real_server 192.168.9.31 999 {
    weight 1 
    HTTP_GET {
        url {
          path /index.html
          digest 3ee77b3918b70aa9b6afdad9f84c6606
        }
        connect_timeout 3   # HTTP SSL的通用参数在url外
        nb_get_retry 3
        delay_before_retry 3
    }
}

real_server 192.168.9.31 999 {
    weight 1
    TCP_CHECK {
       connect_ip 192.168.9.31  # TCP_CHECK在里
       connect_port 999
       nb_get_retry 3
       delay_before_retry 10
    }
}

默认配置说明

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 用法

三、keepalived-DR配置

服务器 真实地址 VIP 网关
keepalive-master + nginx 192.168.9.31 192.168.9.111 192.168.9.1
keepalive-slave + nginx 192.168.9.32 192.168.9.111 192.168.9.1

​ 客户端访问地址为虚拟地址192.168.9.111,客户端的请求到达keepalived后,会修改数据包的MAC地址然后发送给真实服务器,RS 由于也配有虚拟地址,所以会正常接收这个数据包,然后返回流量通过网关直接返回给用户,而非流经 LB 再走网关,所以负载均衡器的压力相对NAT模式大大减小。负载均衡器和HTTP服务器均只开放999端口。DR模式下开放的端口必须一致

​ 在 RS 上因为要设置lo网卡上的 Vip,并关闭arp响应(如果不关闭,那么所有使用vip的服务器就会造成IP冲突或者说是ARP欺骗

#!/usr/bin/env bash
#
SNS_VIP=192.168.9.111
#/etc/rc.d/init.d/functions
case "$1" in
start)
       ifconfig lo:0 $SNS_VIP netmask 255.255.255.255 broadcast $SNS_VIP
       /sbin/route add -host $SNS_VIP dev lo:0
       echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
       echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
       echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
       echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
       sysctl -p >/dev/null 2>&1
       echo "RealServer Start OK"
       ;;
stop)
       ifconfig lo:0 down
       route del $SNS_VIP >/dev/null 2>&1
       echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore
       echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce
       echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore
       echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce
       echo "RealServer Stoped"
       ;;
*)
       echo "Usage: $0 {start|stop}"
       exit 1
esac
exit 0

1、master配置

! 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 test_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 199  # 路由值需要一一对应
    priority 100     # 优先级
    advert_int 1     # 间隔时长
    authentication {
        auth_type PASS
        auth_pass 111111
    }
    virtual_ipaddress {
        192.168.9.111
    }
}

virtual_server 192.168.9.111 999 {
    delay_loop 6
    lb_algo rr
    lb_kind DR    # DR模型
    persistence_timeout 50
    protocol TCP

    real_server 192.168.9.31 999 {
        weight 1
        HTTP_GET {
            url {
              path /index.html
              digest 3ee77b3918b70aa9b6afdad9f84c6606
            }
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }
    }
    real_server 192.168.9.32 999 {
        weight 1
        HTTP_GET {
            url {
              path /index.html
              digest 5d39b29b0c64dc934531915ee13098a6
            }
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }
    }
}

获取 digest码

]# genhash -s 192.168.9.31 -p 999 -u /index.html
    MD5SUM = 3ee77b3918b70aa9b6afdad9f84c6606

2、slave配置

global_defs {
。。。。  # 与master一样
}

vrrp_instance VI_1 {
    state BACKUP      # 设置为 备节点
    interface ens160
    virtual_router_id 199
    priority 90       # 优先级需要比主低
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 111111
    }
    virtual_ipaddress {
        192.168.9.111
    }
}

检验

启动keepalived
keepalived]# tailf /var/log/messages
Feb 20 15:12:50 localhost systemd: Starting LVS and VRRP High Availability Monitor...
Feb 20 15:12:50 localhost Keepalived[2746]: Starting Keepalived v1.3.5 (03/19,2017), git commit v1.3.5-6-g6fa32f2
Feb 20 15:12:50 localhost Keepalived[2746]: Opening file '/etc/keepalived/keepalived.conf'.
Feb 20 15:12:50 localhost Keepalived[2747]: Starting Healthcheck child process, pid=2748
Feb 20 15:12:50 localhost systemd: Started LVS and VRRP High Availability Monitor.
Feb 20 15:12:50 localhost Keepalived[2747]: Starting VRRP child process, pid=2749
Feb 20 15:12:50 localhost Keepalived_healthcheckers[2748]: Opening file '/etc/keepalived/keepalived.conf'.
Feb 20 15:12:50 localhost Keepalived_vrrp[2749]: Registering Kernel netlink reflector
Feb 20 15:12:50 localhost Keepalived_vrrp[2749]: Registering Kernel netlink command channel
Feb 20 15:12:50 localhost Keepalived_vrrp[2749]: Registering gratuitous ARP shared channel
Feb 20 15:12:50 localhost Keepalived_vrrp[2749]: Opening file '/etc/keepalived/keepalived.conf'.
Feb 20 15:12:50 localhost Keepalived_vrrp[2749]: VRRP_Instance(VI_1) removing protocol VIPs.
Feb 20 15:12:50 localhost Keepalived_vrrp[2749]: Using LinkWatch kernel netlink reflector...
Feb 20 15:12:50 localhost Keepalived_vrrp[2749]: VRRP sockpool: [ifindex(2), proto(112), unicast(0), fd(10,11)]
Feb 20 15:12:50 localhost Keepalived_healthcheckers[2748]: Activating healthchecker for service [192.168.9.111]:999
Feb 20 15:12:50 localhost Keepalived_healthcheckers[2748]: Activating healthchecker for service [192.168.9.111]:999
Feb 20 15:12:51 localhost Keepalived_vrrp[2749]: VRRP_Instance(VI_1) Transition to MASTER STATE
Feb 20 15:12:52 localhost Keepalived_vrrp[2749]: VRRP_Instance(VI_1) Entering MASTER STATE
Feb 20 15:12:52 localhost Keepalived_vrrp[2749]: VRRP_Instance(VI_1) setting protocol VIPs.
Feb 20 15:12:52 localhost Keepalived_vrrp[2749]: Sending gratuitous ARP on ens160 for 192.168.9.111
Feb 20 15:12:52 localhost Keepalived_vrrp[2749]: VRRP_Instance(VI_1) Sending/queueing gratuitous ARPs on ens160 for 192.168.9.111

# 此时查看VIP已经正常绑定在网卡中了,  注意Lo也需要同时绑定并设置arp通各
keepalived]#  ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet 192.168.9.111/32 scope global lo:0
       valid_lft forever preferred_lft forever
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:0c:29:8b:06:8f brd ff:ff:ff:ff:ff:ff
    inet 192.168.9.31/24 brd 192.168.9.255 scope global noprefixroute ens160
       valid_lft forever preferred_lft forever
    inet 192.168.9.111/32 scope global ens160
       valid_lft forever preferred_lft forever
停止切换检验- 主到备
# 将主master stop 之后的日志,正常停止 并移除VIP地址
keepalived]# tailf /var/log/messages
Feb 20 15:17:44 localhost kernel: IPVS: __ip_vs_del_service: enter
Feb 20 15:17:44 localhost Keepalived[2747]: Stopping
Feb 20 15:17:44 localhost systemd: Stopping LVS and VRRP High Availability Monitor...
Feb 20 15:17:44 localhost Keepalived_healthcheckers[2748]: Removing service [192.168.9.31]:999 from VS [192.168.9.111]:999
Feb 20 15:17:44 localhost Keepalived_healthcheckers[2748]: Removing service [192.168.9.32]:999 from VS [192.168.9.111]:999
Feb 20 15:17:44 localhost Keepalived_vrrp[2749]: VRRP_Instance(VI_1) sent 0 priority
Feb 20 15:17:44 localhost Keepalived_vrrp[2749]: VRRP_Instance(VI_1) removing protocol VIPs.
Feb 20 15:17:44 localhost Keepalived_healthcheckers[2748]: Stopped
Feb 20 15:17:45 localhost Keepalived_vrrp[2749]: Stopped
Feb 20 15:17:45 localhost Keepalived[2747]: Stopped Keepalived v1.3.5 (03/19,2017), git commit v1.3.5-6-g6fa32f2
Feb 20 15:17:45 localhost systemd: Stopped LVS and VRRP High Availability Monitor.

# 此时在查看备slave
Feb 20 15:20:32 node2 Keepalived_vrrp[2081]: VRRP_Instance(VI_1) Transition to MASTER STATE
Feb 20 15:20:33 node2 Keepalived_vrrp[2081]: VRRP_Instance(VI_1) Entering MASTER STATE
Feb 20 15:20:33 node2 Keepalived_vrrp[2081]: VRRP_Instance(VI_1) setting protocol VIPs.
Feb 20 15:20:33 node2 Keepalived_vrrp[2081]: Sending gratuitous ARP on ens160 for 192.168.9.111
Feb 20 15:20:33 node2 Keepalived_vrrp[2081]: VRRP_Instance(VI_1) Sending/queueing gratuitous ARPs on ens160 for 192.168.9.111

# 在查看VIP地址是否绑定
 ~]# ip a      #注意lo接口地址以及arp通告,强调几次的重要性
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet 192.168.9.111/32 scope global lo:0
       valid_lft forever preferred_lft forever
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:0c:29:b3:94:b3 brd ff:ff:ff:ff:ff:ff
    inet 192.168.9.32/24 brd 192.168.9.255 scope global noprefixroute ens160
       valid_lft forever preferred_lft forever
    inet 192.168.9.111/32 scope global ens160
停止切换检验- 备到主
# 在将主节点服务正常启动,  此时当备节点监听到主节点正常启动会主动移除VIP
keepalived]# tailf /var/log/messages
Feb 20 15:22:30 node2 Keepalived_vrrp[2081]: VRRP_Instance(VI_1) Received advert with higher priority 100, ours 90
Feb 20 15:22:30 node2 Keepalived_vrrp[2081]: VRRP_Instance(VI_1) Entering BACKUP STATE
Feb 20 15:22:30 node2 Keepalived_vrrp[2081]: VRRP_Instance(VI_1) removing protocol VIPs.

# 在查看主节点日志
Feb 20 15:22:30 localhost Keepalived_vrrp[2827]: VRRP_Instance(VI_1) Transition to MASTER STATE
Feb 20 15:22:31 localhost Keepalived_vrrp[2827]: VRRP_Instance(VI_1) Entering MASTER STATE
Feb 20 15:22:31 localhost Keepalived_vrrp[2827]: VRRP_Instance(VI_1) setting protocol VIPs.
Feb 20 15:22:31 localhost Keepalived_vrrp[2827]: Sending gratuitous ARP on ens160 for 192.168.9.111
Feb 20 15:22:31 localhost Keepalived_vrrp[2827]: VRRP_Instance(VI_1) Sending/queueing gratuitous ARPs on ens160 for 192.168.9.111

# 在查看主节点的VIP 也正常启动
检验MD5
# 检验页面MD5值判断服务的可用性,模拟修改主页的内容
~]# tailf /var/local/messages
Feb 20 14:43:17 localhost Keepalived_healthcheckers[2308]: MD5 digest error to server [192.168.9.32]:999.
Feb 20 14:43:20 localhost Keepalived_healthcheckers[2308]: MD5 digest error to server [192.168.9.32]:999.

# 当检验多次失败之后会将该主机移出
Feb 20 15:08:17 node2 Keepalived_healthcheckers[1876]: Check on service [192.168.9.31]:999 failed after 3 retry.
Feb 20 15:08:17 node2 Keepalived_healthcheckers[1876]: Removing service [192.168.9.31]:999 from VS [192.168.9.111]:999

# 此时在将原页面修改为正常的值, 又将会正常加入到服务中
Feb 20 15:09:17 node2 Keepalived_healthcheckers[1876]: MD5 digest success to [192.168.9.31]:999 url(1).
Feb 20 15:09:17 node2 Keepalived_healthcheckers[1876]: Remote Web server [192.168.9.31]:999 succeed on service.
Feb 20 15:09:17 node2 Keepalived_healthcheckers[1876]: Adding service [192.168.9.31]:999 to VS [192.168.9.111]:999
nginx日志访问
 ~]# tailf /var/log/nginx/access.log  # 这里由  参数 delay_loop 6 控制
192.168.9.31 - - [20/Feb/2020:15:28:35 +0800] "GET /index.html HTTP/1.0" 200 13 "-" "KeepAliveClient" "-"
192.168.9.31 - - [20/Feb/2020:15:28:41 +0800] "GET /index.html HTTP/1.0" 200 13 "-" "KeepAliveClient" "-"
192.168.9.31 - - [20/Feb/2020:15:28:47 +0800] "GET /index.html HTTP/1.0" 200 13 "-" "KeepAliveClient" "-"
组播地址抓取
# 通过抓取 224.0.0.18 keepalived 默认组播地址,  可能看到 mstart每一秒会发送一个心跳包证明自己还存活
~]# tcpdump -i ens160 dst 224.0.0.18
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens160, link-type EN10MB (Ethernet), capture size 262144 bytes
15:29:57.432652 IP node1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 199, prio 100, authtype simple, intvl 1s, length 20
15:29:58.433249 IP node1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 199, prio 100, authtype simple, intvl 1s, length 20
15:29:59.433871 IP node1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 199, prio 100, authtype simple, intvl 1s, length 20

# 停止keepalived , 可以看到当停止之后它的优先级会直接定义为 0 ,直接下线
15:31:43.346157 IP node1 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 199, prio 0, authtype simple, intvl 1s, length 20

# 此时在启动keepalived, 通过组播地址就看到 这会是 node2在运行, 比对一下优先级 100大于90,此时就将vip抢过来了
15:34:23.164824 IP node2 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 199, prio 90, authtype simple, intvl 1s, length 20

四、脑裂问题

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

1、脑裂产生的原因

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

2、脑裂解决办法

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

3、脑裂解决方案

先附上脚本

 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

测试关闭 systemctl stop nginx

从节点,查看IP地址就自动切换过来了。

附一个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

keepalived 配置文件参数详解: https://blog.csdn.net/mofiu/article/details/76644012

参考文档 mysql 临时: https://mp.weixin.qq.com/s/loSjdQhdykIX7SVqEzn7rw

配置文件详解:https://blog.csdn.net/mofiu/article/details/76644012

健康检查: https://blog.csdn.net/yaoyaodexiaozhu/article/details/52067931

lvs+keepalived+nginx: https://www.jianshu.com/p/88589646aae8

Keepalived实现LVS(DR模式) : https://blog.csdn.net/lvshaorong/article/details/81238944