负载均衡集群 LVS 详解

Loadbalancer & LVS

Posted by Sunday on 2018-08-07

负载均衡集群

负载均衡集群指使用多台提供相同服务的服务器组成集群系统,提高服务的并发处理能力。负载均衡集群的前端使用一个调度器,将客户端请求平均分配到后端的服务器中,同时调度器可能还具有后端服务器状态检测的功能,将故障的服务器自动下线,使得集群具有一定的容错能力。

使用负载均衡集群能够有效的扩展服务的并发能力,负载均衡集群中的主机间应该尽量的「低耦合」,最好是「无状态」的,这样就能够方便的增加主机实现扩展。

常见的负载均衡器

根据工作在的协议层划分可划分为:

  • 四层负载均衡:根据请求报文中的目标地址和端口进行调度
  • 七层负载均衡:根据请求报文的内容进行调度,这种调度属于「代理」的方式
    根据软硬件划分:

硬件负载均衡:

  • F5 的 BIG-IP
  • Citrix 的 NetScaler
  • 这类硬件负载均衡器通常能同时提供四层和七层负载均衡,但同时也价格不菲
    软件负载均衡:
  • TCP 层:LVS,Haproxy,Nginx
  • 基于 HTTP 协议:Haproxy,Nginx,ATS(Apache Traffic Server),squid,varnish
  • 基于 MySQL 协议:mysql-proxy

LVS

LVS 是一个工作在四层的负载均衡器,它的实现和 iptables/netfilter 类似,工作在内核空间的 TCP/IP 协议栈上,LVS 工作在 INPUT Hook Funtion 上,并在 INPUT 设置附加规则,一旦客户端请求的是集群服务,LVS 会强行修改请求报文,将报文发往 POSTROUTING,转发至后端的主机。

和 iptables/netfilter 类似,LVS 也是两段式的:

  • ipvsadm:工作在用户空间,负责定义和管理集群服务的规则
  • ipvs:工作在内核中,在 2.4.23 之前,必须向内核打补丁,并重新编译内核。在 2.4.23 和 2.6 之后的版本,ipvs 直接内置在内核中。

LVS 集群的设备地址命名

  • VIP:Virtual IP,LVS 面向用户请求的 IP 地址
  • RIP:Real server IP,后端服务器用于和 LVS 通信的 IP 地址
  • DIP:Director IP,LVS 用户和后端服务器通信的 IP 地址
  • CIP:Client IP,客户端 IP 地址

LVS 的工作模型

LVS-NAT

11
LVS-NAT 模型类似于 DNAT,工作机制与 DNAT 一样,当客户端请求的是集群服务时,LVS 修改请求报文的目标地址为 RIP,转发至后端的 RealServer,并修改后端响应报文的源地址为 VIP,响应至客户端。

在 LVS-NAT 模型下,Director 进出请求报文都经过 Director,因此 Director 的压力是比较大的。

LVS-NAT 的特性:

  • 集群节点跟 Director 必须在同一个 IP 网络中
  • RIP 通常是私有地址,仅用于各集群节点间的通信
  • Director 位于 client 和 Realserver 之间,负责处理进出的所有报文
  • Realserver 必须将网关指向 DIP
  • 支持端口映射
  • 较大规模应用场景中,Director 易成为系统瓶颈(bottleneck),扩展有限最多20台

LVS-DR

11
DR 值 Direct Routing,直接路由,DR 模型中,Director 和 Realserver 处在同一网络中,对于 Director,VIP 用于接受客户端请求,DIP 用于和 Realserver 通信。对于 Realserver,每个 Realserver 都配有和 Director 相同的 VIP(此 VIP 隐藏,关闭对 ARP 请求的响应),仅用户响应客户端的请求,RIP 用于和 Director 通信。

当客户端请求集群服务时,请求报文发送至 Director 的 VIP(Realserver的 VIP 不会响应 ARP 请求),Director 将客户端报文的源和目标 MAC 地址进行重新封装,将报文转发至 Realserver,Realserver 接收转发的报文。此时报文的源 IP 和目标 IP 都没有被修改,因此 Realserver 接受到的请求报文的目标 IP 地址为本机配置的 VIP,它将使用自己的 VIP 直接响应客户端。

LVS-DR 模型中,客户端的响应报文不会经过 Director,因此 Director 的并发能力有很大提升。

LVS-DR 模型的特性:

  • 保证前端路由器将目标地址为 VIP 的报文通过 ARP 解析后送往 Director。
  • 静态绑定:在前端路由将 VIP 对应的目标 MAC 地址静态配置为Director VIP 接口的 MAC 地址。
  • arptables:在各 Realserver 上,通过 arptables 规则拒绝其响应对 VIP 的 ARP 广播请求
  • 修改内核参数:在 Realserver 上修改内核参数,并结合地址的配置方式实现拒绝响应对 VIP 的 ARP 广播请求
  • 各RIP 必须与 DIP 在同一个物理网络中
  • RS 的 RIP 可以使用私有地址,也可以使用公网地址,以方便配置
  • Director 仅负责处理入站请求,响应报文由 Realserver 直接发往客户端
  • Realserver 不能将网关指向 DIP,而直接使用前端网关
  • 不支持端口映射
    由于数据返回不走director ,而是直接由 realserver返回给用户。所认其承载能力比较强,单台lvs dr模式下后端负载的realserver 数量可以达到100台。不过也因为其比较简单,只有四层处理能力,所以其无法对后端server 的健康状态做检查。

LVS-TUN

11
和 DR 模型类似,Realserver 都配有不可见的 VIP,Realserver 的 RIP 是公网地址,且可能和 DIP 不再同一网络中。当请求到达 Director 后,Director 不修改请求报文的源 IP 和目标 IP 地址,而是使用 IP 隧道技术,使用 DIP 作为源 IP,RIP 作为目标 IP 再次封装此请求报文,转发至 RIP 的 Realserver 上,Realserver 解析报文后仍然使用 VIP 作为源地址响应客户端。

LVS-TUN 的特性:

  • 集群节点和可以跨越 Internet
  • RIP,DIP,VIP 都是公网地址
  • Director 仅负责处理入站请求,响应报文由 Realserver 直接发往客户端
  • Realserver 使用自己的网关而不是 Director
  • Realserver 只能使用支持隧道功能的操作系统
  • 不支持端口映射

LVS-FULLNAT

FULLNAT 由淘宝研发,目前还没有加入至 CentOS 可用的内核中,使用时需要向内核打补丁。
类似于 DNAT,它修改请求报文的源地址为 DIP,目标地址为 RIP 来实现转发。对于响应报文,源地址修改为 VIP,目标地址修改为 CIP 来实现转发。

特点:

  • RIP,DIP 可以使用私有地址
  • RIP 和 DIP 可以不再同一网络中,且 RIP 的网关不需要指向 DIP
  • 支持端口映射
  • 请求和响应报文都经由 Director

LVS 的调度算法

当 LVS 接受到一个客户端对集群服务的请求后,它需要进行决策将请求调度至某一台后端主机进行响应。LVS 的调度算法共有 10 种,按类别可以分为动态和静态两种类型。

静态调度算法

静态调度算法调度时不会考虑后端服务器的状态

  • rr:round robin,轮询,即简单在各主机间轮流调度
  • wrr:weighted round robin,加权轮询,根据各主机的权重进行轮询
  • sh:source hash,源地址哈希,对客户端地址进行哈希计算,保存在 Director 的哈希表中,在一段时间内,同一个客户端 IP 地址的请求会被调度至相同的 Realserver。sh 算法的目的是实现 session affinity(会话绑定),但是它也在一定程度上损害了负载均衡的效果。如果集群本身有 session sharing 机制或者没有 session 信息,那么不需要使用 sh 算法
  • dh:destination hash,和 sh 类似,dh 将请求的目标地址进行哈希,将相同 IP 的请求发送至同一主机,dh 机制的目的是,当 Realserver 为透明代理缓存服务器时,提高缓存的命中率。

动态调度算法

动态调度算法在调度时,会根据后端 Realserver 的负载状态来决定调度选择,Realserver 的负载状态通常由活动链接(active),非活动链接(inactive)和权重来计算。

  • lc:least connted,最少连接,LVS 根据 overhead = active*256 + inactive 计算服务器的负载状态,每次选择 overhead 最小的服务器
  • wlc:weighted lc,加权最少连接,LVS 根据 overhead = (active*256+inactive)/weight 来计算服务器负载,每次选择 overhead 最小的服务器,它是 LVS 的默认调度算法
  • sed:shortest expected delay,最短期望延迟,它不对 inactive 状态的连接进行计算,根据 overhead = (active+1)*256/weight 计算服务器负载,选择 overhead 最小的服务器进行调度
  • nq:never queue,当有空闲服务器时,直接调度至空闲服务器,当没有空闲服务器时,使用 SED 算法进行调度
  • LBLC:locality based least connection,基于本地的最少连接,相当于 dh + wlc,正常请求下使用 dh 算法进行调度,如果服务器超载,则使用 wlc 算法调度至其他服务器
  • LBLCR:locality based least connection with replication,基于本地的带复制功能的最少连接,与 LBLC 不同的是 LVS 将请求 IP 映射至一个服务池中,使用 dh 算法调度请求至对应的服务池中,使用 lc 算法选择服务池中的节点,当服务池中的所有节点超载,使用 lc 算法从所有后端 Realserver 中

IPVSADM

ipvsadm 用于配置 LVS 的调度规则,管理集群服务和 Realserver

管理集群服务

1
2
3
4
5
6
7
8
9
10
11
添加:-A -t|u|f service-address [-s scheduler]
-t: TCP协议的集群
-u: UDP协议的集群
service-address: IP:PORT
-f: FWM: 防火墙标记
service-address: Mark Number
修改:-E
删除:-D -t|u|f service-address

例如:
# ipvsadm -A -t 10.10.0.1:80 -s rr

管理集群服务中的 RS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
添加:-a -t|u|f service-address -r server-address [-g|i|m] [-w weight]
-t|u|f service-address:事先定义好的某集群服务
-r server-address: 某RS的地址,在NAT模型中,可使用IP:PORT实现端口映射;
[-g|i|m]: LVS类型
-g: DR
-i: TUN
-m: NAT
[-w weight]: 定义服务器权重
修改:-e
删除:-d -t|u|f service-address -r server-address

例如:
# ipvsadm -a -t 10.10.0.2:80 -r 192.168.10.8 -m
# ipvsadm -a -t 10.10.0.3:80 -r 192.168.10.9 -m

查看规则

1
2
3
4
5
6
-L|-l
-n:数字格式显式主机地址和端口
--stats:统计数据
--rate: 速率
--timeout: 显示tcp、tcpfin和udp的会话超时时长
-c: 显示当前的ipvs连接状况

删除所有集群服务

1
-C:清空 ipvs 规则

保存规则

1
2
3
-S
如:
# ipvsadm -S > /path/to/somefile

载入保存的规则

1
2
3
-R
如:
# ipvsadm -R < /path/from/somefile

DR 模型的配置

DR 模型的 Realserver 禁止 ARP 响应

对于 Linux 来说,地址是属于主机的,Linux 主机在开机时会通告连接所有网络内的所有其他主机自己的所有 ip 地址和 mac 地址。
可以利用 Linux 的特性,将VIP配置在 Realserver 的本地回环接口上作为别名,并使用 arp_ignore 和 arp_annouce 内核参数。

禁止 ARP 响应的方式

arptables:红帽系类系统上提供的程序

修改内核参数:
arp_ignore:定义接收到 ARP 请i去时的响应级别

  • 0:只要本地配置有响应地址,就给与响应
  • 1:仅在请求的目标地址配置请i去到达的接口上的时候,才进行响应

arp_announce:定义主机将自己的地址想外通告时的通告级别

  • 0:将本地任何接口上的任何地址向外通告
  • 1:向目标网络通告与其网络匹配的地址
  • 2:仅向本地接口上匹配的网络进行通告
    添加特殊的路由条目 Linux 主机在使用某一接口发出报文时,默认会使用此接口的 IP 作为源 IP 地址。

当请求 Realserver 时,Realserver 的 VIP 是 lo 接口的别名,而 VIP 对外的通信实际需要使用的却是 eth0 接口,因此需要添加路由条目,让主机在使用 VIP 向外通信时,强制使用 lo 端口,因而它会使用 lo 端口的地址作为源 IP 进行响应,并最终由 lo 接口转发至 eth0 接口发出报文。

1
# /sbin/route add -host $VIP dev lo:0

持久连接

添加一个集群服务为192.168.10.10:80,使用的调度算法为wrr,持久连接的保持时间是3600秒。当超过3600秒都没有请求时,则清空LVS的持久连接模板。这里我们可以在上面的策略后面加其他服务,指向其他realserver服务器 。

1
ipvsadm -A -t 192.168.10.10:80 -s wrr -p 3600

LVS_NAT配置

准备工作

时间同步

director服务器:VIP 192.168.10.10 DIP 192.168.10.100
RS1服务器:RIP 192.168.10.101 GATEWAY 192.168.10.100
RS2服务器:RIP 192.168.10.102 GATEWAY 192.168.10.100
客户端主机:CIP 192.168.10.251

director服务器操作

1
yum install ipvsadm -y
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
#!/bin/sh
# description: start LVS of Nat
VIP=192.168.10.10
DIP=192.168.10.100
RS1=192.168.10.101
RS2=192.168.10.102
. /etc/rc.d/init.d/functions
case "$1" in
start)
echo " start LVS of NAtServer"
# 开启路由转发
echo "1" >/proc/sys/net/ipv4/ip_forward
# 禁用发送ICMP IPv4重定向消息,默认是开启,值为1
echo "0" >/proc/sys/net/ipv4/conf/all/send_redirects
echo "0" >/proc/sys/net/ipv4/conf/default/send_redirects
echo "0" >/proc/sys/net/ipv4/conf/eth0/send_redirects
echo "0" >/proc/sys/net/ipv4/conf/eth1/send_redirects
#Clear IPVS table
ipvsadm -C
#set LVS
ipvsadm -A -t $VIP:80 -s rr
ipvsadm -a -t VIP:80 -r $RS1:80 -m -w 5
ipvsadm -a -t VIP:80 -r $RS2:80 -m -w 10
#Run LVS
ipvsadm
;;
stop)
echo "close LVS Nat server"
echo "0" >/proc/sys/net/ipv4/ip_forward
echo "1" >/proc/sys/net/ipv4/conf/all/send_redirects
echo "1" >/proc/sys/net/ipv4/conf/default/send_redirects
echo "1" >/proc/sys/net/ipv4/conf/eth0/send_redirects
echo "1" >/proc/sys/net/ipv4/conf/eth1/send_redirects
ipvsadm -C
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
esac

防火墙的一些操作

1
iptables -t nat -A POSTROUTING -s 192.168.10.0/24 -o eth0 -j MASQUERADE

注:
1、使用NAT模式必须要开启路由转发;
2、禁用发送ICMP ipv4重定向消息主要是出于安全考虑,并不影响ping检测;
3、ipvsadm 这里使用的是Round Robin轮询算法,-m 参数是指定使用的masquerading nat模式,-w 参数是指定权重,在 rr算法下,指定权重是没意义的,因为两台主机的的权重是一样的,每台主机都是公平平均轮询的。-p 3600 持久连接

RS1服务器操作

1
2
3
4
yum install -y nginx
echo "RS1 101" > /usr/share/nginx/html/index.html
systemctl start nginx
route del default gw 192.168.10.1 && route add default gw 192.168.10.100

注:需将在两台 RS 上设置网关的 IP 为 director 的内网 IP。

RS2服务器操作

1
2
3
4
yum install -y nginx
echo "RS2 102" > /usr/share/nginx/html/index.html
systemctl start nginx
route del default gw 192.168.10.1 && route add default gw 192.168.10.100

客户端操作

1
2
3
4
5
6
7
8
[root@sunday ~]# curl http://192.168.10.10
RS1 101
[root@sunday ~]# curl http://192.168.10.10
RS2 102
[root@sunday ~]# curl http://192.168.10.10
RS1 101
[root@sunday ~]# curl http://192.168.10.10
RS2 102
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
director操作
查看连接
ipvsadm -ln

IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.10.10:80 rr
-> 192.168.10.101:80 Route 1 0 0
-> 192.168.10.102:80 Route 1 0 0

修改算法
ipvsadm -E -t 192.168.10.10:80 -s wrr

保存配置
ipvsadm-save

LVS_DR配置

director操作

1
yum install ipvsadm -y
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
#!/bin/sh
VIP=192.168.10.10
RIP1=192.168.10.101
RIP2=192.168.10.102
case "$1" in
start)
echo "start LVS of DirectorServer"
/sbin/ifconfig eth0:0 $VIP netmask 255.255.255.255 broadcast $VIP up
/sbin/route add -host $VIP dev eth0:0
#Clear IPVS Table
ipvsadm -C
#Set Lvs
ipvsadm -A -t $VIP:80 -s rr
ipvsadm -a -t $VIP:80 -r $RIP1:80 -g
ipvsadm -a -t $VIP:80 -r $RIP2:80 -g
#Run Lvs
ipvsadm
;;
stop)
echo "close LVS Directorserver"
ipvsadm -C
/sbin/ifconfig eth0:0 down
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
esac

rs1 rs2操作

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
VIP=192.168.10.10
BROADCAST=192.168.10.255 #vip's broadcast
. /etc/rc.d/init.d/functions
case "$1" in
start)
echo "reparing for Real Server"
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
ifconfig lo:0 $VIP netmask 255.255.255.255 broadcast $BROADCAST up
/sbin/route add -host $VIP dev lo:0
;;
stop)
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
;;
*)
echo "Usage: lvs {start|stop}"
exit 1
esac

LVS_TUN

director主机

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
#!/bin/sh
# description: start LVS of Directorserver
VIP=192.168.10.10
RS1=192.168.1.101
RS2=192.168.1.102
RS3=192.168.0.100
. /etc/rc.d/init.d/functions
case "$1" in
start)
echo " start LVS of DirectorServer"
# set the Virtual IP Address
/sbin/ifconfig tunl0 $VIP broadcast $VIP netmask 255.255.255.0 up
/sbin/route add -host $VIP dev tunl0
#Clear IPVS table
ipvsadm -C
#set LVS
ipvsadm -A -t $VIP:80 -s rr
ipvsadm -a -t $VIP:80 -r $RS1:80 -i
ipvsadm -a -t $VIP:80 -r $RS2:80 -i
ipvsadm -a -t $VIP:80 -r $RS3:80 -i
#Run LVS
ipvsadm
#end
;;
stop)
echo "close LVS Directorserver"
ifconfig tunl0 down
/sbin/ipvsadm -C
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
esac

RS1-3主机

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/sh
# description: Config realserver tunl port and apply arp patch
VIP=192.168.10.10
. /etc/rc.d/init.d/functions
case "$1" in
start)
echo "Tunl port starting"
ifconfig tunl0 $VIP netmask 255.255.255.0 broadcast $VIP up
/sbin/route add -host $VIP dev tunl0
echo "1" >/proc/sys/net/ipv4/conf/tunl0/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/tunl0/arp_announce
echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
sysctl -p
;;
stop)
echo "Tunl port closing"
ifconfig tunl0 down
echo 1 > /proc/sys/net/ipv4/ip_forward
echo 0 > /proc/sys/net/ipv4/conf/all/arp_announce
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
esac

健康检测

在LVS模型中,director不负责检查RS的健康状况,这就使得当有的RS出故障了,director还会将服务请求派发至此服务器 。为实现像F5这样专业的LB设备的功能,LVS可以与ldirectord工具结合,自动屏蔽后端异常的服务器。也可以通过简单的脚本来实现后端健康检查,当curl获取后端某服务器的状态发现不对时,自动通过ipvsadm命令将该服务器删除。正常时同样通过ipvsadm命令加入。由于本篇暂时还未跳出LVS工具本身,所以这里通过脚本实现后端健康检查。脚本内容如下:

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
#!/bin/bash
VIP=192.168.10.100
CPORT=80
BACKUP=127.0.0.1
STATUS=("1""1")
RS=("192.168.10.101" "192.168.10.101")
RW=("2" "1")
RPORT=80
TYPE=g
while:; do
letCOUNT=0
add() {
ipvsadm -a -t $VIP:$CPORT -r $1:$RPORT -$TYPE -w $2
[ $? -eq0 ] && return0 || return1
}
del() {
ipvsadm -d -t $VIP:$CPORT -r $1:$RPORT
[ $? -eq0 ] && return0 || return1
}
for I in${RS[*]}; do
if curl --connect-timeout 1 http://$I &> /dev/null; then
if[ ${STATUS[$COUNT]} -eq 0 ]; then
add $I ${RW[$COUNT]}
[ $? -eq 0 ] && STATUS[$COUNT]=1
fi
else
if[ ${STATUS[$COUNT]} -eq 1 ]; then
del $I
[ $? -eq0 ] && STATUS[COUNT]=0
fi
fi
letCOUNT++
done
sleep 5
done

http://www.361way.com/lvs-nat/5187.html
http://www.361way.com/lvs-dr/5192.html
http://www.361way.com/lvs-persistent-healthy-check/5204.html
http://liaoph.com/lvs/