简介
lvs是linux上的一种负载均衡技术。这篇文章简单记录测试和理解。
网络基础
网络的七层模型中,第三层是网络层,常见的系统维护工作就是网络层的维护,如ip配置,路由配置;第二层是数据链路层,是服务器底层网卡和交换机数据交换的基础,数据链路层的数据包的结构是地址+类型+数据,其中数据可以是第三层的ip报文。当服务器A向服务器B发送数据,除了在ip报文里面存放源ip地址和目标ip地址,还需要在数据链路层的数据包地址部分封装源MAC地址和目标MAC地址,服务器是如何根据ip地址获取目标MAC地址的呢?这里要分两种情况讨论:如果服务器A和服务器B在同网段,那么目标MAC地址就是B服务器配置了对应IP的网卡的MAC地址,这时候服务器A是通过arp协议,发送广播给同网段的服务器,查询指定ip的MAC地址;如果不在同网段那么目标MAC地址是网关设备(一般就是路由器)的MAC地址,服务器在数据包中封装的目标MAC就是网关设备的MAC(可以理解为什么服务器的网关要设置成与自身ip同网段,因为服务器也是通过arp获取网关MAC的),然后把包发送给网关设备,后面的转发由网关设备去完成。可以理解MAC就是数据包下一跳的网卡的MAC地址
arp测试
arp一般都是自动工作的,不需要手工配置,手工配置一般是网络层,简化了系统维护工作。
#arp相关命令
arp -n #查看ip和mac地址对应关系,可以用来排查ip冲突
arp -d xx.xxx.xx.xx #删除某个arp缓存
tcpdump -e arp host xx.xx.xx.xx #抓取目的ip或者源ip是xx.xx.xx.xx的arp数据包,并且打印mac地址信息
lvs原理
lvs的通信架构就是客户端发送数据到直连服务器(简写DR),再转到真实服务器(简写RS,一般多台)。
lvs有三种模式:
- nat:客户端发送数据到DR之后,DR修改数据包的源地址和目标地址,然后转发给RS,RS处理之后会给DR,DR再回给客户端。nginx也是这种工作模式。由于公网ip数量有限,局域网和互联网的通信也是这种模式,局域网有个路由器连接了互联网和局域网内部的网络,它同时有互联网ip和局域网ip。特点是配置简单,要求低。
- tun: 隧道模式,就是在基础的数据包外层再封装一层数据包结构。特点是支持跨机房。
- dr: 客户端发送数据到DR之后,DR修改数据包的MAC地址为真实服务器网卡的MAC地址,不修改数据包的源ip(源IP还是客户端IP),然后发送给RS(RS需要配置与DR相同的ip,这样RS收到数据包就会正常处理),RS处理之后,直接应答给客户端(因为DR发来的数据包的源ip是客户端ip),不需要经过DR。DR是使用最广泛的,因为数据包只有进来的时候需要DR分发给RS,RS返回数据的时候不需要经过DR处理,性能比nat和tun好。DR模式的网络处理瓶颈在于修改进来的网络包MAC,所以如果进来的网络包较小,处理能力就较强。
对于dr模式,需要在RS和DR上面配置同样的ip,那么不会存在ip冲突的问题吗?这个问题就需要上面提到的arp协议知识解答了。如果多台服务器(DR+RS)配置同一个ip(假设为vip),但是只有一个服务器会回应对vip的arp查询(实际就是DR服务器),那么所有到vip的数据包都会发送到这个能正常回应arp查询的服务器,不会发生ip冲突的问题。
为什么需要在RS上面也配置vip呢?因为这样RS收到DR修改了MAC地址的数据包的时候,解析数据包发现目的ip就是自己配置了的vip,就会正常处理并返回数据包;RS应答的数据包的源ip就是vip,目标ip是客户端ip,客户端收到后也能正常处理,形成一个闭环。或者如果RS返回数据包的源ip是其他ip,就会发生客户端问ipx一个问题,忽然收到一个来自ipy的回答,它对应不上来,处理不了。
lvs测试验证
- 环境准备:
一台DR,ip为18.1.99.238,两台RS,ip分别为18.1.99.236和18.1.99.237,vip为18.1.99.239,客户端则是任意一个linux服务器 - RS服务器配置:
在两台RS上安装nginx,并进行简单配置
[root@18.1.99.236 conf.d]# cat index.conf
server {
listen 8080;
location / {
return 200 "this is on 236\n";
}
}
[root@18.1.99.236 conf.d]# cd /usr/local/nginx/sbin/
[root@18.1.99.236 sbin]# ./nginx
[root@18.1.99.236 sbin]# curl http://18.1.99.236:8080
this is on 236
[root@18.1.99.237 conf.d]# cat index.conf
server {
listen 8080;
location / {
return 200 "this is on 237\n";
}
}
[root@18.1.99.237 conf.d]# cd /usr/local/nginx/sbin/
[root@18.1.99.237 sbin]# ./nginx
[root@18.1.99.237 sbin]# curl http://18.1.99.237:8080
this is on 237
- 网络配置
ipvsadm命令参数
-A #添加DR
-D #删除DR
-L #显示配置
-a #添加RS
-d #删除RS
-t #tcp协议
-s [rr|wrr|dh|sh|..] #DR分发给RS的策略
-g #dr模式,默认模式
-i #tun模式
-m #nat模式
#dr配置
[root@18.1.99.238 ~]# cd /etc/sysconfig
[root@18.1.99.238 ~]# touch ipvsadm
[root@18.1.99.238 ~]# systemctl start ipvsadm
[root@18.1.99.238 ~]# ipvsadm -A -t 18.1.99.239:8080 -s rr
[root@18.1.99.238 ~]# ipvsadm -a -t 18.1.99.239:8080 -r 18.1.99.236:8080 -g -w 1
[root@18.1.99.238 ~]# ipvsadm -a -t 18.1.99.239:8080 -r 18.1.99.237:8080 -g -w 1
[root@18.1.99.238 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 18.1.99.239:8080 rr
-> 18.1.99.236:8080 Route 1 0 0
-> 18.1.99.237:8080 Route 1 0 0
#客户端访问测试
curl http://18.1.99.239:8080/
#抓包监控
#DR和RS服务器上同时抓包和监控数据包错误
[root@18.1.99.237 ~]# tcpdump -nn -vv tcp port 8080
tcpdump: listening on ens192, link-type EN10MB (Ethernet), capture size 262144 bytes
19:16:01.154305 IP (tos 0x0, ttl 62, id 55678, offset 0, flags [DF], proto TCP (6), length 60)
18.1.98.240.54158 > 18.1.99.239.8080: Flags [S], cksum 0xb33c (correct), seq 4057379965, win 29200, options [mss 1460,sackOK,TS val 847014882 ecr 0,nop,wscale 7], length 0
^C
1 packet captured
1 packet received by filter
0 packets dropped by kernel
[root@18.1.99.237 ~]# sar -n EIP 1
Linux 4.18.0-193.el8.x86_64 (18.1.99.237) 04/10/2023 _x86_64_ (4 CPU)
07:15:49 PM ihdrerr/s iadrerr/s iukwnpr/s idisc/s odisc/s onort/s asmf/s fragf/s
07:16:00 PM 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
07:16:02 PM 0.00 1.00 0.00 0.00 0.00 0.00 0.00 0.00
[root@18.1.99.100 ~]# arp -n | grep 18.1.99.239
18.1.99.239 ether 00:50:56:b2:74:f4 C eth0
#可以发现RS18.1.99.237收到了DR转发过来的数据包
#但是由于RS上面没有配置18.1.99.239,导致网络包错误iadrerr增加,无法正常响应客户端的访问
#所以必须在RS在lo网卡上配置vip18.1.99.239,并且禁止RS响应关于18.1.99.239的arp查询请求
#ens192是我服务器的网卡,根据自己服务器情况设置
sysctl -w net.ipv4.conf.ens192.arp_ignore=1
ifconfig lo:1 18.1.99.239 netmask 255.255.255.255
#这个配置会导致从ens192收到的arp查询的地址如果不是ens192网卡上配置的,
#就不会回应。数据包都是从ens192进来的,vip配置在lo网卡上,所以RS服务器不会回应vip的arp查询请求。
#再次测试,客户端收到并且正常回复
#但是如果客户端也在18.1.99.0/24这个网段(和RS一个网段),还是会出现问题
#测试结果见下面图片arp_announce.png
#观察发现RS的确没有回应客户端的arp查询请求,
#但是RS回包给客户端的时候发现客户端和自己是一个网段的,于是RS通过arp请求查询客户端服务器MAC地址,
#这个arp请求包的源ip是vip,MAC是RS的网卡ens192的MAC地址,客户端收到这个请求之后会更新arp表,将vip和RS网卡ens192的MAC对应上了。
#所以linux上服务器A查询服务器B的MAC可能会触发服务器B的arp表更新
#为了解决这个问题,就需要调整RS系统参数
sysctl -w net.ipv4.conf.ens192.arp_announce=2
#这会导致RS查询客户端MAC的时候arp查询请求包的源ip是ens192网卡的ip,而不是lo网卡上配置的vip(虽然RS是打算使用vip和客户端通信),这样RS查询客户端的MAC地址的时候就不会更新客户端的arp表。
总结
想要理解lvs的工作原理,需要了解数据链路层的通信原理,arp协议;在这篇文章中,我通过一步一步添加配置,然后抓包测试,分析缺少配置会发生的问题,有助于深入了解lvs工作原理。其中最重要的是通过tcpdump抓包分析这个过程。
网上关于arp_announce的说明始终有点难以完全理解,但是我通过保持arp_announce为0,进行测试,观察导致的问题之后,就很好理解为什么要设置arp_announce为2了。