欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

keepalive基础知识

程序员文章站 2022-04-26 11:18:15
...

一、LVS负载均衡集群的缺点

二、Keepalived介绍

三、Keepalived的功能

四、Keepalived工作原理

五、Keepalived组件框架

六、Keepalived的安装

  6.1 高可用集群配置的前提

  6.2 Keepalived配置

  6.3 Keepalived高可用其它服务

 

 

一、LVS负载均衡集群的缺点

 

在之前的一篇博客中介绍了有关LVS的工作原理,详情可参考《LVS工作原理》。LVS能够实现四层负载,能够支持足够大的并发量,但使用LVS负载均衡集群有以下两个缺点。

① 如果调度器(Director)挂了(不可用),将会导致整个系统不可用,从而调度器成为了单点故障(SPOF)。

② 调度器(Director)无法对后端RealServer做健康状态检测。因此,如果后端的某一台RealServer挂了,前端调度器将无法得知,仍然会向该RealServer调度请求,导致服务不可用;另外,如果某一台挂了的RealServer但已经恢复正常并重新提供服务时,前端调度器也无法得知,并将其加入调度队列。

因此,为了能够使前端调度器(Director)能够冗余、使Director能够对后端各RS做健康状态检测,并按需增删RS,需要引入高可用集群的解决方案。以下介绍的是Keepalived高可用软件。

 

 

二、Keepalived介绍

 

Keepalived是VRRP协议的实现,原生设计目的是高可用IPVS服务。此外,Keepalived能够根据配置文件的规则生成IPVS规则,并能够对各RealServer的健康状态进行检测。

 

Keepalived的高可用功能是通过VRRP协议实现的,VRRP是Virtual Router Redundancy Protocol(虚拟路由器冗余协议)的缩写。VRRP的出现是为了解决静态路由单点故障的问题,当某一节点出现故障时,可以防止导致整个网络不可用。Keepalived除了可以高可用LVS之外,还可以作为其他系统网络服务(Nginx、Haproxy等)的高可用解决方案(Keepalived可通过调用vrrp_script来高可用其它服务,并通过调用vrrp_track来追踪每一个服务)。

 

 

三、Keepalived的功能

 

Keepalived有三个重要功能,如下。

(1) 高可用系统网络服务。

Keepalived可以实现在两台或多台主机之间的故障切换转移。如果在两台主机中都安装了Keepalived,当正常工作时,有一台主机工作为Master角色,另一台主机工作为Backup角色。角色为Master的主机获得所有资源(VIP资源、服务资源)并向用户提供服务,角色为Backup的主机不提供服务而仅作为Master主机的热备。当角色为Master的主机出现故障时,角色为Backup的主机将自动接管Master主机的所有资源(VIP资源、服务资源)并开始工作。当Master主机故障修复完成时,将重新接管原来的资源和工作,而Backup主机则释放Master主机故障时它接管的资源和工作,各自恢复原来的角色。

(2) 实现对LVS集群中各RealServer的健康状态进行检测。

Keepalived可以通过在自身配置文件keepalived.conf中配置LVS集群服务中各台RealServer的IP地址和相关参数,并可以通过网络层、传输层和应用层这三层进行探测各RealServer的健康状态。当有一台或多台RealServer出现故障而无法提供服务时,Keepalived服务可以把出现故障的RealServer从LVS的正常转发队列中移除,保证不影响用户的访问。而当有RealServer故障修复完成时,Keepalived服务可以将其重新加入LVS的正常转发队列中,向用户提供服务。

(3) 管理LVS负载均衡软件。

Keepalived可以读取配置文件,并通过一个更为底层的接口来管理IPVS并生成IPVS规则,这使得LVS的使用更为方便。

 

 

四、Keepalived工作原理

 

前面提到,Keepalived的高可用功能是通过VRRP协议实现的,要了解Keepalived的工作原理,需要先了解VRRP协议的工作原理。

 

4.1 VRRP工作原理

 

VRRP,是Virtual Router Redundancy Protocol的缩写,中文名为虚拟路由器冗余协议。VRRP的出现是为了解决静态路由的单点故障问题,早期应用于交换机、路由器等设备中。VRRP协议是通过竞选机制来实现将路由任务交给某一台VRRP路由器的。

 

在一组VRRP路由器中,可以有多台物理路由器,但只有一台称为Master的路由器负责路由任务(响应ARP请求、转发发送给网关的数据包),而其他路由器都是Backup角色。Master和Backup是通过竞选机制选举出来的,这种竞选机制是根据优先级的高低来选举,在这一组VRRP路由器中优先级最高的路由器就是Master,其他路由器为Backup。Master路由器需要基于IP组播(默认组播地址为224.0.0.18)的方式发送心跳消息给其它的Backup路由器,告诉其它路由器自己还活着,同时告知Master自己的优先级。而当Master路由器发生故障时,就没法发送心跳消息,这样Backup路由器就检测不到Master的心跳消息了,这时多台Backup路由器就会再通过同样的竞选机制,选举出优先级最高的一台作为新的Master路由器,并接管路由任务(响应ARP请求、转发发送给网关的数据包)。

 

虚拟路由器由VRID(虚拟路由器标识,0-255),对外部则表现为一个VMAC(Virtual MAC)地址:00-00-5E-00-01-{VRID}和一个VIP(Virtual IP)。

 

4.2 Keepalived工作原理

 

Keepalived服务的高可用对之间的通信是基于VRRP协议进行通信的,VRRP是通过竞选机制来确定主、备节点的,主节点的优先级高于备节点。当Keepalived服务正常工作时,主Master节点接管IP资源和服务资源,向用户提供服务,并不断地向备Backup节点发送心跳消息(以组播的方式),用以告知备节点自己还活着,而备节点收到心跳消息后,一方面得知主节点还活着,另一方面得知主节点的优先级仍高于自己,因此不敢“犯上”。当主Master节点出现故障时,就无法发送心跳消息,所以备Backup节点就收不到主节点发来的心跳消息,这时所有备节点就会再此互相发送协议报文进行协商(再次进行竞选),各备节点将自己的优先级通告给所有其他备节点,最后优先级高的备节点则为新的主Master节点,由其调用自身的接管程序接管之前主节点的IP资源和服务资源,向用户提供服务。当先前的主Master节点故障修复完成时,则会再次接管IP资源和服务资源(如果Keepalived工作于抢占模式的话),继续向用户提供服务,而后来接管IP资源和服务资源的备Backup节点会释放接管的资源,再次成为备节点。

 

导致主、备节点切换的原因不仅仅是主节点发生了故障,也有可能是主Master节点被人为降低优先级(降到比备节点还低),这时备Backup节点会发现主节点的优先级比自己还低,同样会抢占资源并成为主节点。

 

前面提到,Keepalived主要由三个功能,此处介绍的原理只是其中之一,另外两个功能较为简单,其原理可以参考前面第三点。

 

 

五、Keepalived的组件框架

 

Keepalived的框架可以参考下图(图片来自Keepalived官网)。

keepalive基础知识

Keepalived的组件有控制组件(配置文件分析器)、内存管理、I/O复用和核心组件。其中,核心组件包括VRRP Stack、Checkers、IPVS wrapper、WatchDog和SMTP等,各部分功能如下。

VRRP Stack ==> 是VRRP协议的实现,能够实现Keepalived的高可用性。

Checkers ==> 能够基于网络层(IP)、传输层(TCP)、应用层(HTTP、SSL等)对RealServer进行健康状态检查。

IPVS Checkers ==> 根据配置文件生成IPVS规则并送往IPVS使之生效。

WatchDog ==> 用于监控VRRP Stack和Checkers这两个Keepalived关键组件是否正常工作。

SMTP ==> 是一个SMTP接口,主要用于当VRRP Stack中发生地址流动时,或者当Checkers发现服务上下线并增删服务节点时,能够以邮件方式通知管理员。

 

 

六、Keepalived的安装

 

对于CentOS 6.4+的系统版本,Keepalived程序包已经在base源提供。在CentOS 7.x上使用yum工具安装如下:

# yum -y install keepalived
 

 

6.1 高可用集群配置的前提

 

① 确保各节点时间同步 ==> 基于NTP协议或Chrony协议

② 确保iptables和selinux不会阻碍

③ 各节点之间可通过主机名互相通信(对Keepalived并非必须) ==> 名称解析服务的解析结果必须与"uname -n"命令的执行结果相同。

④ 各节点之间的root用户可以基于**认证的SSH进行通信(对Keepalived并非必须)

 

6.2 Keepalived配置

 

主配置文件 ==> /etc/keepalived/keepalived.conf

Unit File ==> /usr/lib/systemd/system/keepalived.service

Unit File的配置文件 ==> /etc/sysconfig/keepalived

 

Keepalived配置内容块:

GLOBAL CONFIGURATION(全局配置)

VRRPD CONFIGURATION(VRRP配置)

LVS CONFIGURATION(LVS配置)

 

(1) GLOBAL CONFIGURATION(全局配置)

全局配置框架:

global_defs {
    ...
}
 

 

常用配置:

global_defs {
    notification_email        # 邮件通告的收件人(如果有多个收件人须用花括号'{}'括起来)
        {
        aaa@qq.com    # postfix服务只能由本机用户发给本机用户
        ...                   # 如果要发送给其它主机上的用户可借助于有提供smtp服务的主机,
        }                       但一般smtp服务器需要申请有互联网的公网IP地址
    notification_email_from aaa@qq.com    # 邮件通告的发送人
    smtp_server 127.0.0.1     # smtp服务器的IP地址。如果是向外提供服务的smtp服务器,则需要
                                支持正反解
    smtp_connect_timeout 30   # 发送邮件的超时时长
    router_id my_hostname     # 定义当前路由器设备的ID号(不一定要使用主机名)
    vrrp_mcast_group4 224.0.0.18    # 基于VRRP协议的多个虚拟路由器使用的组播地址
                                      group4表示使用ipv4格式地址
}
 

 

全局配置示例:

global_defs {
   notification_email {
        aaa@qq.com
   }
   notification_email_from aaa@qq.com
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id node1
   vrrp_mcast_group4 224.0.100.18
}
 

 

(2) VRRPD CONFIGURATION(VRRP配置)

虚拟路由配置框架:

vrrp_sync_group GRP_NAME {    # 规定所有的虚拟路由器的主Master节点都必须在同一台主机上
    ...
}
vrrp_instance INST_NAME {     # 配置虚拟路由器实例,可配置多个实例
    ...
}
 

 

常用配置:

vrrp_instance <string> {     # 配置虚拟路由器实例
    state MASTER | BACKUP    # 当前节点在此虚拟路由器中的初始状态
    interface ETHERCARD      # VRRP实例工作(绑定)的网络接口
    virtual_router_id 51     # 虚拟路由器ID,范围是0-255
    priority 100             # 当前物理节点在此虚拟路由器中的优先级
    advert_int #             # VRRP通告的时间间隔
    
    authentication {         # 认证机制
        # PASS||AH
        # PASS - Simple Passwd (suggested)
        # AH - IPSEC (not recommended))
        auth_type PASS       # 认证类型,推荐使用简单字符串认证(PASS类型)
        # Password for accessing vrrpd.
        # should be the same for all machines.
        # Only the first eight (8) characters are used. ==> 以下认证密码仅前8位字符有效
        auth_pass 1234       # 认证密码(建议使用随机数作为认证密码)
    }
    
    virtual_ipaddress {      # 定义虚拟IP
        <IPADDR>/<MASK> brd <IPADDR> dev <STRING> scope <SCOPE> label <LABEL>
        192.168.200.17/24 dev eth1
        192.168.200.18/24 dev eth2 label eth2:1
        192.168.200.18/32
    }
    
    track_interface {        # 定义要监控的接口
        eth0
        eth1
        ...
    }
    
    nopreempt                # 工作于非抢占模式(不指定时默认为抢占模式),如果两台路由器
                               性能相差不大时可以考虑工作于非抢占模式
    preempt_delay 300        # 工作于抢占模式时,设定再次上线的节点(原来为主Master节点)
                               应该延迟多长时间再抢占
    
    # 通告脚本定义
    notify_master <STRING>|<QUOTED-STRING>    # 当前节点变成主Master节点时使用的通告脚本
    notify_backup <STRING>|<QUOTED-STRING>    # 当前节点变成备Backup节点时使用的通告脚本
    notify_fault <STRING>|<QUOTED-STRING>     # 当前节点故障时使用的通告脚本
    notify <STRING>|<QUOTED-STRING>           # 以上无论哪一个状态发生变化,都使用的通告脚本
}
 

 

虚拟路由器实例配置示例:

vrrp_instance VI_1 {
    state MASTER
    interface ens33
    virtual_router_id 6
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 5FfZnxFm
    }   
    virtual_ipaddress {
        192.168.100.66    # VIP地址
    }
    notify_master "/etc/keepalived/notify.sh master"    # 注意要使用双引号
    notify_backup "/etc/keepalived/notify.sh backup"
    notify_fault "/etc/keepalived/notify.sh fault"
}
 

 

通知脚本示例:

# vim /etc/keepalived/notify.sh 

#!/bin/bash
#
contact='aaa@qq.com'

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

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

注意:通告脚本需要有执行权限。

# chmod +x /etc/keepalived/notify.sh
 

 

(3) LVS CONFIGURATION(LVS配置)

LVS集群配置框架:

virtual_server_group VSG_NAME {
    ...
}
virtual_server IP port | 
virtual_server fwmark int {
    protocol TCP
	...
    real_server <IPADDR> <PORT> {
        ...
    }
    real_server <IPADDR> <PORT> {
        ...
    }
    ...		
}
 

 

常用配置:

virtual_server IP port |        #==> 通过请求报文的目标IP和目标PORT定义集群服务
virtual_server fwmark # {       #==> 通过防火墙标记定义集群服务
    lb_algo rr|wrr|lc|wlc|lblc|sh|dh    # 定义负载均衡调度算法
    delay_loop <INT>            # 定义服务轮询时间间隔,即对后端服务的检测时间间隔
    lb_kind NAT|DR|TUN          # 集群的类型
    persistence_timeout <INT>   # 持久连接时长
    protocol TCP                # 服务协议
    virtualhost <STRING>        # 当后端Web服务器上有多台虚拟主机时,可使用此项来指明要检测
                                  的虚拟主机(带上Host首部)。否则,则只向RIP发出请求,并只
                                  由后端Web服务器上的默认虚拟主机响应
    sorry_server <IPADDR> <PORT>     # 所有RS均故障时,提供say sorry的服务器
    real_server <IPADDR> <PORT> {    # 定义RS
        weight <INT>                 # 权重
        notify_up <STRING>|<QUOTED-STRING>        # 节点上线时调用的通知脚本
        notify_down <STRING>|<QUOTED-STRING>      # 节点离线时调用的通知脚本
        # HTTP_GET|SSL_GET|TCP_CHECK|SMTP_CHECK|MISC_CHECK
        # 支持所有的健康状态检测方式
        
        HTTP_GET|SSL_GET {
            url {
                path <STRING>            # 健康状态检测时请求的资源的URL
                digest <STRING>          # 基于获取的内容摘要码进行健康状态判定
                status_code <INT>        # 基于要求返回的状态码进行健康状态判定
            }
            nb_get_retry <INT>           # 尝试的次数
            delay_before_retry <INT>     # 两次尝试之间的时间间隔
            connect_timeout <INTEGER>    # 连接的超时时长
            connect_ip <IP ADDRESS>      # 向此处指定的地址发测试请求(不指定时则向此前指定的RIP发测试请求)
            connect_port <PORT>          # 项此处指定的PORT发测试请求(不指定时则向此前指定的RS的PORT发测试请求)
            bindto <IP ADDRESS>          # 指定测试请求报文的源IP
            bind_port <PORT>             # 指定测试请求报文的源PORT
        }
        TCP_CHECK {
            # 除了不能定义url,其它的配置同HTTP_GET|SSL_GET
        }
    }
}
 

 

LVS集群配置示例:

virtual_server 192.168.10.7 80 {
    delay_loop 6
    lb_algo rr
    lb_kind DR
    persistence_timeout 50
    protocol TCP
    sorry_server 127.0.0.1 80

    real_server 192.168.10.11 80 {
        weight 1
        HTTP_GET {
            url {
              path /
              status_code 200
            }
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }
    }
    real_server 192.168.10.12 80 {
        weight 1
        HTTP_GET {
            url {
              path /
              status_code 200
            }
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }
    }
}
 

 

6.2 Keepalived高可用其它服务

 

(1) vrrp_script、track_script

vrrp_script ==> 用于自定义一个资源(或服务)监控脚本;vrrp实例能够将脚本的状态返回值作为判断一个资源(或服务)是否可用的依据。需要公共定义,可被多个vrrp实例调用,因此要定义在vrrp实例之外。

track_script ==> 用于调用vrrp_script定义的脚本去监控资源(或服务),并能够在监控过程中,一旦发现脚本状态返回值为成功时,则使节点的优先级不变或升高,当发现脚本状态返回值为是失败时,则使节点的优先级降低。而当前节点就可以通过向外通告自身优先级来完成节点角色(主或备)的切换。

 

(2) 使用示例1 ==> 当检测到/etc/keepalived/down文件存在时,则进行地址漂移

vrrp_script chk_down {
    script "[[ -f /etc/keepalived/down ]] && exit 1 || exit 0"
    # 当检测到down文件存在时(成功),定义为失败(状态码为1);而将检测不到down文件存在时(失败),反而定义为成功(状态码为0)
    interval 2    # 每隔2秒执行脚本检测一次
    weight -5	  # 一旦脚本运行失败,则执行该项(将优先级降低5)
}

vrrp_instance INST_NAME {
    ...
    track_script {
        chk_down    # 调用已定义的vrrp_script
    }
    ...
}
 

 

(3) 使用示例2 ==> 监控具体的服务进程

vrrp_script chk_httpd {
    script "killall -0 httpd"    # 当httpd进程运行不正常时,则脚本运行失败(状态码为1);
                                   当httpd进程运行正常时,则脚本运行成功(状态码为0)
    interval 2
    weight -5     # 一旦脚本运行失败,则执行该项(将优先级降低5)
}

vrrp_instance VI_1 {
    ...
    track_script {
        chk_httpd
    }
    ...
}
 

注意:

(1) killall命令使用-0选项时只起探测作用,不会真正杀死进程。

(2) 调用的脚本中使用的命令必须为已经安装的命令。本示例中的vrrp脚本只有在主机上必须有killall命令时才能执行,如果没有killall命令,则可使用yum安装psmisc。

 

问题:如何改进该脚本?

vrrp_script所实现的监控功能应该更为完整,例如可以在检测不到资源(或服务)时,不要立即调低优先级,而先尝试重启服务,并规定尝试几次,两次尝试之间的时间间隔。当然,在vrrp_script中所实现的功能也可由前面所提到的通告脚本来实现,也就是可以将前面的通告脚本示例再改进一下。改进后的脚本如下。

#!/bin/bash
#
contact='aaa@qq.com'

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

case $1 in
master)
	notify master
	systemctl start httpd.service      # 保证服务是启动的
	;;
backup)
	notify backup
	systemctl restart httpd.service    # 当切换为备节点时,重启一次服务
	;;
fault)
	notify fault
	systemctl restart httpd.service    # 当节点发生错误时,重启一次服务
	;;
*)
	echo "Usage: $(basename $0) {master|backup|fault}"
	;;
esac