一、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官网)。
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 # 邮件通告的收件人(如果有多个收件人须用花括号'{}'括起来) { admin@example1.com # postfix服务只能由本机用户发给本机用户 ... # 如果要发送给其它主机上的用户可借助于有提供smtp服务的主机, } 但一般smtp服务器需要申请有互联网的公网IP地址 notification_email_from admin@example.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 { root@localhost } notification_email_from kaadmin@itab.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='root@localhost' 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='root@localhost' 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
转载于:https://blog.51cto.com/xuweitao/1953167