一、详解keepalived配置和使用

keepalived使用

keepalived介绍

vrrp 协议的软件实现,原生设计目的为了高可用 ipvs服务

官网:Keepalived for Linux

功能:

  • 基于vrrp协议完成地址流动
  • 为vip地址所在的节点生成ipvs规则(在配置文件中预先定义)
  • 为ipvs集群的各RS做健康状态检测
  • 基于脚本调用接口完成脚本中定义的功能,进而影响集群事务,以此支持nginx、haproxy等服务

Keepalived 架构

官方文档:Keepalived User Guide — Keepalived 1.4.3 documentationKeepalived for Linux

  • 用户空间核心组件:
  1.        vrrp stack:VIP消息通告
  2.        checkers:监测real server
  3.        system call:实现 vrrp 协议状态转换时调用脚本的功能
  4.        SMTP:邮件组件
  5.        IPVS wrapper:生成IPVS规则
  6.        Netlink Reflector:网络接口
  7.        WatchDog:监控进程
  • 控制组件:提供keepalived.conf 的解析器,完成Keepalived配置
  • IO复用器:针对网络目的而优化的自己的线程抽象
  • 内存管理组件:为某些通用的内存管理功能(例如分配,重新分配,发布等)提供访问权限

环境准备

  • 各节点时间必须同步:ntp,chrony
  • 关闭防火墙及SELinux
  • 各节点之间可通过主机名互相通信:非必须
  • 建议使用/etc/hosts文件实现:非必须
  • 各节点之间的root用户可以基于密钥认证的ssh服务完成互相通信:非必须

keepalived配置

配置文件组成部分

配置文件:/etc/keepalived/keepalived.conf

配置文件组成部分:

  • GLOBAL CONFIGURATION

       Global definitions:定义邮件配置,route_id,vrrp配置,多播地址等

  • VRRP CONFIGURATION

       VRRP instance(s):定义每个vrrp虚拟路由器

  • LVS CONFIGURATION

       Virtual server group(s)

       Virtual server(s):LVS集群的VS和RS

配置文件语法

当生产环境复杂时, /etc/keepalived/keepalived.conf 文件中内容过多,不易管理,可以将不同集群的配置,比如:不同集群的VIP配置放在独立的子配置文件中,利用include 指令可以实现包含子配置文件

全局配置

global_defs {
   notification_email {
     root@localhost   #keepalived发生故障切换时邮件发送的目标邮箱,可以按行区分写多个
   }
   notification_email_from keepalived@localhost   #发邮件的地址
   smtp_server 127.0.0.1     #邮件服务器地址
   smtp_connect_timeout 30   #邮件服务器连接timeout
   router_id LVS_DEVEL       #每个keepalived主机唯一标识,建议使用当前主机名,但多节点重名不影响
   vrrp_skip_check_adv_addr  #对所有通告报文都检查,会比较消耗性能,启用此配置后,如果收到的通告报文和上一个报文是同一个路由器,则跳过检查,默认值为全检查
   vrrp_strict         #严格遵守VRRP协议,禁止以下状况:1.无VIP地址 2.配置了单播邻居 3.在VRRP版本2中有IPv6地址,开启动此项会自动开启iptables防火墙规则,建议关闭此项配置
   vrrp_garp_interval 0   #gratuitous ARP messages报文发送延迟,0表示不延迟
   vrrp_gna_interval 0    #unsolicited NA messages (不请自来)消息发送延迟
   vrrp_mcast_group4 224.0.0.18    #指定组播IP地址,默认值:224.0.0.18 范围:224.0.0.0到239.255.255.255
   vrrp_iptables    #此项和vrrp_strict同时开启时,则不会添加防火墙规则,如果无配置vrrp_strict项,则无需启用此项配置
}


include /etc/keepalived/conf.d/*.conf #将VRRP相关配置放在子配置文件中

配置虚拟路由器

vrrp_instance <STRING> { #<String>为vrrp的实例名,一般为业务名称
  配置参数
  ......
}

#配置参数:
state  MASTER|BACKUP     #当前节点在此虚拟路由器上的初始状态,状态为MASTER或者BACKUP
interface IFACE_NAME     #绑定为当前虚拟路由器使用的物理接口,如:eth0,bond0,br0,可以和VIP不在一个网卡
virtual_router_id VRID   #每个虚拟路由器惟一标识,范围:0-255,每个虚拟路由器此值必须唯一,否则服务无法启动,同属一个虚拟路由器的多个keepalived节点必须相同
priority 100       #当前物理节点在此虚拟路由器的优先级,范围:1-254,每个keepalived主机节点此值不同
advert_int 1       #vrrp通告的时间间隔,默认1s
authentication { #认证机制
  auth_type AH|PASS
  auth_pass <PASSWORD> #预共享密钥,仅前8位有效,同一个虚拟路由器的多个keepalived节点必须一样
}
virtual_ipaddress { #虚拟IP
    <IPADDR>/<MASK> brd <IPADDR> dev <STRING> scope <SCOPE> label <LABEL>
    192.168.200.100         #指定VIP,不指定网卡,默认为eth0,注意:不指定/prefix,默认为/32
    192.168.200.101/24 dev eth1                 #指定VIP的网卡
    192.168.200.102/24 dev eth2 label eth2:1    #指定VIP的网卡label 
}
track_interface { #配置监控网络接口,一旦出现故障,则转为FAULT状态实现地址转移
    eth0
    eth1
    …
}

启用keepalived日志功能

[root@node5 ~]# vim /etc/sysconfig/keepalived

KEEPALIVED_OPTIONS="-D -S 6"

[root@node5 ~]# vim /etc/rsyslog.conf 
local6.*                                                /var/log/keepalived.log

[root@node5 ~]# systemctl restart keepalived.service rsyslog.service
[root@node5 ~]# tail -f /var/log/keepalived.log

二、keeplived 结合nginx 实现高可用

keeplived+nginx节点1:172.20.21.170

keeplived+nginx节点2:172.20.21.175

后端web服务器1:172.20.22.11

后端web服务器2:172.20.22.12

#先准备好两台后端web服务器
[root@localhost ~]# yum install -y httpd
[root@localhost ~]# echo 'web1 172.20.22.11'
[root@localhost ~]# systemctl start httpd

#访问测试
[root@localhost ~]# curl 172.20.22.11
web1 172.20.22.11
[root@localhost ~]# curl 172.20.22.12
web2 172.20.22.12


#在两个节点都配置nginx反向代理
[root@node5 ~]# yum install -y nginx
[root@node5 ~]# vim /etc/nginx/nginx.conf
http {
    upstream websrvs {
        server 172.20.22.11 weight=1;
        server 172.20.22.12 weight=1;
    }
    server {
        listen 80;
        server_name www.a.com;
        location / {
            proxy_pass http://websrvs/;
        }
    }
}


#在两个节点都配置实现nginx反向代理高可用
[root@node5 ~]# cat /etc/keepalived/keepalived.conf
global_defs {
   notification_email {
     root@localhost
   }
   notification_email_from keepalived@localhost
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id node5               #另一个节点为node8
   vrrp_mcast_group4 224.20.0.18
}


vrrp_instance VI_1 {
    state MASTER                   #在另一个节点为BACKUP
    interface eth0
    virtual_router_id 65
    priority 100                   #在另一个节点为80
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass PbP2YKme
    }
    virtual_ipaddress {
        172.20.22.50/16 dev eth0 label eth0:0
    }
}


[root@node5 ~]# cat /etc/keepalived/keepalived.conf
[root@node5 ~]# systemctl start keepalived
[root@node5 ~]# ifconfig eth0:0
eth0:0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.20.22.50  netmask 255.255.0.0  broadcast 0.0.0.0
        ether 00:0c:29:47:bb:03  txqueuelen 1000  (Ethernet)


##访问172.20.22.50测试,当170的keepalived进程挂了之后vip:172.20.22.50会自动转移到175上,用户访问172.20.22.50不受影响。当170的keepalived进程恢复之后,170的设定的优先级比175的高,所以vip又会自动转移回170上。
[root@localhost ~]# while true;do curl http://172.20.22.50;sleep 1;done
web2 172.20.22.12
web2 172.20.22.12
web1 172.20.22.11
web2 172.20.22.12
web1 172.20.22.11

三、keepalived脑裂产生的原因以及解决的办法

keepalived脑裂产生的原因

脑裂(split-brain):指在一个高可用(HA)系统中,当联系着的两个节点断开联系时,本来为一个整体的系统,分裂为两个独立节点,这时两个节点开始争抢共享资源,结果会导致系统混乱,数据损坏。

一般来说裂脑的发生,有以下几种原因:

  1. 心跳线断开或连接心跳线的中间故障(交换机等);
  2. 设备故障,网卡及相关驱动存在问题;
  3. iptables防火墙阻挡IP或阻挡VRRP协议传输;
  4. virtual_router_id两端参数配置不一致;

keepalived脑裂解决办法

一般采用2个方法:

1、仲裁

  当两个节点出现分歧时,由第3方的仲裁者决定听谁的。这个仲裁者,可能是一个锁服务,一个共享盘或者其它什么东西。

2、fencing

  当不能确定某个节点的状态时,通过fencing把对方干掉,确保共享资源被完全释放,前提是必须要有可靠的fence设备

四、实现keeplived监控,通知

keepalived利用 VRRP Script 技术,可以调用外部的辅助脚本进行资源监控,并根据监控的结果实现优先动态调整,从而实现其它应用的高可用性功能

实现Keepalived 状态切换的通知脚本

#在所有keepalived节点配置如下
[root@node3 ~]# cat /etc/keepalived/notify.sh 
#!/bin/bash
#
contact='root@localhost'

notify() {
  local mailsubject="$(hostname) to be $1, vip floating"
  local mailbody="$(date +'%F %T'): vrrp transition, $(hostname) changed to be $1"
  echo "$mailbody" | mail -s "$mailsubject" $contact
}

case $1 in
master)
  systemctl start nginx
  notify master
  ;;
backup)
  systemctl start nginx
  notify backup
  ;;
fault)
  systemctl stop nginx
  notify fault
  ;;
*)
  echo "Usage: $(basename $0) {master|backup|fault}"
  exit 1
   ;;
esac


##配置示例
[root@node5 ~]# vim /etc/keepalived/keepalived.conf

vrrp_instance VI_1 {
......
    virtual_ipaddress {
        192.168.30.77/24 dev eth0 label eth0:0
    }

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

VRRP Script 配置

分两步实现:

1、定义脚本

  vrrp_script:自定义资源监控脚本,vrrp实例根据脚本返回值,公共定义,可被多个实例调用,定义在vrrp实例之外的独立配置块,一般放在global_defs设置块之后。

  通常此脚本用于监控指定应用的状态。一旦发现应用的状态异常,则触发对MASTER节点的权重减至低于SLAVE节点,从而实现 VIP 切换到 SLAVE 节点

2、调用脚本

  track_script:调用vrrp_script定义的脚本去监控资源,定义在实例之内,调用事先定义的vrrp_script

##定义VRRP script
vrrp_script <SCRIPT_NAME> {          #定义一个检测脚本,在global_defs 之外配置
    script <STRING>|<QUOTED-STRING>  #shell命令或脚本路径
    interval <INTEGER>               #间隔时间,单位为秒,默认1秒
    timeout <INTEGER>                #超时时间
    weight <INTEGER:-254..254>       #此值为负数,表示fall((脚本返回值为非0)时,会将此值与本节点权重相加可以降低本节点权重,如果是正数,表示 rise (脚本返回值为0)成功后,会将此值与本节点权重相加可以提高本节点权重,通常使用负值较多
    fall <INTEGER>                   #脚本连续监测成功后,把服务器从成功标记为失败的次数
    rise <INTEGER>                   #脚本连续监测成功后,把服务器从失败标记为成功的次数
    user USERNAME [GROUPNAME]        #执行监测脚本的用户或组 
    init_fall                        #设置默认标记为失败状态,监测成功之后再转换为成功状态
}


##调用VRRP script
vrrp_instance VI_1 {
    …
    track_script {
        chk_down
  }
}

实现HAProxy高可用

##在两个节点修改内核参数
[root@node5 ~]# vim /etc/sysctl.conf 
[root@node5 ~]# sysctl -p
net.ipv4.ip_nonlocal_bind = 1

#在两个节点先实现haproxy的配置
[root@node5 ~]# cat /etc/haproxy/haproxy.cfg
listen stats
  mode http
  bind 0.0.0.0:9999
  stats enable
  log global
  stats uri     /haproxy-status
  stats auth    haadmin:123456

listen  web_port
  bind 172.20.22.50:8899
  mode http
  log global
  server web1  172.20.22.11:80  check inter 3000 fall 2 rise 5
  server web2  172.20.22.12:80  check inter 3000 fall 2 rise 5
  
  
[root@node5 ~]# cat /etc/keepalived/keepalived.conf
global_defs {
   notification_email {
     root@localhost
   }
   notification_email_from keepalived@localhost
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id node5                 #在另一个节点为node8
   vrrp_mcast_group4 224.20.0.20
}

vrrp_script check_haproxy {        #定义脚本
    script "/etc/keepalived/chk_haproxy.sh"
    interval 1
    weight -30
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state MASTER                 #在另一个节点为BACKUP
    interface eth0
    virtual_router_id 65
    priority 100                 #在另一个节点为80
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass PbP2YKme
    }
    virtual_ipaddress {
        172.20.22.50/16 dev eth0 label eth0:0
    }
    track_script {
        check_haproxy            #调用上面定义的脚本
    } 
    notify_master "/etc/keepalived/notify.sh master"
    notify_backup "/etc/keepalived/notify.sh backup"
    notify_fault  "/etc/keepalived/notify.sh fault"
}


[root@node3 ~]# cat /etc/keepalived/notify.sh 
#!/bin/bash
#
contact='root@localhost'

notify() {
  local mailsubject="$(hostname) to be $1, vip floating"
  local mailbody="$(date +'%F %T'): vrrp transition, $(hostname) changed to be $1"
  echo "$mailbody" | mail -s "$mailsubject" $contact
}

case $1 in
master)
  systemctl start nginx
  notify master
  ;;
backup)
  systemctl start nginx
  notify backup
  ;;
fault)
  systemctl stop nginx
  notify fault
  ;;
*)
  echo "Usage: $(basename $0) {master|backup|fault}"
  exit 1
   ;;
esac


[root@node5 ~]# yum install -y psmisc
[root@node5 ~]# cat /etc/keepalived/chk_haproxy.sh 
#!/bin/bash
/usr/bin/killall -0 haproxy