关于 Keepalived 系列其他文章的传送门
《 实现高可用集群的神器 详解 Keepalived 》 您当前所在位置
《 LVS 小宇宙爆发! 当 Keepalived 遇上 LVS,实现集群高可用 》
一、高可用集群介绍
高可用集群的类型主要有三种 :
LB:负载均衡集群;
HA:高可用集群;
HP:高性能集群。
本篇主要介绍高可用集群
高可用集群 (High Availability Cluster) 简称 HA Cluster ,是一组计算机,它们作为一个整体向用户提供各种资源,集群里的 host 就是节点 (node)。
高可用集群是以减少服务中断时间为目的的服务器集群技术,它通过保护用户的业务程序对外不间断提供的服务,把因软件、硬件、人为造成的故障对业务的影响降低到最小程度,简单说就是保证服务不间断地运行。
集群可用性
集群可用性是通过系统的可靠性 (reliability) 和可维护性 (maintainability) 来衡量。
通常使用平均无故障时间 (MTTF) 来衡量系统的可靠性,用平均修复时间 (MTTR) 来衡量系统的可维护性。
计算公式 :HA=MTTF/(MTTF+MTTR)
高可用集群实现机制
高可用集群主要实现自动侦测 (Auto-Detect) 故障、自动切换/故障转移 (FailOver) 和自动恢复 (FailBack) 功能。简单来说就是,用高可用集群软件实现故障检查和故障转移 (故障/备份主机切换) 的自动化,当然像负载均衡、DNS 分发也可提供高可性。
自动侦测机制
自动侦测阶段由主机上的软件通过冗余侦测线,经由复杂的监听程序,逻辑判断,来相互侦测对方运行的情况。
常用的方法是:集群各节点间通过心跳信息判断节点是否出现故障。
避免集群脑裂
如果当有节点(一个或多个)和另外节点互相接收不到对方心跳信息时,如何决定哪一部分节点是正常运行的,而哪一部分是出现故障需要隔离的呢
这时候通过法定票数 (quorum) 决定,即当有节点故障时,节点间投票决定哪个节点是有问题的,票数大于半数为合法。
票数
每个节点可以设置票数,即决定节点在集群内是否合法(正常)的权限值,这个是可以有多有少的,例如有些节点的性能较好或有其他优势,可以设置较多的票数。
法定票数(quorum)
当一个节点能和另一个节点保持心跳信息,该节点就获取得了另一个节点的票数,该节点获得的所有票数就是法定票数。
比较特殊的是只有两个节点的集群,或两边票数相等。
这时候可以借助另外的参考节点,如 ping 网关(或是一个节点),可以和测试点 ping 通,但不可以和对方通,说明对方节点有问题,或本节点有问题;还有就是通过仲裁设备,如仲裁磁盘,每个节点都间隔一定时间不停往磁盘写数据,若监测到对方不再写入的时候,可能对方节点出故障。
但最好还是使得组成集群的节点数量为单数台(2n+1),当集群分区脑裂时,节点数量小于一半(>n+1)的分区自动停止对外提供服务。
Paxos 算法
关于“投票”,有必要知道著名的 Paxos 算法。
Paxos 算法解决的是保证集群中每个节点执行相同的操作序列,可以保证分布式群集中的数据一致性。
例如,通过投票来对写操作进行全局编号,同一时刻,只有一个写操作被批准,同时并发的写操作要去争取选票,只有获得过半数选票的写操作才会被批准(所以永远只会有一个写操作得到批准);而其他的写操作竞争失败只好再发起一轮投票,就这样,在日复一日年复一年的投票中,所有写操作都被严格编号排序。
编号严格递增,当一个节点接受了一个编号为 100 的写操作,之后又接受到编号为 99 的写操作(因为网络延迟等很多不可预见原因),它马上能意识到自己数据不一致了,自动停止对外服务并重启同步过程。
任何一个节点挂掉都不会影响整个集群的数据一致性(总 2n+1 台,除非挂掉大于 n 台)。
HA Cluster 的实现方案
通过 vrrp 协议的实现
通过 ais 架构实现
RHCS(cman)
heartbeat
corosync
本文主要介绍 Keepalived 。
二、Keepalived 介绍
keepalived 的主要特性
vrrp 协议的软件实现,原生设计的目的为了高可用 ipvs 服务:
基于 vrrp 协议完成地址浮动;
为 vip 地址所在的节点生成 ipvs 规则(在配置文件中预先定义);
为 ipvs 集群的各 RS 做健康状态检测;
可以基于脚本调用接口通过执行脚本完成脚本中定义的功能,进而影响集群事务;
工作模式
主/备 :单虚拟路由器;
主/主 :主/备(虚拟路由器 1),备/主(虚拟路由器 2)
keepalived 的安装配置
HA Cluster 的配置前提 :
各节点时间必须同步,使用 ntp, chrony;
确保 iptables 及 selinux 不会成为阻碍;
各节点之间可通过主机名互相通信(对 KA 并非必须),建议使用 /etc/hosts 文件实现;
确保各节点的用于集群服务的接口支持组播 MULTICAST 通信,使用 D 类 224-239 的地址。
如果没开启使用 ip link set 打开即可;
注意 :keepalived 不支持重载,需要重启来重新加载配置文件。
CentOS 6.4 以上的 base 仓库提供,使用 yum 安装即可。
1 yum install keepalived -y
程序环境
主配置文件:/etc/keepalived/keepalived.conf
主程序文件:/usr/sbin/keepalived
Unit File: keepalived.service
Unit File 的环境配置文件:/etc/sysconfig/keepalived
配置文件
配置文件分为 3 段:
GLOBAL CONFIGURATION:全局
Global definitions:全局配置段;
Static routes/addresses/rules:静态路由和地址相关配置;
VRRPD CONFIGURATION:VRRP
VRRP script(s):脚本相关配置;
VRRP synchronization group(s):同步组相关配置;
VRRP gratuitous ARP:免费 ARP 相关配置;
VRRP instance:每个 vrrp instance 即对应一个 vrrp 路由器;
LVS CONFIGURATION: IPVS
Virtual server group(s):定义 ipvs 组;
Virtual server(s):定义 ipvs 服务,配置 ipvs 集群的 vs 和 rs。
三、Keepalived 的配置
1. GLOBAL CONFIGURATION 配置段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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_strict vrrp_garp_interval 0 vrrp_gna_interval 0 vrrp_mcast_group4 224.0 .0 .18 vrrp_iptables }
2. VRRP instance 配置段
配置 instance
1 2 3 vrrp_instance <STRING> { ... }
专用参数
state MASTER|BACKUP:当前节点在此虚拟路由器上的初始状态;只能有一个是 MASTER,余下的都应该为 BACKUP;
interface IFACE_NAME:绑定为当前虚拟路由器使用的物理接口;
virtual_router_id VRID:当前虚拟路由器的惟一标识,同一组应该相同,范围是 0-255;
priority 100:当前主机在此虚拟路由器中的优先级,范围 1-254;
advert_int 1:vrrp 心跳检测的时间间隔,默认 1s;
配置发送集群事务的认证信息
1 2 3 4 authentication { auth_type PASS auth_pass <PASSWORD> }
定义虚拟路由器 VIP
1 2 3 4 5 virtual_ipaddress { 192.168 .200 .17 /24 dev eth1 192.168 .200 .18 /24 dev eth2 label eth2:1 }
单主配置示例
VIP:192.168.50.200
keepalived_server:192.168.50.11
keepalived_server:192.168.50.12
vim /etc/keepalived/keepalived.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 ! Configuration File for keepalived global_defs { notification_email { root@localhost } notification_email_from keepalived@localhost smtp_server 127.0 .0 .1 smtp_connect_timeout 30 router_id node1 vrrp_skip_check_adv_addr vrrp_strict vrrp_garp_interval 0 vrrp_gna_interval 0 vrrp_mcast_group4 224.0 .0 .200 vrrp_iptables } vrrp_instance VI_1 { state MASTER interface ens33 virtual_router_id 1 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 11112222 } virtual_ipaddress { 192.168 .50 .200 /24 brd 192.168 .50 .255 dev ens33 label ens33:0 } }
复制到备用节点:scp /etc/keepalived/keepalived.conf node2:/etc/keepalived/keepalived.conf
备节点修改特有信息:
router_id node1 --> router_id node1
state MASTER --> state BACKUP
priority 100 --> priority 98
启动服务,完成。
1 systemctl start keepalived
双主双活配置示例
VIP:192.168.50.200
VIP:192.168.50.201
keepalived_server:192.168.50.11
keepalived_server:192.168.50.12
配置节点 1 :
vim /etc/keepalived/keepalived.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 ! Configuration File for keepalived global_defs { notification_email { root@localhost } notification_email_from keepalived@localhost smtp_server 127.0 .0 .1 smtp_connect_timeout 30 router_id node1 vrrp_skip_check_adv_addr vrrp_strict vrrp_garp_interval 0 vrrp_gna_interval 0 vrrp_mcast_group4 224.0 .0 .200 vrrp_iptables } vrrp_instance VI_1 { state MASTER interface ens33 virtual_router_id 1 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 11112222 } virtual_ipaddress { 192.168 .50 .200 /24 brd 192.168 .50 .255 dev ens33 label ens33:0 } } vrrp_instance VI_2 { state BACKUP interface ens33 virtual_router_id 2 priority 98 advert_int 1 authentication { auth_type PASS auth_pass 11112222 } virtual_ipaddress { 192.168 .50 .201 /24 brd 192.168 .50 .255 dev ens33 label ens33:1 } }
配置节点 2,与节点 1 基本相反 :
vim /etc/keepalived/keepalived.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 ! Configuration File for keepalived global_defs { notification_email { root@localhost } notification_email_from keepalived@localhost smtp_server 127.0 .0 .1 smtp_connect_timeout 30 router_id node2 vrrp_skip_check_adv_addr vrrp_strict vrrp_garp_interval 0 vrrp_gna_interval 0 vrrp_mcast_group4 224.0 .0 .200 vrrp_iptables } vrrp_instance VI_1 { state BACKUP interface ens33 virtual_router_id 1 priority 98 advert_int 1 authentication { auth_type PASS auth_pass 11112222 } virtual_ipaddress { 192.168 .50 .200 /24 brd 192.168 .50 .255 dev ens33 label ens33:0 } } vrrp_instance VI_2 { state MASTER interface ens33 virtual_router_id 2 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 11112222 } virtual_ipaddress { 192.168 .50 .201 /24 brd 192.168 .50 .255 dev ens33 label ens33:1 } }
启动服务
1 systemctl start keepalived
在 DNS 定义两条 A 记录
1 2 vip.monster.com A 192.168 .50 .200 vip.monster.com A 192.168 .50 .201
使用域名访问,完成
1 2 3 4 5 6 7 -> PING vip.monster.com (192.168.50.200) 56(84) bytes of data. 64 bytes from 192.168.50.200 (192.168.50.200): icmp_seq=1 ttl=64 time=0.295 ms -> PING vip.monster.com (192.168.50.201) 56(84) bytes of data. 64 bytes from 192.168.50.201 (192.168.50.201): icmp_seq=1 ttl=64 time=0.277 ms
3. 通知机制
在节点发生状态改变时,可以使用通知机制,自动执行一个指定的脚本。
(1). 在 VRRP instance 配置段中使用的语法:
当一个节点的状态转为 MASTER 时,执行指定的脚本发给指定的用户 username。
notify_master <STRING>|<QUOTED-STRING> [username [groupname]]
转为 BACKUP 时。
notify_backup <STRING>|<QUOTED-STRING> [username [groupname]]
转为 FAULT 时。
notify_fault <STRING>|<QUOTED-STRING> [username [groupname]]
转为 STOP 时。
notify_stop <STRING>|<QUOTED-STRING> [username [groupname]]
也可以手动指定。
notify <STRING>|<QUOTED-STRING> [username [groupname]]
其中:
<STRING>:可以是字符串;
<QUOTED-STRING>:也可以使用带引号的字符串,当脚本路径有空白字符时就需要用引号括起来。
(2). 在 VRRP synchronization group(s) 中配置时,大体上是一样的:
当一个节点的状态转为 MASTER 时,执行指定的脚本发给指定的用户 username。
notify_master /path/to_master.sh [username [groupname]]
转为 BACKUP 时。
notify_backup /path/to_backup.sh [username [groupname]]
转为 FAULT 时。
notify_fault "/path/fault.sh VG_1" [username [groupname]]
也可以使用这样一个脚本,完成上述三个脚本的功能,但是需要传递一些变量。
notify /path/notify.sh [username [groupname]]
传递变量:
$1:一个组或实例;
$2:组或实例的名称;
$3:在节点转到什么状态时就警告;
$4:优先级的值;
示例:状态通知
使用这个通知机制,实现当 Keepalived 的自身状态发生变化时,给管理员发送一个邮件通知。
注意 :要在主备中同时配置
创建通知脚本 vim /etc/keepalived/notify.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # !/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) notify master ;; backup) notify backup ;; fault) notify fault ;; *) echo "Usage: $(basename $0) {master|backup|fault}" exit 1 ;; esac
在虚拟路由器中定义好调用脚本的时机。
1 2 3 4 5 6 7 vrrp_instance VI_1 { ... ... notify_master "/etc/keepalived/notify.sh master" notify_backup "/etc/keepalived/notify.sh backup" notify_fault "/etc/keepalived/notify.sh fault" }
可能需要安装 mailx:yum install mailx -y
重启 keepalived 服务,然后通过 mail 就可以看到状态转变的邮件通知了。
1 2 3 4 5 -> Heirloom Mail version 12.5 7/5/10. Type ? for help. "/var/spool/mail/root": 2 messages 2 unread > U 1 root Mon Aug 19 23:14 19/677 "node1.monster.com to be backup, vip floating" U 2 root Mon Aug 19 23:14 19/677 "node1.monster.com to be master, vip floating"
4. VRRP script(s)
Keepalived 支持自定义一个脚本,然后它会定期执行这个脚本,并用脚本的退出码作为判断实例健康与否的依据。
1. 使用方法
在一个新的上下文中定义脚本,语法:
1 2 3 4 5 6 7 8 9 10 vrrp_script <SCRIPT_NAME> { script <STRING>|<QUOTED-STRING> interval <INTEGER> timeout <INTEGER> weight <INTEGER:-254. .254 > rise <INTEGER> fall <INTEGER> user USERNAME [GROUPNAME] init_fail }
在 VRRP instance 中引用脚本才会生效,语法:
1 2 3 4 track_script { <SCRIPT_NAME> <SCRIPT_NAME> weight <-254. .254 > }
2. 配置示例
两个节点:
node1 为 MASTER;
node2 为 BACKUP;
node1 上的配置文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ... ... vrrp_script maint { script "/bin/bash -c '[[ -e /etc/keepalived/down ]]' && exit 1 || exit 0" interval 1 fall 3 rese 2 weight -5 timeout 1 } vrrp_instance VI_1 { state MASTER interface ens33 virtual_router_id 1 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 11112222 } virtual_ipaddress { 192.168 .50 .200 /24 brd 192.168 .50 .255 dev ens33 label ens33:1 } track_script { maint } }
以上配置的意思是 :使用 /bin/bash -c '[[ -e /etc/keepalived/down ]]' && exit 1 || exit 0 这个脚本语句每秒执行 1 次,如果检测到 /etc/keepalived/down 这个文件存在,退出码即为错误。那么在错误 3 次后就将实例的优先级减 5,之前默认优先级是 100,减 5 后为 95,就比 BACKUP 低了,所以 BACKUP 节点就会被激活变成主节点。
在组播地址 224.0.0.200 上抓包,可以看到 node1 节点的广播优先级是 100,目前最高,处于激活状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ~]# tcpdump -i ens33 host 224.0.0.200 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes 19:17:38.560801 IP node1 > 224.0.0.200: VRRPv2, Advertisement, vrid 1, prio 100, authtype simple, intvl 1s, length 20 19:17:39.561860 IP node1 > 224.0.0.200: VRRPv2, Advertisement, vrid 1, prio 100, authtype simple, intvl 1s, length 20 19:17:40.563088 IP node1 > 224.0.0.200: VRRPv2, Advertisement, vrid 1, prio 100, authtype simple, intvl 1s, length 20 19:17:41.564403 IP node1 > 224.0.0.200: VRRPv2, Advertisement, vrid 1, prio 100, authtype simple, intvl 1s, length 20 19:17:42.565387 IP node1 > 224.0.0.200: VRRPv2, Advertisement, vrid 1, prio 100, authtype simple, intvl 1s, length 20 19:17:43.566705 IP node1 > 224.0.0.200: VRRPv2, Advertisement, vrid 1, prio 100, authtype simple, intvl 1s, length 20 # 此时新开一个终端来创建文件 touch /etc/keepalived/down,就可以看到 node1 的优先级减了 5,变成了 95,低于 node2 的优先级 98,于是 node2 被激活 19:17:44.568362 IP node1 > 224.0.0.200: VRRPv2, Advertisement, vrid 1, prio 95, authtype simple, intvl 1s, length 20 # node2 的优先级为 98,高于 95,于是被激活 19:17:44.568578 IP node2 > 224.0.0.200: VRRPv2, Advertisement, vrid 1, prio 98, authtype simple, intvl 1s, length 20 19:17:45.569353 IP node2 > 224.0.0.200: VRRPv2, Advertisement, vrid 1, prio 98, authtype simple, intvl 1s, length 20
3. 检测 Nginx 或 HAProxy 的健康状态
其实通过上述的使用方法,还可以用来检测其他进程的健康与否,如 Nginx 或 HAProxy。
以 Nginx 为例,可以使用 killall -0 nginx 这条命令向待检测的进程传递信号 0,信号 0 代表检测进程的运行状态,而不是去 kill 进程。进程运行正常就返回正确的状态码,否则返回错误的。
killall 命令由 psmisc 提供,需要安装 yum install psmisc -y。
于是将上述示例中的脚本替换为:
1 script "killall -0 nginx"
这样当 node1 上的 Nginx 进程运行异常时,node1 节点就会被降低优先级,node2 就会变为 MASTER 。
在某些时候,还可以结合通知机制 ,当 node1 被降级为 BACKUP 时,就调用脚本尝试重启 Nginx,不过在双主模式中,要仔细检查逻辑,防止出现混乱。
定义通知机制要触发的脚本 :
1 notify_backup "/etc/keepalived/restart_nginx.sh backup"
脚本内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 # !/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) notify master ;; backup) notify backup systemctl restart nginx ## 尝试重启 ;; fault) notify fault ;; *) echo "Usage: $(basename $0) {master|backup|fault}" exit 1 ;; esac
4. LVS CONFIGURATION 配置段
定义 IPVS 配置的语法格式、常用参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 virtual_server IP PORT { virtual_server fwmark <INT> protocol TCP|UDP|SCTP delay_loop lb_algo rr|wrr|lc|wlc|lblc|sh|dh lb_kind NAT|DR|TUN persistence_timeout [<INT>] sorry_server <IPADDR> <PORT> real_server <IPADDR> <PORT> { weight <INT> notify_up <STRING>|<QUOTED-STRING> [username [groupname]] notify_down <STRING>|<QUOTED-STRING> [username [groupname]] uthreshold <INTEGER> lthreshold <INTEGER> HTTP_GET|SSL_GET|TCP_CHECK|SMTP_CHECK|DNS_CHECK|MISC_CHECK HTTP_GET|SSL_GET { url { path <STRING> status_code <INT> digest <STRING> } nb_get_retry <INT> delay_before_retry <INT> connect_timeout <INTEGER> connect_ip <IP ADDRESS> connect_port <PORT> } TCP_CHECK { connect_ip <IP ADDRESS> connect_port <PORT> fwmark <INTEGER> connect_timeout <INTEGER> retry <INT> delay_before_retry <INT> } } }
关于实现 LVS 双主双活的详细配置,可以看这篇 :
《 LVS 小宇宙爆发! 当 Keepalived 遇上 LVS,实现集群高可用 》
参考:https://blog.csdn.net/tjiyu/article/details/52643096