实战 | LVS 负载均衡器理论基础及抓包分析
写在之前
LVS 是 Linux Virtual Server 的简写,即 Linux 虚拟服务器,是一个虚拟的服务器集群系统。本项目在1998年5月由章文嵩博士成立,是中国国内最早出现的自由软件项目之一。(百科)
LVS 基础
ipvsadm:用户空间的命令行工具,用于管理集群服务及集群服务上的RS等;(管理工具)
ipvs:工作于内核上的程序,可根据用户定义的集群实现请求转发;(内核模块)
VS:Virtual Server (虚拟服务)
DS:Director Server(负载均衡器)
RS:Real Server (后端真实处理请求的服务器)
CIP: Client IP (用户端IP)
VIP:Director Virtual IP (负载均衡器虚拟 IP)
DIP:Director IP (负载均衡器 IP)
RIP:Real Server IP (后端请求处理服务器 IP)
(图片来源于网络)
ipvs (IP Virtual Server) 实现了4层负载均衡,ipvs 运行在主机上,在Real Server 集群前充当LB(负载均衡器),ipvs 将基于 TCP 和 UDP 的服务请求转发到真实服务器上,并使真实服务器上面的服务,能够在前面 Director Server上、通过提供的 VIP 对外提供服务。
RR(Round Robin):轮询调度,在不考虑每台服务器处理能力的情况下,轮询调度算法是把来自用户的请求轮流分配给内部中的服务器,从1开始,直到N(内部服务器个数),然后重新开始循环;
WRR(Weight Round Robin):加权轮询,由于每台服务器的配置、跑业务类型不同,其处理能力也会不同,所以我们根据服务器的不同处理能力,给每个服务器分配不同的权值,使其能够接受相应权值数据的服务请求;
LC(Least Connections):最少链接,将网络请求调度到已建立的链接数最少的服务器上。如果集群系统的真实服务器具有相近的系统性能,采用“最小连接”调度算法可以较好地均衡负载;
WLC(Weighted Least Connections):加权最少链接,在集群系统中的服务器性能差异较大的情况下,调度器采用“加权最少链接”调度算法优化负载均衡性能,具有较高权值的服务器将承受较大比例的活动连接负载,调度器可以自动问询真实服务器的负载情况,并动态地调整其权值;
除上面一些常用调度算法外,还有一些几个,如下:LBLC(Locality-Based Least Connections)基于局部性的最少链接、LBLCR(Locality-Based Least Connections with Replication)带复制的基于局部性最少链接、SED(Shortest Expected Delay Scheduling)最短的期望的延迟、NQ(Never Queue Scheduling NQ)最少队列调度;
LVS 模式
DR、NAT、隧道模式;(后面重点讲解)
LVS 组件安装
yum -y install ipvsadm
-A 添加虚拟服务VIP
-D 删除虚拟服务VIP
-L 查看虚拟服务VIP
-C 清除所有虚拟服务VIP
-t 指定虚拟服务及端口 VIP:Port
-r 指定真实服务及端口 RS:Port
-w 指定权重
-m 指定转发模式为NAT
-g 指定转发模式为DR
-i 指定转发模式为IPIP隧道
NAT 模式
(图片来源于网络)
1. 当用户请求到达 DS 时,请求报文会先经过内核空间中的 PREROUTING 链,此时源 IP 为CIP,目的 IP 为 VIP;
2. 在 PREROUTING 规则链上进行检查目的IP是否为本机,如果是的话将数据包送至 INPUT 链;
4. POSTROUTING 链的作用就是选路,根据 INPUT 链中目标 IP,将数据包发送给 RS;
5. RS 发现数据包中的目标 IP 是自己的 IP,此时它会开始构建响应报文发并回给 DS, 此时报文的源IP为RIP,目标IP为 CIP;
NAT 模式 | |
IP | 角色 |
1.65.15.140/24 | CIP |
1.65.15.140/24 | DS (VIP 所在宿主) |
100.222.111.1/24 | VIP |
1.65.15.143/24 | RS |
1.65.15.144/24 | RS |
[root@master02 ~]# ip addr add 100.222.111.1/24 dev ens32
[root@master02 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:50:56:8e:53:c1 brd ff:ff:ff:ff:ff:ff
inet 1.65.15.141/24 brd 1.65.15.255 scope global ens32
valid_lft forever preferred_lft forever
inet 100.222.111.1/24 scope global ens32
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:46:24:ae:8c brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
[root@master02 ~]# ping -c 1 100.222.111.1
PING 100.222.111.1 (100.222.111.1) 56(84) bytes of data.
64 bytes from 100.222.111.1: icmp_seq=1 ttl=64 time=0.033 ms
--- 100.222.111.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.033/0.033/0.033/0.000 ms
[root@master02 ~]#
创建负载均衡service
[root@master02 ~]# ipvsadm -A -t 100.222.111.1:8000 -s rr
[root@master02 ~]#
添加 RS 到指定的负载均衡器下
[root@master02 ~]# ipvsadm -a -t 100.222.111.1:8000 -r 1.65.15.143:80 -m
[root@master02 ~]# ipvsadm -a -t 100.222.111.1:8000 -r 1.65.15.144:80 -m
[root@master02 ~]#
在client上面访问VIP
[root@master01 ~]# ping -c 1 100.222.111.1
PING 100.222.111.1 (100.222.111.1) 56(84) bytes of data.
--- 100.222.111.1 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
[root@master01 ~]#
我们发现并不通,由于这个VIP是虚拟的,需要添加路由,添加静态路由如下。
[root@master01 ~]# ip route add 100.222.111.1 via 1.65.15.141 dev ens32
[root@master01 ~]#
继续测试连通性
[root@master01 ~]# ping -c 1 100.222.111.1
PING 100.222.111.1 (100.222.111.1) 56(84) bytes of data.
64 bytes from 100.222.111.1: icmp_seq=1 ttl=64 time=0.088 ms
--- 100.222.111.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.088/0.088/0.088/0.000 ms
[root@master01 ~]#
发现可以ping通了,我们接下来验证service
[root@master01 ~]# curl -m 15 --retry 1 -sSL 100.222.111.1:8000
curl: (28) Connection timed out after 15001 milliseconds
[root@master01 ~]#
LB抓包
RS 抓包如下
[root@master02 ~]# iptables -t nat -A POSTROUTING -m ipvs --vaddr 100.222.111.1 --vport 8000 -j MASQUERADE
[root@master02 ~]# iptables -t mangle -A POSTROUTING -m ipvs --vaddr 100.222.111.1 --vport 8000 -j LOG --log-prefix '[k8svip ipvs]'
[root@master02 ~]#
[root@master01 ~]# curl -m 15 --retry 1 -sSL 100.222.111.1:8000
curl: (28) Connection timed out after 15001 milliseconds
[root@master01 ~]#
哪问题出在什么地方呢?由于PROC文件系统的/proc/sys/net/ipv4/vs/conntrack可控制IPVS是否对其连接启用Netfilter系统的conntrack功能,默认情况下是关闭状态,这里需要打开,如下。
[root@master02 ~]# sysctl net.ipv4.vs.conntrack=1
net.ipv4.vs.conntrack = 1
[root@master02 ~]#
下面这个图是是否开启conntrack功能的iptables日志对比,上面只是SYN,根本无法完成三次握手,缺少链路追踪,这里需要再开启conntrack功能。
再次测试,成功。
[root@master01 ~]# curl -m 15 --retry 1 -sSL 100.222.111.1:8000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
。。。。。
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[root@master01 ~]#
RS 抓包
LB抓包
到这里nat实验就完成了,可以看下数据包,源IP已经变成了LB的IP。
2. 客户端的请求和响应报文都要经由 DS 转发,在大并发、大流量的场景中,DS有可能会成为系统瓶颈;
3. 支持端口映射;
DR 模式
报文请求过程图
(图片来源于网络)
1. 当用户请求到达 DS 时,请求报文会先经过内核空间中的 PREROUTING 链,此时源IP为CIP,目的 IP 为 VIP;
2. 在 PREROUTING 规则链上进行检查目的IP是否为本机,如果是的话将数据包送至 INPUT 链;
6. 响应报文最终到客户端;
DR 模式 | ||
IP | MAC | 角色 |
1.65.15.140/24 | 00:50:56:8e:39:98 | CIP |
1.65.15.140/24 | 00:50:56:8e:53:c1 | DS(VIP 所在宿主) |
1.65.15.145/24 | 00:50:56:8e:53:c1 | VIP |
1.65.15.143/24 | 00:50:56:8e:00:47 | RS |
1.65.15.144/24 | 00:50:56:8e:0f:e8 | RS |
[root@master02 ~]# ping -c 1 1.65.15.145
PING 1.65.15.145 (1.65.15.145) 56(84) bytes of data.
From 1.65.15.141 icmp_seq=1 Destination Host Unreachable
--- 1.65.15.145 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms
[root@master02 ~]#
添加 VIP 网卡信息
[root@master02 ~]# ifconfig ens32:0 1.65.15.145/24 up
[root@master02 ~]#
创建 service 负载均衡
[root@master02 ~]# ipvsadm -A -t 1.65.15.145:80 -s rr
[root@master02 ~]#
把 RS 添加到负载均衡
[root@master02 ~]# ipvsadm -a -t 1.65.15.145:80 -r 1.65.15.143:80 -g
[root@master02 ~]# ipvsadm -a -t 1.65.15.145:80 -r 1.65.15.144:80 -g
[root@master02 ~]#
客户端连通性测试
[root@master01 ~]# ping -c 1 1.65.15.145
PING 1.65.15.145 (1.65.15.145) 56(84) bytes of data.
64 bytes from 1.65.15.145: icmp_seq=1 ttl=64 time=0.078 ms
--- 1.65.15.145 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.078/0.078/0.078/0.000 ms
[root@master01 ~]#
负载测试
[root@master01 ~]# curl -m 15 --retry 1 -sSL 1.65.15.145:80
curl: (7) Failed connect to 1.65.15.145:80; 没有到主机的路由
[root@master01 ~]#
发现负载服务不通,进行抓包分析
dr模式LB抓包
dr模式RS抓包
[root@master03 ~]# ifconfig lo:0 1.65.15.145/32 up
[root@master03 ~]# route add -host 1.65.15.145 dev lo
[root@master03 ~]#
此时问题又来了,这就相当于在一个局域网内有两个相同的IP,IP重复了怎么办?办法就是隐藏这个虚拟网卡,不让它回复ARP,其他主机的neigh也就不可能知道有这么个网卡的存在了。
[root@node01 ~]# echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
[root@node01 ~]# echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
[root@node01 ~]#
因为我们在RS上都配置了VIP,因此此时是存在IP冲突的,当外部客户端向VIP发起请求时,会先发送arp请求,此时调度器和RS都会响应这个请求。如果某个RS响应了这个请求,则之后该客户端的请求就都发往该RS,并没有经过LVS,因此也就没有真正的负载均衡,LVS也就没有存在的意义。因此我们需要设置RS不响应对VIP的arp请求,这样外部客户端的所有对VIP的arp请求才会都解析到调度器上,然后经由LVS的调度器发往各个RS。
arp_announce=2,网卡在发送arp请求时使用出口网卡IP作为源IP。
测试连通性
[root@master01 ~]# curl 1.65.15.145:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
。。。。
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[root@master01 ~]#
RS抓包如下
LB抓包如下
1. 前端路由器将目标 IP 为 VIP 时的请求报文,发往DS,需要在前端网关做静态绑定,RS上使用 arptables,并且在RS上修改内核参数以限制 arp 通告及应答级别;
4. 请求报文要经由 DS,但响应时不经过 DS,而是由 RS 直接发往 客户端;
5. DR 模式不支持端口映射;
IPIP 模式
报文请求过程图
(图片来源于网络)
1. 当用户请求到达 DS 时,请求报文会先经过内核空间中的 PREROUTING 链,此时源IP为CIP,目的 IP 为 VIP;
2. 在 PREROUTING 规则链上进行检查目的IP是否为本机,如果是的话将数据包送至 INPUT 链;
3. 数据包到达INPUT链后,IPVS 会比对数据包请求的服务是否为集群服务,若是,在请求报文的首部再次封装一层 IP 报文,封装源 IP 为 DIP,目标 IP 为 RIP,然后发至POSTROUTING链,此时源 IP 为 DIP,目标 IP 为 RIP;
4. POSTROUTING 链根据最新封装的 IP 报文,将数据包发至 RS(因为在外层封装多了一层IP首部,所以可以理解为此时通过隧道传输);此时源 IP 为 DIP,目标 IP 为 RIP;
6. 响应报文最终送达至客户端;
IPIP 实验
IPIP模式 | |
IP | 角色 |
1.65.15.140 | CIP |
1.65.15.141 | DS |
1.65.15.145 | VIP |
1.65.15.143 | RS |
1.65.15.144 | RS |
# 配置 VIP
[root@master02 ~]# ifconfig ens32:1 1.65.15.145 netmask 255.255.255.0 up
# 测试连通性
[root@master02 ~]# ping -c 1 1.65.15.145
PING 1.65.15.145 (1.65.15.145) 56(84) bytes of data.
64 bytes from 1.65.15.145: icmp_seq=1 ttl=64 time=0.026 ms
--- 1.65.15.145 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.026/0.026/0.026/0.000 ms
[root@master02 ~]#
[root@master02 ~]# ipvsadm -A -t 1.65.15.145:80 -s rr
[root@master02 ~]#
# 添加 RealServer 作为 VIP 的后端
[root@master02 ~]# ipvsadm -a -t 1.65.15.145:80 -r 1.65.15.143:80 -i
[root@master02 ~]# ipvsadm -a -t 1.65.15.145:80 -r 1.65.15.144:80 -i
[root@master02 ~]#
[root@master02 ~]# echo 1 >/proc/sys/net/ipv4/ip_forward
[root@master02 ~]#
[root@master02 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 1.65.15.145:80 rr
-> 1.65.15.143:80 Tunnel 1 0 0
-> 1.65.15.144:80 Tunnel 1 0 0
[root@master02 ~]#
# 加载下 tunl 模式
[root@node01 ~]# modprobe ipip
# 为 tunl0 网口配置 IP(即VIP)
[root@node01 ~]# ifconfig tunl0 1.65.15.145 netmask 255.255.255.255 up
[root@node01 ~]#
修改内核参数(所有RS服务器)
[root@node01 ~]# echo 0 > /proc/sys/net/ipv4/ip_forward
[root@node01 ~]# echo 1 > /proc/sys/net/ipv4/conf/tunl0/arp_ignore
[root@node01 ~]# echo 2 > /proc/sys/net/ipv4/conf/tunl0/arp_announce
[root@node01 ~]# echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
[root@node01 ~]# echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
[root@node01 ~]# echo 0 > /proc/sys/net/ipv4/conf/tunl0/rp_filter
[root@node01 ~]# echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
1:开启严格的反向路径校验,对每个入访的数据包,通过指定网卡,校验其出访路径是否为最佳路径,如果不是最佳路径,则直接丢弃;(Linux 服务器默认是1)
测试访问
[root@master01 ~]# curl -m 15 --retry 1 -sSL http://1.65.15.145
<!DOCTYPE html>
<html>
<head>
。。。
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[root@master01 ~]#
RS 抓包
图一
图二
通过RS抓包,可以清楚看出IPIP隧道数据包封装的格式,但回包的时候,直接就回了;
DS抓包(转发server)
这个数据包,感觉有问题,后面我再查下,分析下原因,如果有人知道,也可交流;
2. RS 的网关不能、也不可能指向DIP;
3. 请求报文要经由 DS,但响应不经过 DS;
4. 不支持端口映射;
5. RS 的 OS 得支持隧道功能;
总结
对比项 | NAT 模式 | DR模式 | IPIP模式 |
实现要点 | 地址转化 | MAC转化 | 封装IP |
复杂度 | 简单 | 复杂 | 复杂 |
操作系统 | 任何操作系统 | 服务器支持虚拟网卡,并且能够禁用设备的ARP响应 | 须支持IP隧道模式,目前只有Linux |
网络 | 私有地址即可 | 合法局域网地址,并且必须同一网段 | 可私网IP、可公网IP |
支持RS数量 | 依赖均衡器处理能力而定,一般支持10~20个 | 支持上100个RS | 支持上100个RS |
网关要求 | 均衡器(DS)即为RS网关 | RS同自己的网关或路由直接连接,不经过负载均衡器 | RS同自己的网关或路由直接连接,不经过负载均衡器 |
安全性 | 较好,采用内部IP | 采用公网IP时,较差,结点完全暴露 | 采用公网IP时,较差,结点完全暴露 |
IP要求 | 仅需要一个合法的IP作为VIP | 除VIP外,还需要RS拥有合法的IP地址,可以直接路由至客户端 | 除VIP外,还需要RS拥有合法的IP地址,可以直接路由至客户端 |
扩展性(主要从IP网段分析) | 差 | 好 | 很好 |
由于各个公司实际情况不同、业务类型不同、容灾策略、基础架构不同,需要结合自己公司的情况进行分析,然后选择合适的解决方案。
后台回复“加群”,带你进入高手如云交流群
推荐阅读:
下载
▼
喜欢,就给我一个“在看”