背景

​ 公司有个项目,Web服务是.net开发的跑在IIS上,平时有2-3台一模一样的web服务器提供访问。最开始使用的是Nginx做反向代理实现负载均衡,公网IP和域名都指向的是这台Nginx服务器。最近改为LVS提供负载均衡,并使用keepalived来实现LVS的主备,这样从前到后实现了完整的高可用。

服务器 IP 操作系统 部署应用
lvsmaster 10.10.247.25
VIP:10.10.247.3
CentOS Linux release 7.7.1908 Keepalived v1.3.5
ipvsadm v1.27
lvsbackup 10.10.247.26 CentOS Linux release 7.7.1908 Keepalived v1.3.5
ipvsadm v1.27
后端Web01 10.10.247.23 Windows server 2012 R2 .net 4.6
后端Web02 10.10.247.24 Windows server 2012 R2 .net 4.6

LVS安装配置

​ 直接使用yum在两台lvs服务器上安装keepalived和ipvsadm,LVS是集成在Linux内核里的,由IVPS模块实现的,安装ipvsadm工具来进行管理。

​ 安装完成后直接对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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
[root@lvsmaster ~]# cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived

global_defs {
notification_email {
youremail@gmail.com.cn
}
notification_email_from user@system.com
smtp_server 10.10.247.69
smtp_connect_timeout 30
router_id lvsmaster
vrrp_garp_interval 0
vrrp_gna_interval 0
}

vrrp_instance VI_1 {
state MASTER
interface ens160
virtual_router_id 31
vrrp_mcast_group4 224.0.0.31
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1133
}

virtual_ipaddress {
10.10.247.3/24 brd 10.10.247.3 dev ens160 label ens160:0
}
}

virtual_server 10.10.247.3 80 {
delay_loop 6
lb_algo sh
lb_kind DR
persistence_timeout 3
protocol TCP

sorry_server 10.10.247.123 80

# 后端应用服务器 IP PORT
real_server 10.10.247.23 80 {
weight 1
MISC_CHECK {
misc_path /root/http_hc_check.sh http://10.10.247.23/Default.aspx
}
}

real_server 10.10.247.24 80 {
weight 1
MISC_CHECK {
misc_path /root/http_hc_check.sh http://10.10.247.24/Default.aspx
}
}
}

​ 整个配置文件分为了三部分,global_defs、vrrp_instance、virtual_server。

  • 全局定义块主要配置故障发生时的通知对象以及机器标识等。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    global_defs {
    notification_email {
    youremail@gmail.com.cn # 接收故障通知的收件箱地址,可以配多个
    }
    notification_email_from user@system.com # 故障通知发件箱地址
    smtp_server 10.10.247.69 # 邮件故障通知smtp服务器地址
    smtp_connect_timeout 30 # smtp服务器的超时时间
    router_id lvsmaster # 当前节点的标示字符串,默认是主机名,也可以自定义,通知信息里会用到
    vrrp_garp_interval 0 # Delay in ms between gratuitous ARP messages sent on an interface
    vrrp_gna_interval 0 # Delay in ms between unsolicited NA messages sent on an interface
    }

​ 这块内容主要是配置故障通知,也可以不配使用其他监控告警手段。这里有一个重要的配置节:vrrp_strict

使用这个配置后,会强制严格遵守VRRP协议,必须在IPtable上配置相应的规则才能使VIP生效,所以一般情况下不是必要不要配置此项!

  • VRRP实例定义块主要用来定义对外提供服务的VIP区域及其相关属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
vrrp_instance VI_1 {
state MASTER # MASTER或BACKUP,其他keepalived节点启动时会将priority较大的节点选举为MASTER
interface ens160 #对外提供服务的网络接口,用来发VRRP包
virtual_router_id 31 # 0-255,区分多个instance的VRRP组播, 同网段中不能重复,并且同一个vrrp实例使用唯一的标识
vrrp_mcast_group4 224.0.0.31 # 默认的组播地址是224.0.0.18,但是为了避免冲突也可以自定义
priority 100 # for electing MASTER, highest priority wins;MASTER最好比其他节点大50点
advert_int 1 # 发送VRRP包的时间间隔,单位为秒
authentication {
auth_type PASS # 认证类型,通常使用PASS,同一vrrp实例MASTER与BACKUP使用相同的密码才能正常通信
auth_pass 1133
}
virtual_ipaddress { # 可以配多个VIP地址,每个地址占一行,必须与RealServer上设定的VIP一致
10.10.247.3/24 brd 10.10.247.3 dev ens160 label ens160:0
}
}

​ 一般VIP在这里只填写IP地址即可,查询官网可知有很多参数进行配置:

1
2
3
4
5
6
7
8
virtual_ipaddress {
<IPADDR>[/<MASK>] [brd <IPADDR>] [dev <STRING>] [scope <SCOPE>]
[label <LABEL>] [peer <IPADDR>] [home]
[-nodad] [mngtmpaddr] [noprefixroute]
[autojoin] [no_track] [preferred_lft nn|forever]
192.168.200.17/24 dev eth1
192.168.200.18/24 dev eth2 label eth2:1
}
  • 虚拟服务器定义块,用来配置后端的真实服务器信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
virtual_server 10.10.247.3 80 {
delay_loop 6 # 延迟轮询时间(单位秒)
lb_algo sh # 后端调度算法:
lb_kind DR # LVS工作模式:NAT/DR/TUN
persistence_timeout 3 # 持久连接超时时间
protocol TCP # 转发协议protocol.TCP和UDP

sorry_server 10.10.247.123 80 # 所有RealServer宕机时,指向sorry server

# 后端应用服务器 IP PORT
real_server 10.10.247.23 80 {
weight 1
MISC_CHECK { # 健康检查
misc_path /root/http_hc_check.sh http://10.10.247.23/Default.aspx
}
}

real_server 10.10.247.24 80 {
weight 1
MISC_CHECK {
misc_path /root/http_hc_check.sh http://10.10.247.24/Default.aspx
}
}
}

​ 这块配置需要深入研究的很多,比如IVS有三种工作模式,本次配置使用了DR;后端的调度算法,一共有10种,本次配置使用了SH。网上的资料很多,可以根据实际情况来配置。

​ 真实的后端服务器需要进行健康检查来确认服务是否存活,检查的方式也有很多种,最常用的TCP_CHECK,HTTP_CHECK,这里选择了MISC_CHECK,使用自定义脚本来检查网站状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# http_hc_check.sh 
# 检查输入的URL地址是否返回200状态码
#!/bin/bash
if [ $# -ne 1 ];then
echo "Warning: check_command error."
exit 1
else
CHECK_URL=$1
CMD=`/usr/bin/curl -IL "${CHECK_URL}" 2>/dev/null | grep "200 OK" | wc -l`
if [ ${CMD} -eq 1 ];then
exit 0
else
exit 1
fi
fi

后端服务器配置

​ 通常后端服务器都是nginx或者Apache,这里的后端服务器是windows 2012 R2的系统,所以配置略微不同。需要将VIP配到后端服务器环回虚拟网卡上,这样才能正常使用DR模式。

  1. 首先添加环回虚拟网卡(Microsoft Loopback Adapter)

以管理员身份运行cmd后,在cmd命令窗口中执行:hdwwiz:

image-20200901215451138 image-20200901215510333 image-20200901215557206 image-20200901220319038 image-20200901220358973
  1. 配置IP地址

安装完成以后,在网络连接里会多一块网卡,将网卡重命名为realserver,配置虚IP地址

image-20200901221358759
  1. 一些其他需要注意的

网上的资料有很多针对旧版系统的配置,记录如下:

windows 2008的默认中,网卡的stronghost处于启用状态,这个设置可以防止跨接口转发数据包,这就表明:来自一个网络适配器的请求不会被环回适配器处理,因为这个请求来自于不同的网络适配器。为了将环回适配器从stronghost切换为weakhost,需要运行以上四条命令,要不然TCP的状态会一直处于SYN_RECV 状态。

将以下代码保存为bat执行,或直接在CMD中依次执行2~5行命令即可(双引号中需根据实际连线名称修改)

1
2
3
4
5
6
7
@echo off
netsh interface ipv4 set interface "realserver" weakhostreceive=enabled
netsh interface ipv4 set interface "realserver" weakhostsend=enabled
netsh interface ipv4 set interface "本地连线" weakhostreceive=enabled
netsh interface ipv4 set interface "本地连线" weakhostsend=enabled
pause

子网掩码255.255.255.255但在MS NT/2K/XP会被认为是无效的。 可以有以下解决方法: 在MS NT/2K/XP中,网络界面(interfaces)在HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters\Interfaces

找到适当的网络连接IP地址,修改subnetMask子网掩码,(注意修改方式)不需要重启,只需重新激活该网络连接即可。

LVS启动服务

​ 启动keepalived服务,观察是否vip成功配置在master上。f访问后端服务器Web地址,看是否可以正常访问。然后停止主机的进程,观察VI是否正常切换。

使用ipvsadm命令来查看lvs的信息:

1
2
3
4
5
6
7
8
9
# 查看当前配置的虚拟服务和各个RS的权重
ipvsadm -Ln

# 查看当前ipvs模块中记录的连接(可用于观察转发情况)
ipvsadm -lnc

# 查看ipvs模块的转发情况统计
ipvsadm -Ln --stats
ipvsadm -Ln --rate

配置监控LVS

​ 使用zabbix平台来监控LVS服务器和keepalived的状态。基本思路是利用自定义脚本来获取LVS的一些状态信息,然后通过zabbix_sender定时推送给zabbix server端。自定义脚本使用网上大神的一个py脚本,稍作修改适用于当前的环境。

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
51
52
53
54
55
#!/usr/bin/env python
#coding=utf-8
#date 2015-7-8
#auth :yangr
#function:汇报lvs的相关状态,有每秒连接数,每秒转发数,VIP主从切换.每秒转发带宽
#lvs_conns_sec,lvs_packets_sec,keepalived_vip_status

import os,commands,sys,time
#变量定义----------------------
#从zabbix_agentd.conf中获取server IP或hostname
zabbix_agent_file='/etc/zabbix/zabbix_agentd.conf'
if not os.path.exists(zabbix_agent_file):
sys.exit(4)
zabbix_server=commands.getstatusoutput('''grep '^ServerActive' %s |awk -F[=] '{print $2}' '''%zabbix_agent_file)[1].strip()
zabbix_hostname=commands.getstatusoutput('''grep '^Hostname' %s |awk -F[=] '{print $2}' '''%zabbix_agent_file)[1].strip()
if not zabbix_server or not zabbix_hostname:
sys.exit()
zabbix_server_port=10054
timestamp = int(time.time())
keepalived_vip=['10.10.247.3'] #指定VIP
tmp_file_path='/tmp/lvs_status.txt' #指定监控值输出文件
#-------------------------


def monit_lvs():
#获取每秒包转发数
status,lvs_packets_sec=commands.getstatusoutput('''tail -1 /proc/net/ip_vs_stats | /usr/bin/awk '{print strtonum("0x"$1),strtonum("0x"$2), strtonum("0x"$3), strtonum("0x"$4),strtonum("0x"$5)}'|awk '{print $2}' ''')

#获取每秒转发的流量
status,lvs_bit_sec=commands.getstatusoutput('''tail -1 /proc/net/ip_vs_stats | /usr/bin/awk '{print strtonum("0x"$1),strtonum("0x"$2), strtonum("0x"$3),strtonum("0x"$4), strtonum("0x"$5)}'|awk '{print $4}' ''')
#获取lvs会话连接数
status,lvs_conns_sec=commands.getstatusoutput('cat /proc/net/ip_vs_conn | wc -l')
#获取VIP状态,如值非0为master,为0则是backup,如果有变动,则进行了切换
status,lvs_keepalived_vip_status=commands.getstatusoutput('/sbin/ip addr |grep %s |wc -l'%keepalived_vip[0])
#如果本机有VIP,则取出VIP的最后一段十进制。
if int(lvs_keepalived_vip_status) != 0:
status,result_ip=commands.getstatusoutput(''' echo %s |awk -F '.' '{print $NF}' '''%keepalived_vip[0])
try:
lvs_keepalived_vip_status =int(result_ip)
except:
pass
#把 key值信息写到一个临时文件,格式为 hostname,key,timestamp,value
with open(tmp_file_path,'wb') as f:
f.write('%s %s %s %s\n'%(zabbix_hostname,'lvs_packets_sec',timestamp,lvs_packets_sec))
f.write('%s %s %s %s\n'%(zabbix_hostname,'lvs_bit_sec',timestamp,lvs_bit_sec))
f.write('%s %s %s %s\n'%(zabbix_hostname,'lvs_conns_sec',timestamp,lvs_packets_sec))
f.write('%s %s %s %s\n'%(zabbix_hostname,'lvs_keepalived_vip_status',timestamp,lvs_keepalived_vip_status))


if __name__=='__main__':
monit_lvs()
#把临时文件通过zabbix_sender命令发送到server端
send_data_cmd='zabbix_sender -vv -z %s -p %s -T -i %s'%(zabbix_server,zabbix_server_port,tmp_file_path)
#print send_data_cmd
os.popen(send_data_cmd)

​ 配置定时任务来执行脚本,crontab默认只能每分钟执行一次,所以写一个小脚本来每10秒跑一次。

1
2
3
4
5
6
7
8
9
10
11
# lvs_status_sender.sh
# 每10秒执行一次脚本

#!/bin/bash

step=10
for (( i = 0; i < 60; i=(i+step) )); do
$(python '/root/lvs_status_sender.py')
sleep $step
done
exit 0
1
2
# crontab -e
* * * * * bash /root/lvs_status_sender.sh >>/var/log/crontab.log2>&1

​ 然后就可以在Zabbix上配置模板,添加监控项和触发器。

注意:键值必须和脚本里的参数完全一致、类型必须是Zabbix采集器。

image-20200828235921687

分别给VIP状态和每秒转发数设置一个触发器,当VIP切换时会发生警告,当每秒转发数大于10W的时候也会报警。

image-20200829000134101

除此之外还配置了一个keepalived的进程监控,可以监控进程是否存活,一旦停止触发器就会告警动作。