全局定义(globaldefinnition)

这一部分用来设置keepalived的故障通知机制和RouterID标识。

1
2
3
4
5
6
7
8
9
10
!Configuration File for keepalived
global_defs {
notification-email
[xxx@qq.com](mailto:xxx@qq.com) #需要抵达的邮箱地址
[xxx@hotmail.com](mailto:xxxx@hotmail.com) #需要抵达的邮箱地址
}
notification_email_from [xxx@qq.com](mailto:xxx@qq.com) #用于发送的邮箱
smtp_server smtp.qq.com #stmp服务器
smtp_connect_timeout 30 #设置超时时间
router_id LVS_DEVEL #keepalived路由标识(router_id)。在统一局域网内,这个标识应该是唯一的。

大括号“{}”用于分割区块,需要成对出现。如果少了半个,keepalived也不会报错,但也得不到预期效果。

VRRP实例定义区块部分

这个部分主要用来定义具体服务的实例配置,包括Keepalived主备状态、接口、优先级、认证方式和IP信息等。

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
#定义一个vrrp_instance实例,名字是VI_1,每个vvrp_instance实例都可以认为是Keepalived中的一个业务服务。

#keepalived中可以有多个这一的实例,备节点也要存在相同的实例才能进行故障切换接管。
vrrp_instance VI_1 {
state MASTER #角色为master,其他节点应当为BACKUP,才能进行顺利的故障接管
interface ens33 #网络通信接口,要确定好具体是哪一块网卡

#虚拟路由ID,这个标识最好是一个数字,并且在一keepalived中是唯一的。
#但是MASTER和BACKUP中保持一致,否则会出现”脑裂”问题。
virtual_router_id 51

priority 100 #优先级/权重为100,master要比backup大
advert_int 1 #同步时间间隔,master和bakcup之间的通信检查时间,默认为1s

#往下都是权限认证配置,包括认证类型(auth_type)和认证密码(auth_pass),认证类型有PASS(Simple Passwd(suggested))。

#AH(ipasec(not recommended))两种,官方推荐类型为PASS。密码为明文模式,最好不要超过8个字符,推荐用4个字符。
#同一VRRP实例的master和backup使用相同密码才能通信。

authentication {
auth_type PASS
auth_pass 123456 //定义密码,这个密码自定义
}

#虚拟IP(VIP)地址,可以配置多个虚拟IP(VIP)。
#配置时,最好指定子网掩码和VIP绑定的网络接口,否则默认验码是32位。绑定的网卡要和前面的interface保持一致。
#这里就是和我们工作中需要和域名绑定的IP,即和配置的高可用服务监听保持一致。

virtual_ipaddress {
192.168.1.10 #定义VIP_1
192.168.1.20 #定义VIP_2
192.168.1.30 #定义VIP_3
}

track_script {
chk_nginx //定义监控脚本,这里和上面vrr_script后面的字符串保持一致
}
}

高可用单实例

配置主机

切换目录

1
cd /etc/keepalived/

备份配置文件

1
cp keepalived.conf{,.ori}

清空配置文件

1
\ > keepalived.conf

编写配置文件

1
vim keeplived.conf

配置文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
global_defs {
router_id web01 #<== ID为web01,不同的keepalived.conf此ID要唯一。
}

vrrp_instance VI_1 { #<== 实例名字为VI_1,相同实例的备节点名字要和这个相同。
state MASTER #<== 状态为MASTER,备节点状态要为BACKUP。
interface ens33 #<== 通信接口为ens33,对于此参数,备节点和主节点相同。
virtual_router_id 51 #<== 实例ID为51,在同一keepalived.conf配置文件内是唯一。
priority 150 #<== 优先级为150,备节点的优先级要小于主节点的优先级。
advert_int 1 #<== 通信检查时间间隔1秒。
authentication {
auth_type PASS #<== PASS认证类型,对于此参数,备节点设置要和主节点相同。
auth_pass 123456 #<== 密码是123456,对于此参数,备节点设置要和主节点相同。
}
virtual_ipaddress {
192.168.1.120 dev ens33 label eth33:0
#<== 虚拟IP,即VIP,子网验码为24位,绑定接口为eth0,别名为eth33:0,对于此参数,备节点要和主系欸但设置相同。
}
}
#提示:此处设置的虚拟IP为192.168.1.120,即为域名网站绑定的IP。

检查并启动keepalived

1
2
3
systemctl start keepalived

ps -ef |grep keep|grep -v grep

检查IP情况

1
ip addr|grep 192.168.1.120

配置备机

切换目录

1
cd /etc/keepalived/

备份配置文件

1
cp keepalived.conf{,.ori}

清空配置文件

1
\ > keepalived.conf

编写配置文件

1
vim keeplived.conf

配置文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
global_defs {
router_id web02 #<== ID为web02,不同的keepalived.conf此ID要唯一。
}

vrrp_instance VI_1 { #<== 实例名字为VI_1,相同实例的备节点名字要和这个相同。
state BACKUP #<== 状态为MASTER,备节点状态要为BACKUP。
interface ens33 #<== 通信接口为ens33,对于此参数,备节点和主节点相同。
virtual_router_id 51 #<== 实例ID为51,在同一keepalived.conf配置文件内是唯一。
priority 100 #<== 优先级为150,备节点的优先级要小于主节点的优先级。
advert_int 1 #<== 通信检查时间间隔1秒。
authentication {
auth_type PASS #<== PASS认证类型,对于此参数,备节点设置要和主节点相同。
auth_pass 123456 #<== 密码是123456,对于此参数,备节点设置要和主节点相同。
}
virtual_ipaddress {
192.168.1.120 dev ens33 label eth33:0
#<== 虚拟IP,即VIP,子网验码为24位,绑定接口为eth0,别名为eth33:0,对于此参数,备节点要和主系欸但设置相同。
}
}
#提示:此处设置的虚拟IP为192.168.1.120,即为域名网站绑定的IP。

检查并启动keepalived

1
2
systemctl start keepalived
ps -ef |grep keep|grep -v grep

这里因为没有接管,所以不应该有返回

如果有,就证明脑裂了。

  1. 检查通信情况,iptables/firewalld

  2. 检查配置文件,例如virtual_router_id是否不一致

1
ip addr|grep 192.168.1.120

高可用测试

暂停主节点keepalived服务或关机

1
systemctl stop keepalived

分别在主节点和备节点上检查ip状态(若主节点已关机则不用检查主节点)

备节点应该拿到了VIP

1
ip addr|grep 192.168.1.120

重启主节点服务

1
systemctl start keepalived

检查VIP状态,主节点应当拿回了VIP

1
ip addr|grep 192.168.1.120

配置差别

Keepalived 参数差别 MASTER 节点特殊参数 BACKUP 节点特殊参数
router_id(唯一标识) routrer_id web01 routrer_id web02
state state MASTER state BACKUP
priority priority 150 priority 100

“脑裂”问题

什么是脑裂

在指定时间内两台高可用服务无法检测到对他心跳消息,各自取得资源以及服务所有权,而此时两台高可用服务器都会去抢占同样的资源,这样会导致同一个IP或服务在两端同时存在发生冲突,最严重的就是两台主机抢占同一个IVP地址,当用户写入数据时,可能会分别写入两端,造成两端数据不一致或数据丢失,这种情况就被成为脑裂。

导致脑裂发生的原因

  1. 高可用服务器之间对心跳链路故障,导致无法正常通信。

    • 心跳线坏了(断裂、老化)
    • 网卡以及相关驱动坏了,IP配置以及冲突问题(网卡直连)
    • 心跳线之间连接设备故障(网卡及交换机)
    • 仲裁机器出问题(采用仲裁方案)
  2. 高可用服务器对上开启了iptables防火墙阻挡了心跳消息传输

  3. 高可用服务器上心跳网卡地址等信息配置不正确

  4. 其他服务配置不当等原因,如心跳方式不同、心跳广播冲突、软件BUG等

  5. 同一VRRP实例virtual_router_id参数两端配置不一样

解决脑裂常见方案

  1. 使用串行电缆和一台电缆链接,同时使用两条心跳线路,做线路高可用。
  2. 检测到脑裂时强行关闭一个心跳节点(需要特殊设备支持,如Stonith、fence)。相当于备节点收不到心跳消息,发送关机命令通过单路的线路来关闭
  3. 做好对脑裂的状态监控(邮件、手机短信、值班)报警,发生脑裂时第一时间人为介入仲裁。如百度的报警短信可以区分上下行,管理员可以通过短信回复数字或字符串返回给服务器,让服务器根据指令自动操作。或者开发简单app,第一时间点点点操作。

常见的keepalived脑裂解决方案

互联网应用服务器,前端web负载均衡的的高可用,对于普通业务一般是可以接受的,对于数据库和存储服务不可以接受。

  • 如果开启防火墙,一定要开放心跳消息通过,一般采用定向IP段可通过。
  • 可以拉一条以太网线或者串口线作为线路冗余
  • 开发监测程序通过监控软件(如Nagios)监测脑裂

生产判断脑裂思路:

  1. 简单思路:备节点出现VIP就报警,有两种情况,一是主节点宕机了,备节点接管;二是主机没宕,脑裂了,无论是哪一种都要进行报警,然后人工查看。
  2. 严谨思路:备节点出现VIP,主节点以及对应服务仍然存活,说明脑裂了

Keepalived双实例双主机配置

规划

HOSTNAME IP 说明
web01 192.168.1.55 VIP:192.168.1.100(绑定A服务 www.xxx.com)
web02 192.168.1.56 VIP:192.168.1.101(绑定B服务 bbs.yyy.com)

配置web01

在原来的单实例基础上增加一个实例(至于你们怎么搭建服务,我建议用nginx测试)

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
global_defs {
router_id web01
}

vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 51
priority 150
advert_int 1
authentication {
auth_type PASS
auth_pass 1234
}

virtual_ipaddress {
192.168.1.100 dev ens33 label eth33:0
}
}

vrrp_instance VI_2 {
state BACKUP
interface ens33
virtual_router_id 52
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 5678
}
virtual_ipaddress {
192.168.1.101 dev ens33 label eth33:1
}
}

配置web02

建议直接scp然后改一改

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
global_defs {
router_id web02
}

vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1234
}

virtual_ipaddress {
192.168.1.100 dev ens33 label eth33:0
}
}

vrrp_instance VI_2 {
state MASTER
interface ens33
virtual_router_id 52
priority 150
advert_int 1
authentication {
auth_type PASS
auth_pass 5678
}
virtual_ipaddress {
192.168.1.101 dev ens33 label eth33:1
}
}

web01、web02上都重启服务

1
systemctl restart keepalived

web01、web02分别查看ip

1
ip add|egrep “192.168.1.100|192.168.1.101”

轮流暂停keepalived服务,同时查看未暂停服务的web服务器,是否拿到了IP

其中一个暂停了服务,另一个服务器应当会拿到两个ip

1
2
3
systemctl stop keepalived

ip add|egrep “192.168.1.100|192.168.1.101”

在停止服务的服务器上,重新启动之前暂停的服务,再次查看

应当看到服务器把ip拿回来了

1
ip add|egrep “192.168.1.100|192.168.1.101”

配置区别

web01 web02
state MASTER state BACKUP
priority 150 priority 100
state BACKUP state MASTER
priority 100 priority 150

主要区别

  • state(状态)
  • priority(权重/优先级)

优先级决定了VIP在哪个机器上初始运行

Nginx配合Keeplived

逻辑架构图

img

配置两边nginx

1
2
3
cd /pwd/nginx/conf/

vim nginx.conf

nginx内容

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
worker_processes 1;
events {
worker_connections 1024;
}

http {
include mime.types;
default_type /pwd/octet-stream;
sendfile on;
keepalive_timeout 65;

upstream www_pools { #<== 这里定义了web服务器池,包含了100、101两个web节点。
server 192.168.1.100:80 weight=1;
server 192.168.1.101:80 weight=1;
}

server { #<== 这里定义了代理的负载均衡域名虚拟主机。
listen 192.168.1.55:80; #<==这里指定了IP监听。
server_name www.xxx.com;
location / {
proxy_pass https://www_pools; #<== 访问[www.hanpi.com](http://www.hanpi.com),请求发送给www_pools里面的节点。
proxy_set_header Host $host;
}
}
}

配置keepalived服务

配置web01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
global_defs {
router_id web01
}

vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 51
priority 150
advert_int 1
authentication {
auth_type PASS
auth_pass 1234
}

virtual_ipaddress {
192.168.1.100 dev ens33 label eth33:0
}
}

配置web02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
global_defs {
router_id web02
}

vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1234
}

virtual_ipaddress {
192.168.1.100 dev ens33 label eth33:0
}
}

配置DNS解析

1
192.168.1.100 www.xxx.com

两边重启nginx服务

在web页面进入www.xxx.com

此时关闭web01高可用服务或者关闭服务器

在nginx页面刷新页面

查看web02的IP状态

1
ip addd|grep 192.168.1.100

开启web01,查看ip

1
ip addd|grep 192.168.1.100

web重新访问 www.hanpi.com

状态应该处于正常

监听网卡上不存在的IP地址

如果配置使用了”listen 192.168.1.100:80;“ 指定IP监听服务,而本地上没有这个IP,nginx就会报错。

如果实施双主(即主备)同时提供不同的服务,配置文件里制定了IP监听,备节点就会因为网卡不存在实际IP而报错。

出现上面的问题原因在于物理网卡上没有与配置文件里监听的IP相对应的IP,我们要让Nginx服务在网卡上没有指定监听的IP时也能启动,不报错。

解决办法

是在/etc/sysctl.conf中加入如下内核参数。

1
net.ipv4.ip_nonlocal_bind = 1

表示启动nginx而忽略配置中监听的ip是否存在,同样适用于Haproxy。

快速写入,刷新生效

1
echo “net.ipv4.ip_nonlocal_bind = 1” >> /etc/sysctl.conf
1
sysctl -p

解决服务宕后keepalived仍运行

默认情况下,Keepalived软件只有在对方机器宕机或者Keepalived停止的时候会接管业务。生产环境中,有业务服务停止的Keepalived还在工作的情况,这种时候就会造成数据丢失。

方法一

写守护进程脚本来处理。当Nginx业务出现问题时,就停掉本地的Keepalived服务,实现飘逸接管。

编写脚本

1
2
3
4
5
6
7
8
9
10
vim /pwd/check_nginx.sh
#!/bin/sh
while true
do
if [ ‘netstat -lntup|grep nginx|wc -l’-ne 1 ];then
systemctl stop keepalived
break
fi
sleep 5
done

启动脚本

1
sh /pwd/check_nginx.sh &

检查脚本运行情况

1
ps -ef | grep check

查看端口

1
lsof -I :80

停止nginx,查看是否切换

1
systemctl stop nginx

查看nginx状态

1
systemctl status nginx

再次检查nginx

1
netstat -lntup|grep nginx

在备节点上查看

1
ip add|grep 192.168.1.100

方法二

使用keepalived的配置文件参数触发脚本

1
2
3
4
5
6
vim check_nginx_proxy.sh
#!/bin/sh

if [ ‘netstat -lntup|grep nginx|wc -l’ -ne 1 ];then
system stop keeplived
fi

授权

1
chmod +x check_nginx_proxy.sh

检查授权

1
ls -l check_nginx_proxy.sh

keepalived配置如下

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
global_defs {
router_id web01
}
vrrp_script chk_nginx_proxy{ #<== 定义VRRP脚本,检查HTTP端口。
script “/pwd/check_nginx_proxy.sh” #<== 执行脚本,当Nginx服务器出现问题时,就停掉Keepalived服务。
interval 2 #<== 时间间隔2s。
weight 2
}

vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 51
priority 150
advert_int 1
authentication {
auth_type PASS
auth_pass 1234
}

virtual_ipaddress {
192.168.1.100 dev ens33 label eth33:0
}
tarck_script {
check_nginx_proxy #<== 触发检查。
}
}

测试脚本

检查端、服务、ip

1
2
3
4
5
lsof -i :80

ps -ef | grep keep

ip add|grep 192.168.1.100

停止nginx服务

1
systemctl stop nginx

再次检查

1
2
3
4
5
lsof -i :80

ps -ef | grep keep

ip add|grep 192.168.1.100

查看日志

1
cat /var/log/messages | grep Keepalived

多组Keepalived局域网内冲突

如果在同一局域网内部署了多组Keepalived服务器,而又未使用专门的心跳线通信时,可能会发生接管故障。

Keepalived高可用是通过VRRP协议实现的,VRRP协议默认通过IP多播实现的高可用服务对之间的通信,如果一个局域网内存在多组Keepalived服务器对,就会造成IP多播地址冲突问题,不同组的Keepalived都会使用默认的224.0.0.18作为多播地址。

所以我们需要在同组Keepalived服务器的所有配置文件中指定独立的多播地址。

指定多播地址

示例:

1
2
3
4
global_defs {
router_id LVS_01
vrrp_mcast_group4 224.0.0.20 #<== 这玩意就是多播地址
}

指定配置文件接收Keepalived服务日志

日志集成,懂得都懂

将KEEPALIVED_OPTIONS=”-D” 修改为 KEEPALIVED_OPTIONS=”-D -d -S 0”

1
vim /ect/sysconfig/keepalived

修改keepalived配置文件

快速替换

1
sed -i ’14 s# KEEPALIVED_OPTIONS=”-D”# KEEPALIVED_OPTIONS=”-D -d -S 0”#g’ /etc/sysconfig/keepalived

检查

1
sed -n ‘14p’ /etc/sysconfig/keepalived

说明,查看 /etc/sysconfig/keepalived 里注释获得以上说明

1
2
3
4
5
6
7
# --dump-conf     -d     导出备份数据。

# --log-detail -D 详细日志。

# --log-facility -S 设置本地的syslog设备,编号0-7(default=LOG_DAEMON)。

# -S 0 表示指定为local0设备。

修改系统日志配置文件

修改rsslog配置文件,尾部添加以下两行内容

1
2
3
4
5
6
7
vim /etc/rsyslog.conf

这两行表示local0设备的所有日志信息都记录到了 /var/log/keepalived.log 文件

\#keepalived

local0.* /var/log/keepalived.log

在如下信息的第一列尾部添加”;local0.none”,注意有分号。

表示来自local0设备的所有日志信息不再记录与/var/log/messages

1
*.info;mail.one;authpriv.none;cron.none;local0.none /var/log/messages

配置完成,重启rsyslog服务

1
systemctl restart rsyslog.service

重启keepaliaved,查看日志

1
2
3
systemctl restart keepalived

tail -f /var/log/keepalived.log

可以在/var/log/messages上设置对/avr/log/keepalived.log进行轮询,防止单个日志文件变大

编写监测Keepalived “脑裂”的脚本

检测思路:在备节点上执行脚本,如果可以ping通主节点并且备节点有VIP就报警,人工介入检查是否发生了”脑裂”。

编写脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
vim check_split_brain.sh

\#!/bin/sh
web01_vip = 192.168.1.100
web01_ip = 192.168.1.55
while true
do
ping -c 2 -W3 $web01_ip &>/dev/null
if [ $? -eq 0 -a ‘ip add|grep “web01_ip”|wc -l’ -eq 1 ]
then
echo “ha is split brain.waring.”
else
echo “ha is ok”
fi
sleep 5
done

测试脚本

1
sh check_split_brain.sh

关闭web01,查看web02报警情况

1
systemctl stop nginx

web02报警正常,重启web01服务,查看恢复情况

1
systemctl start nginx

测试完成后整合到Nagios或者Zabbix里