本文简单介绍一下,如何基于ECMP,使用Quagga+LVS+Keepalived构建多活负载均衡方案
1. 背景介绍
负载均衡,主要用于大规模分布式集群下,提供高性能服务。为了给负载均衡器提供高可用,一般利用主备或者主主模式实现。主备模式即只有主负载均衡器提供负载均衡功能,当主负载均衡器出现异常时,自动切换到备负载均衡器,继续提供负载均衡功能。主主模式(也称之为双活负载均衡),两个负载均衡器同时提供负载均衡服务,全部为主模式,流量均摊到两个负载均衡设备。本文基于LVS+OSPF+Keepalived搭建了一套多活负载均衡。
1.1 LVS
LVS(Linux Virtual Server)即linux虚拟服务器。是一个虚拟的服务器集群系统,讲发送给VIP的请求,转发给对应的后端服务器。从而起到负载均衡的作用。后端服务器处理完请求,将结果返还给用户。LVS主要通过工作在linux内核层的ipvs来实现。而对于ipvs,我们可以用ipvsadm来进行配置。ipvsadm工作
在用户空间,ipvs工作在内核空间。ipvsadm和ipvs的关系就好比是iptables和netfilter的关系。综上所述,LVS由两部分组成,ipvs和ipvsadm
ipvsadm: LVS管理工具,通过ipvsadm定义和管理集群规则。
ipvs:LVS核心实现,根据定义好的集群规则进行工作。
注: LVS为四层负载均衡,基于ip和端口进行转发。
1.2 OSPF/ECMP
OSPF,开放式最短路径优先协议,一种基于链路状态的内部网关协议。每个OSPF路由器都包含了整个网络的拓扑。并计算通过网络的最短路径。OSPF会通过多播的方式自动对外传播检测到的网络变化。
RIP,即路由信息协议,是一个老的协议。RIP路由器向网络中周期性多播它的整个路由表,而不像ospf只多播网络的变化。RIP通过跳数来测量路由,任何超过15跳的路由,都被视为不可达。
ECMP:等价多路径协议。即当存在多条不同的链路到达同一目的地址时,利用ECMP可以同时使用多条链路,不仅增加了传输带宽,还可以无时延、无丢包的备份失效链路的数据传输。如果利用传统的路由算法,只能利用其中的一条链路进行数据的传输。
1.3 quagga
quagga是一个实现ospf的路由软件,用于模拟ospf协议。
2. 双活负载均衡架构
2.1 方案一、利用DNS轮训
配置两套主备模式的负载均衡器,分别配置VIP A和VIP B。在DNS server测针对同一域名,同时注册VIP A和VIP B。然后在请求域名的时候,dns通过轮训返回VIP A或者 VIP B。在服务端,需要通过memcached集群来共享不同节点的内存数据。从而保证session的持久性
2.2 方案二、利用ECMP
在此方案中,不存在主备模式的负载均衡器,所有负载均衡器均为主模式,配置同一VIP。在router测,利用quagga模拟ospf,可以看到到达VIP的链路有两条,当请求到达router的时候,会将流量分配到两台主负载均衡器上。
3. 环境准备
本文利用fusion创建了6台虚拟机。基于centos 7.5,具体信息如下:
- VIP: 10.0.1.100
- client: 10.0.1.132
- router: 10.0.1.136
- lb1: 10.0.1.133
- lb2: 10.0.1.135
- real_server1: 10.0.1.129
- real_server2: 10.0.1.131
注: 所有虚拟机关闭防火墙
1 | systemctl stop firewalld |
4. 环境部署
4.1 real server端
- 安装nginx提供http服务。
1 | yum install nginx -y |
- 配置vip
由于接收数据包的目的IP是VIP,所以需要将vip绑定到lo网卡,因为lo:vip不是用来通信的,所以这里的一定设置/32位子网掩码。对应修改脚本中的”VIP”变量。
由于real_server1, real_sever2, lb1, lb2均绑定vip到lo,所以需要关闭real_server1和real_server2的arp响应。对于linux内核参数arp_ignore和arp_announce的配置,参考文档 https://www.cnblogs.com/lipengxiang2009/p/7451050.html
arp_ignore设置为1:只响应目的IP地址为接收网卡上的本地地址的arp请求。所以对于lo网卡绑定VIP(lo是不接收外部数据包的),所以不会做出响应
arp_announce设置为2:忽略IP数据包的源IP地址,选择该发送网卡上最合适的本地地址作为arp请求的源IP地址。arp_announce的作用是控制系统在对外发送arp请求的数据包时,如何选择发送的源ip地址。因为对于任一主机,当接收到arp请求时,如果该arp的目的ip不是自己的ip,主机会把对端的ip和mac地址的对应关系存储到本地的mac地址表中。所以,如果real_server1发送arp的ip是lo的VIP,那么其他主机就会将VIP和mac的对应关系存储到mac地址表中,当下次像vip发送请求的时候,会直接到达real_server1,而不会经过lb。所以要设置arp_announce为2,这样就避免了通过lo:vip发送arp请求包。
1 | [root@localhost ~]# cat relserver.sh |
4.2 lb端
配置lb1和lb2,lb1和lb2为双主的负载均衡器,基于LVS和keepalived配置。
由于我们使用ospf来实现高可用,所以keepalived不需要配置vrrp功能。keepalived只使用期后端服务检测功能。
在keepalived-LVS集群的抢占模式下,master节点的网卡挂在VIP,此时VIP的设备是唯一的。但是在OSPF-LVS集群下,router根据ospf信息,通过修改报文的目的mac地址,转发到对应的LVS来实现负载均衡。并不根据VIP对应的arp信息,所以对应的每台LVS将VIP挂载在lo上。
- 配置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
57yum install keepalived -y
[root@localhost ~]# cat /etc/keepalived/keepalived.conf
global_defs {
router_id LVS1 #路由器标识
script_user root
enable_script_security
}
virtual_server 10.0.1.100 80 { #此处是VIP
delay_loop 5 #定义健康检查的时间间隔,单位为秒。
lb_algo rr #后端的负载均衡算法,rr为round-robin
lb_kind DR #lvs的调度类型,DR/TUN/NAT
#persistence_timeout 60 #保持客户端的请求在<INT>时间段内全部发到同一个真实服务器。适用于动态网页session共享的环境。默认六分钟。
persistence_granularity 255.255.255.255 #会话保持粒度,配合persistence_timeout使用,通过子网掩码来定义会话保持对一个IP生效还是对一组IP生效。默认值为255.255.255.255,表示单个客户端分配到一个RS上;255.255.255.0表示客户端IP所在的整个网段的请求都会分配给同一台RS
protocol TCP # 使用协议,TCP|UDP|SCTP
inhibit_on_failure on #配置该项,在检测到后端RS节点故障后将weight值改为0,而不是从IPVS中删除
ha_suspend # 在LB节点状态从Master切换到Backup时,不启用对RS节点的健康检查
sorry_server 127.0.0.1 80
real_server 10.0.1.131 80 {
weight 10
HTTP_GET {
url {
path /
status_code 200
}
connect_port 80
connect_timeout 2
retry 1
delay_before_retry 1
}
}
real_server 10.0.1.129 80 {
weight 10
HTTP_GET {
url {
path /
status_code 200
}
connect_port 80
connect_timeout 2
retry 1
delay_before_retry 1
}
}
}
[root@localhost ~]# systemctl reload keepalived
[root@localhost ~]# systemctl enable keepalived
验证结果:
[root@localhost ~]# ipvsadm -L
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP localhost.localdomain:http rr
-> 10.0.1.129:http Route 10 0 0
-> 10.0.1.131:http Route 10 0 0
[root@localhost ~]# - 配置vip
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[root@localhost ~]#ifconfig lo:0 10.0.1.100 netmask 255.255.255.255 up
[root@localhost ~]# ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.0.1.133 netmask 255.255.255.0 broadcast 10.0.1.255
inet6 fe80::af35:1bc3:d630:16c1 prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:4a:64:33 txqueuelen 1000 (Ethernet)
RX packets 169865 bytes 20546055 (19.5 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 131247 bytes 23749082 (22.6 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 746 bytes 57280 (55.9 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 746 bytes 57280 (55.9 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo:0: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 10.0.1.100 netmask 255.255.255.255
loop txqueuelen 1000 (Local Loopback)
[root@localhost ~]# - 配置quagga
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[root@localhost ~]# yum install quagga -y
[root@localhost ~]# cat /etc/quagga/ospfd.conf
hostname 10.0.1.133 #用主机ip代替即可
password test
log file /var/log/quagga/ospfd.log
log stdout
log syslog
service password-encryption
interface ens33 #主机ip所在的网卡名
ip ospf hello-interval 1
ip ospf dead-interval 4
ip ospf priority 0
ip ospf cost 1
router ospf
ospf router-id 10.0.1.133 #用主机ip代替
log-adjacency-changes
network 10.0.1.0/24 area 0.0.0.0
access-list 1 permit 127.0.0.1
line vty
access-class 1
[root@localhost ~]# cat /etc/quagga/zebra.conf
hostname 10.0.1.133 # 主机ip代替
password test
enable password test
log file /var/log/quagga/zebra.log
service password-encryption
interface ens33
access-list 1 permit 127.0.0.1
ip prefix-list ANY seq 5 permit 0.0.0.0/0 le 32
route-map ANY deny 10
match ip address prefix-list ANY
ip protocol ospf route-map ANY
line vty
access-class 1
[root@localhost ~]# systemctl start zebra
[root@localhost ~]# systemctl start ospfd4.3 router端
quagga配置
1 | //开启转发 |
验证结果:可以看到访router到达vip 10.0.1.100有两条链路,一条是10.0.1.133(lb1),一条是10.0.1.135(lb2)
1 | [root@localhost ~]# vtysh |
4.4 client端
client端,需要配置路由,针对访问VIP的请求均跳转到router,这样router就可以利用ospf的ECMP,将流量分发到后端的lb1和lb2。
5. 结果验证
为了验证router确实最client端数据做了分流。将后端lb做了改动,lb1来对接real_server1, lb2来对接real_server2。通过返回的信息就可以确定lb是否为真的双活。
client1
1 | [root@localhost ~]# ifconfig|grep 10.0 |
client2
1 | [root@localhost ~]# ifconfig|grep 10.0 |
参考文献