一、前置知识
1. 网络基础
(1)数据链路层对应的是mac地址,mac地址是用来解决数据包从一个节点传递到相同链路另一个节点的寻址问题
(2)网络层对应的是ip地址,ip地址是用来解决数据包从一个网络传递到另一个网络的寻址问题
2. 七层负载均衡和四层负载均衡
(1)典型的7层负载均衡软件:Nginx,典型的4层负载均衡软件;LVS
(2)7层对应的是应用层,4层对应的是传输层,所以基于7层的负载均衡可以根据url等应用层信息进行负载,基于4层的负载均衡可以根据ip+port等信息进行负载
简单来说,7层负载更加灵活,4层负载效率更高
3. NAT技术
NAT是一种网络地址转换技术,最初是用来解决ipv4地址不足的问题。
一个经典的场景是家庭上网,一般有个路由器作为NAT设备,家庭所有成员共享同一个公网ip,如下图
路由器出产设置的默认ip是192.168.1.1,假设ISP(网络服务提供商)分配的公网ip是88.88.88.88,家里两台电脑都连上了路由器,然后同时访问百度,从网络层面看就会如下图这样
源ip地址会被替换,这就是SNAT。
但是如果仅仅只是替换ip,就会有个问题,baidu的响应数据包到达路由器上时,路由器将不知道应该发给内网的哪台机器,路由器对此有自己的解决办法,如下图
路由器会给需要通信的机器分配一个随机的端口,上图中,分别给192.168.1.2、192.168.1.3这两台机器分配了1066、1088端口,并且维护了一张分配映射表,那么在baidu响应的数据包到达路由器时,路由器就可以根据这张表找到对应的机器并转发回去。
上面就是家庭路由器的SNAT过程,经过SNAT之后,内网的ip地址会被改写为公网的ip地址,这样目标服务器才知道应该把响应数据包返回给谁。
除了SNAT,还有DNAT ,SNAT改写的是源地址,与其相反,DNAT改写的是目标地址。
回到主线,这篇文章的目标是希望把lvs这种四层负载均衡软件的三种网络模式说清楚,如果初次接触这几种模式,只看文字描述一般会觉得比较抽象,为了帮助理解,我画了很多配图,同时简化了底层细节,希望你能有所收获。
关于配图中英文简写:
CIP表示client ip
VIP表示virtual ip,这个是LVS的术语,暴露给客户端访问的虚拟ip
DIP表示director ip,也是LVS的术语,主要是用来与后端Real Server通信的ip
RIP表示Real Server ip,后端Real Server暴露的ip
二、主题
LVS的三种网络模式分别是NAT、DR、TUN。
NAT模式:基于NAT技术的模式
DR模式:直接路由模式,这个是实际应用最广泛的一种模式
TUN模式:隧道模式
先说NAT模式,
如下图,假设Client发送给LB Server的数据包被原封不动的转发给后端的Real Server,Real Server收到数据包后,发现数据包中的目标ip(VIP)并不是自己的ip,于是就会把这个数据包转发出去,最终又会回到LB Server,LB Server又转发给Real Server,一个“死循环”出现了,不是想要的效果。
所以,经过LB Server时,LB Server必须对数据包进行DNAT,如下图,CIP->VIP中的VIP会被替换为RIP,也就是CIP->RIP,这样Real Server就可以成功接收这个包。
但是依然不够,你想,Real Server收到的包是CIP->RIP,那么Real Server的响应包必然是RIP->CIP,但是从Client的视角来看,期望通信的目标ip其实是VIP,“不认识”Real Server发回的数据包,所以就不会处理,整个流程是没法走通的,就像下图这样,
可以看到,上图中Real Server的响应数据包RIP->CIP是直接返回给Client,未经过LB Server,如果说,CIP->RIP的数据包有经过LB Server,那么就可以在LB Server这里对RIP->CIP的数据包做些特别处理,使流程可以走通,如下图:
在LB Server上对数据包进行了SNAT,将源地址RIP替换成VIP,这样数据包到达Client时,Client便可以正常接收了。
那么,现在需要解决的问题就变成了怎么让RIP->CIP的响应数据包一定经过LB?
具体在实施时可以将Real Server的默认网关指向负载均衡服务器,具体操作命令有兴趣的自行百度即可。
上面就是NAT模式的大致流程。
但是在实际中会发现,大多数用户在访问互联网时,上行(Client请求)的数据量多数时候是明显小于下行(Server响应)的数据量的,这就意味着如果上行和下行的数据包都要经过LB Server的话,网络带宽将会成为LB Server的一个瓶颈,其主要还是来自下行数据带来的压力,如果说下行的数据包不再经过LB Server,那么这个问题就可以得到解决。
整个模型大概如下面这样,
Real Server响应给Client的数据包不再经过LB Server,而是直接返回给Client,这样LB Server只需要处理上行少量的请求数据,压力大减。
继续逆推,Real Server要想产生VIP->CIP的数据包,至少自身要有VIP,有点网络基础的都应该知道,IP在公网环境下肯定是唯一的,所以Real Server的这个VIP一定不能暴露出去,也就是说只有自己知道,是一个隐藏的VIP。
如图,
讨论到这里,问题就演变成了怎么隐藏VIP的问题。
先按下这个问题不说,假设现在已经实现了隐藏VIP的效果,那么问题又来了,LB Server真的能成功把CIP->VIP的数据包转发给Real Server吗?首先,数据包的目标ip就是LB Server自身,根据网络常识,这个数据包即便被往外扔,最终还是会回到这里,因为LB Server这里才是公开的目的地(公网VIP)。
所以,需要想办法让CIP->VIP的数据包成功转发给Real Server
解决办法非常巧妙,
根据前置知识知道,mac地址解决的是数据包在相同链路上点到点的传递问题,
利用这个特点,如果LB Server和Real Server是在同一个局域网内部,那么就可以先将数据包的mac地址替换成后端Real Server的mac地址,然后再转发出去,后端Real Server接收到这个数据包后,发现目标ip是自身的ip,于是就会正常接收这个数据包。
其实正是通过mac欺骗实现的同一局域网内数据包的转发,即使自己才是数据包的“归宿”。
整个流程如下图
上面这段说的是DR模式 — 直接路由模式,可以看到,DR模式有一个限制条件,LB Server和Real Server必须要在同一个局域网内,为了解决这个问题,再来看下TUN模式 — “隧道”模式
相比于DR模式,在数据包到达LB Server之后,LB Server会首先修改原始的数据包,将DIP和RIP作为新的源ip和目标ip,再把修改后的数据包转发出去,走正常的路由转发流程,这个数据包一定会到达Real Server,Real Server会首先解析出原始的数据包CIP->VIP,之后就和DR模式一样了,响应的数据包也不经过LB Server,直接返回Client。
看起来就像是在LB Server和Real Server之间有个“隧道”,只要数据包到达LB Server —“隧道”的一端,那么就会自动被“载着”传送到Real Server —“隧道”的另一端。
LUN模式的好处是没有DR那样的限制,LB Server和Real Server可以被放置在不同网络中。
关于TUN模式具体如何实施,可以自行百度,这种具体如何操作的资料网上有很多,跟着操作即可。
还有一个遗留问题,关于如何解决隐藏VIP,也不在这篇说了,有兴趣的可以先自行查阅资料。
欢迎关注我微信公众号《倔强的文哥》(一个表面冷酷,内心热乎的互联网码农),不定时分享各种Java技术经验、面试热题、Python实用小技巧。