超级流量负载方案ospf+lvs

2022-06-26 13:47:07 浏览数 (1)

本文简单介绍一下,如何基于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,具体信息如下:

代码语言:javascript复制
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
注: 所有虚拟机关闭防火墙
systemctl stop firewalld
systemctl disable firewalld

4. 环境部署

4.1 real server端

代码语言:javascript复制
安装nginx提供http服务。
yum install nginx -y
systemctl start nginx
systemctl enable nginx
对于real_server1: echo "RealServer1 10.0.1.129" > /usr/share/nginx/html/index.html
对于real_server2: echo "RealServer2 10.0.1.131" > /usr/share/nginx/html/index.html
systemctl restart nginx

配置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请求包。

代码语言:javascript复制
[root@localhost ~]# cat relserver.sh
#!/bin/bash
# Script to start LVS DR real server.
# description: LVS DR real server
. /etc/rc.d/init.d/functions

VIP=10.0.1.100 #修改为VIP
host=`/bin/hostname`
case "$1" in
    start)
        /sbin/ifconfig lo down
        /sbin/ifconfig lo up
        echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
        echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
        echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
        echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
        /sbin/ifconfig lo:0 $VIP broadcast $VIP netmask 255.255.255.255 up
        /sbin/route add -host $VIP dev lo:0
        ;;
    stop)
        /sbin/ifconfig lo:0 down
        echo 0 > /proc/sys/net/ipv4/conf/lo/arp_ignore
        echo 0 > /proc/sys/net/ipv4/conf/lo/arp_announce
        echo 0 > /proc/sys/net/ipv4/conf/all/arp_ignore
        echo 0 > /proc/sys/net/ipv4/conf/all/arp_announce
        ;;
    status)
        islothere=`/sbin/ifconfig lo:0 | grep $VIP`
        isrothere=`netstat -rn | grep "lo" | grep $VIP`
        if [ ! "$islothere" -o ! "isrothere" ];then
            # Either the route or the lo:0 device not found.
            echo "LVS-DR real server Stopped."
        else
            echo "LVS-DR real server Running."
        fi
        ;;
    *)
        # Invalid entry.
        echo "$0: Usage: $0 {start|status|stop}"
        exit 1
        ;;
    esac
    exit 0
[root@localhost ~]# ./realserver.sh start

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

代码语言:javascript复制
yum 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

验证结果:

代码语言:javascript复制
[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

[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

代码语言:javascript复制
[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 ospfd

4.3 router端

quagga配置

代码语言:javascript复制
//开启转发
[root@localhost ~]# echo 1 >  /proc/sys/net/ipv4/ip_forward
//Router作为网关的接口必须开启arp proxy:
[root@localhost ~]# echo 1 > /proc/sys/net/ipv4/conf/ens33/proxy_arp
[root@localhost ~]# yum install -y quagga
[root@localhost ~]# cat /etc/quagga/ospfd.conf
hostname 10.0.1.136    #主机ip地址
password test
log file /var/log/quagga/ospfd.log
log stdout
log syslog
service password-encryption
interface ens33
  ip ospf hello-interval 1
  ip ospf dead-interval 4
  ip ospf priority 1
  ip ospf cost 1
router ospf
  ospf router-id 10.0.1.136
  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.136 # HOSTNAME改为IP也可以
password test
enable password test
log file /var/log/quagga/zebra.log
#log syslog
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 permit 10  #将ospf路由映射到kernel
  match ip address prefix-list ANY
ip protocol ospf route-map ANY
line vty
  access-class 1
[root@localhost ~]#

验证结果:可以看到访router到达vip 10.0.1.100有两条链路,一条是10.0.1.133(lb1),一条是10.0.1.135(lb2)

代码语言:javascript复制
[root@localhost ~]# vtysh

Hello, this is Quagga (version 0.99.22.4).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

localhost.localdomain# show ip ospf neighbor

    Neighbor ID Pri State           Dead Time Address         Interface            RXmtL RqstL DBsmL
10.0.1.133        0 Full/DROther       3.274s 10.0.1.133      ens33:10.0.1.136         0     0     0
10.0.1.135        0 Full/DROther       3.934s 10.0.1.135      ens33:10.0.1.136         0     0     0
localhost.localdomain# show ip route
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, A - Babel,
       > - selected route, * - FIB route

K>* 0.0.0.0/0 via 10.0.1.2, ens33
O   10.0.1.0/24 [110/1] is directly connected, ens33, 00:03:18
C>* 10.0.1.0/24 is directly connected, ens33
O>* 10.0.1.100/32 [110/11] via 10.0.1.133, ens33, 00:03:09
  *                        via 10.0.1.135, ens33, 00:03:09
C>* 127.0.0.0/8 is directly connected, lo
localhost.localdomain# exit
[root@localhost ~]# ip route
default via 10.0.1.2 dev ens33 proto dhcp metric 100
10.0.1.0/24 dev ens33 proto kernel scope link src 10.0.1.136 metric 100
10.0.1.100 proto zebra metric 11
    nexthop via 10.0.1.133 dev ens33 weight 1
    nexthop via 10.0.1.135 dev ens33 weight 1
[root@localhost ~]#

4.4 client端

client端,需要配置路由,针对访问VIP的请求均跳转到router,这样router就可以利用ospf的ECMP,将流量分发到后端的lb1和lb2。

5. 结果验证

为了验证router确实最client端数据做了分流。将后端lb做了改动,lb1来对接real_server1, lb2来对接real_server2。通过返回的信息就可以确定lb是否为真的双活。

client1

代码语言:javascript复制
[root@localhost ~]# ifconfig|grep 10.0
        inet 10.0.1.138  netmask 255.255.255.0  broadcast 10.0.1.255
        ether 00:0c:29:c8:e5:37  txqueuelen 1000  (Ethernet)
        loop  txqueuelen 1000  (Local Loopback)
[root@localhost ~]# ip route
default via 10.0.1.2 dev ens33 proto dhcp metric 100
10.0.1.0/24 dev ens33 proto kernel scope link src 10.0.1.138 metric 100
10.0.1.100 via 10.0.1.136 dev ens33
[root@localhost ~]# traceroute 10.0.1.100
traceroute to 10.0.1.100 (10.0.1.100), 30 hops max, 60 byte packets
 1  10.0.1.136 (10.0.1.136)  0.260 ms  0.146 ms  0.146 ms
 2  10.0.1.100 (10.0.1.100)  0.293 ms  0.303 ms  0.226 ms
[root@localhost ~]# curl 10.0.1.100
server 10.0.1.129
[root@localhost ~]#

client2

代码语言:javascript复制
[root@localhost ~]# ifconfig|grep 10.0
        inet 10.0.1.137  netmask 255.255.255.0  broadcast 10.0.1.255
        ether 00:0c:29:09:ae:d5  txqueuelen 1000  (Ethernet)
        loop  txqueuelen 1000  (Local Loopback)
[root@localhost ~]# ip route
default via 10.0.1.2 dev ens33 proto dhcp metric 100
10.0.1.0/24 dev ens33 proto kernel scope link src 10.0.1.137 metric 100
10.0.1.100 via 10.0.1.136 dev ens33
[root@localhost ~]# traceroute 10.0.1.100
traceroute to 10.0.1.100 (10.0.1.100), 30 hops max, 60 byte packets
 1  10.0.1.136 (10.0.1.136)  0.278 ms  0.180 ms  0.091 ms
 2  10.0.1.100 (10.0.1.100)  0.452 ms  0.370 ms  0.266 ms
[root@localhost ~]# curl 10.0.1.100
server 10.0.1.131
[root@localhost ~]#

参考文献 https://www.cnblogs.com/lipengxiang2009/p/7451050.html https://segmentfault.com/a/1190000020299695

0 人点赞