LVS(Linux Virtual Server) 入门详解
前言:
当第一次接触LVS,得知是国内的大佬弄出来的东东,还被加入了内核,简直佩服的不要不要的。不过,由于其功能比较简单,配置复杂,其实正真被使用的到不是很多。也许一提到软件的负载均衡,肯定先想到的是Nginx或者haproxy。当然,对于理解LVS的一些概念,各种类型,已经调度方式,对于以后学习应用层的负载均衡技术是有很大帮助的。今天,我们就先来说说LVS的一些概念性的东西。
正文:
首先,我们先以宏观的角度来说说LVS集群的各个类型,多目标DNAT的LVS-nat,直接路由的LVS-dr,此为默认类型,还有基于隧道的LVS-tun。其实基本的类型就以上三种,不过还有一个后面出来的东东LVS-fullnat。我们一个一个的来说~在讲具体LVS类型之前,我们先提几个术语,方便后面的描述,能用到的我会尽量在图中标记出来。整个服务器集群,由一堆提供服务的服务器和调度器组成,服务器是来提供服务器的,调度器使用来决定,当一个客户请求尽量,选择哪个服务器响应的东东。我们把这个调度器称为Virtual Server(VS),Director(调度器),Dispatcher(分发器),Balancer(负载均衡器),把后面的服务器称为Real Server(RS),客户端向调度器发送请求时,客户端的IP即Client IP简称为CIP,接收客户端请求的IP为Virtual Server IP(VIP),连接后端服务器的为Director IP(DIP),服务器端为Real Server IP(RIP)
LVS-nat
咳咳,图有点丑,将就着看把~nat做为网络中很基本的协议,估计是没人不懂把。不懂的道友求请自行学习下~不难!LVS-nat是多目标的DNAT,通过将请求报文中的目标地址和目标端口修改为挑选出来的RS的RIP和PORT实现转发。简单来说就是,用户客户端发送的请求都由VIP接收,然后VS根据调度策略选择某一个RIP。从客户端到调度器的数据包的源IP是CIP,目的IP是VIP,然后DNAT把其目的地址从VIP转换为RIP,所有数据包从DIP发送到RIP是,其源IP为CIP,目的IP为RIP。这样客户端的请求就到达了调度器选择的某一个RS上,然后此RS根据请求,向CIP发送一个响应,如果RIP直接响应CIP,不经过调度器,客户端收到一个源IP不是VIP的数据包,肯定莫名奇妙呀,明明我请求的是VIP,怎么会返回RIP的数据包呢?所以,RS响应客户端请求时,得重新把源地址为RIP的数据转换成VIP。然后客户端就收到响应啦~~~
OK,我们重新来总体描述下。假如现在同时有5个客户端请求发给调度器,然后调度器根据调度策略,把这5个请求送到后面几个服务器上,送的方式就是修改目的IP地址为选择的RIP。然后,各服务器的响应在经过调度器的转换发送给各个客户端。这样就实现了最基本的负载均衡啦~有木有很简单~
在此模式下 ,我们只用把VIP设置为公网IP,把其他的DIP,RIP设置为私有地址,就可以提供服务器并满足负载均衡的需求了。而且,此模式还支持端口映射~因为调度器只是发送一个请求到后端服务器,所以VS必须是linux,比较LVS是linux上的东东,还加到内核了,RS就可以是任何系统了,能提供服务就好。
nat模式有不少优势,但是劣势却是相当明显的,所有的请求,响应都必须经过调度器,可想调度器的负载有多大,当集群越来越大时,调度器就有可能称为整个集群的瓶颈。所以,我们就想,我们能不能只把请求发送给调度器选定的服务器,然后后端服务器直接响应客户端。因为,其实请求的消耗的资源,比起响应消耗的资源简直不要小太多,这样调度器即实现了调度的功能,有减少了负载,妙哉!其实,这种实现方式就是我们下面要讲到的基于直接路由的LVS-dr啦~让我们来一起看看这个有趣又复杂的东东把~
LVS-dr
虽然叫Direct Routing,但是跟我们熟悉的路由不太一样耶~来看看整个结构图把!图丑,见谅~(为了显示方便,我就不打水印了,引用要注明出处哈~我想是没人想看这丑图的!)
普通意义上来讲,用户从客户端到我们的调度器时,已经经过了无数个网络设备了,图上的路由器标识我们局域网的出接口所在的路由器,交换机表示VS和RS所在的局域网,专业点说就是同一个广播域。我们来开始描述下LVS-dr这个东东的结构哈~当客户端的请求经过无数个路由器,交换机到达我们的调度器时,此过程中源IP是客户端的CIP,目的IP是调度器的VIP,到达调度器的内网时,请求报文的源MAC地址为网关的MAC地址,目的MAC地址就是VIP所在的MAC地址。(这个东东必须明晰!不然后面理解有点困难,此处默认大家有基本的网络知识!)此结构中我们的VIP和DIP其实是同一个,然后我们把请求的数据包转发给处在同一网络中的VS选择的一个RS。此时的请求数据包的源IP依然是CIP,目的IP依然是VIP不变,但是目的MAC地址变为RIP所在接口的MAC地址。(必须知道,IP地址是逻辑上的地址,MAC地址才是真正的通信地址,所以我们才可以通过修改MAC地址发送到指定的RS)。RS接收到数据后,假如她能根据请求进行响应,直接发给客户端。这样就响应报文就不用经过调度器。不过,前面我们提到的问题还没解决呀,我们从RIP发出去的数据包,肯定源IP是RIP,客户端依然会觉得莫名奇妙呀!也许你会想,那我们可以给RIP所在的接口加一个VIP地址呀,让它出去的时候源IP是VIP不就行了,嗯。。想法很好,不过还是有问题,RS和VS在同一个网络,同一个网络可是不能有相同地址的。不然,当某个接口发送arp广播,问这个网络中VIP的MAC地址是谁呀,如果有两个接口响应,不是就乱套了吗?当然,这个问题可以有两个解决办法,第一我们可以在出接口的路由器上绑定arp,就把VIP绑定到VS的VIP所在的接口,然后把RIP也设置为VIP。这样问题就解决了~当然,一般来说我们的出接口路由器都在 运营商的机房里我们很难有权利去修改,所以也有第二种方法,我们可以通过arptables禁止RS的VIP去响应和通告arp。这样RS可以不告诉别人我有VIP地址了~完美!
再讲第三种之前,我们先整理一下,我们把VS和RS上都设置有VIP的ip地址,然后不让RS上的RIP接口响应arp也不向别人通告自己,这样的话,当客户端对vip的请求发到本地网络时,发送arp广播问VIP对应的mac地址是多少呀?就只有VS的VIP响应说,啊,VIP的MAC地址就是我们这个MAC地址!然后网关就通过MAC地址将客户端的请求发送给VIP所在的接口,因为是客户端请求,最后由网关发送到VIP的。所以,从网关出去时源IP为CIP,目的IP为VIP,源MAC为网关接口的MAC,目的IP为VIP所在接口的MAC,VS接收到请求数据包之后,根据调度策略选择一个合适的RS,然后从VIP接口发出,到RS所在的RIP接口,此时,源IP还是CIP,目的IP还是VIP,源MAC是VS的VIP所在的接口MAC,目的MAC是RIP所在接口的MAC,然后RS接收到数据包后,查看一下,嗯目的IP是我接口的IP,目的MAC是我接口的MAC,然后继续解封装到应用层,把请求给对应的服务器程序。服务器收到程序后,再从RS的VIP发送响应数据,此时源IP就为送出接口的IP即VIP,目的IP就是CIP啦~然后就实现所谓的响应报文不经过调度器啦。有没有感觉牛逼吊炸天呀 !!!!!!简直不要太牛!!!!不过有个问题还是没解决,RIP收到请求数据包后,在从此接口发出去的时候源IP依然是RIP,怎样才能实现上文提到的源IP为VIP呢?LVS在此处的设计相当精巧。在linux中有个思想就是,ip是属于内核的而不是接口的。即接口a上 有个IP1,接口b上有个ip2,当从a接口收到一个数据包,问接口a你有没有ip2呀,接口a回复说我有ip2,然后接口a收到ip2的数据包之后就转发给接口b。还有一点,一个数据包从哪个接口发出去,它的源 IP就是哪个接口的ip,即从接口a发出去的数据包的源ip为接口a的ip,但是从接口a发出,但转发到接口b发送给另外一个接口,源ip还是接口a的ip。根据这两个常识,第三种的实现如下:RS接收数据包的接口依然是RIP,但是我们把VIP设置到lo0环回接口上,因为接口a都会把lo0接口上的VIP告诉别人,所以我们得通过设置内核参数让ip地址就属于接口本身,不属于接口的ip他不能告诉别人。第二,我们让数据包从lo0口发出,然后从RIP所在的接口发送给外部主机,这样源IP就是lo0接口所在的VIP啦~~~~有木有很精巧!!!可以根据上面的图示进行理解。
因为LVS-dr模式是通过修改目的MAC地址来把请求报文从VS发送到RS的,而且MAC不能跨网络,所以LVS-dr模式只能用在同一广播域中。如果我们向把RS放在各个地方的不同网络中,怎么办?这就是我们接下来要讲的类型。
第一个就是LVS内核本身支持的类型LVS-tun,tun就是tunnel的简写即隧道。如果熟悉vpn的道友,肯定对隧道不陌生。不过,我们还是来仔细讲讲咯~
客户端向VS发出请求,源IP为CIP目的IP为VIP,然后给请求数据包在封装一层,源IP为DIP,目标IP为RIP,然后RS收到数据包,先解开一看,嗯目标地址是自己,再继续解封装,因为RS肯定也得支持隧道,所以就知道是CIP发来的请求,然后就可以用vip发出响应从RIP所在的接口发出直接到达客户端。此类型特殊,用的也不多,就简单这样讲讲~既然是要跨网络的,所以所有的RIP和VIP以及DIP都得是公网地址~
第三种是full-nat,和LVS-nat模式其是差不太多。在LVS-nat中,后端服务器为了能发送数据包给客户端,必须得经过VS,���以RS的网关得设置为DIP,即RS和VS必须在同一网段中,也就是不能跨网络,那如果我们非要让它实现跨网络怎么办?因为LVS-nat只是把目的地址VIP转换为RIP,以至于其不能跨网络,所以为了实现跨网络,我们就可以把请求数据包的源目ip地址都进行转换,这就是所谓的full-nat啦~
full-nat理解起来是最简单的,客户端向VS发送请求,VS接收到请求后,根据调度策略选择某一个RS,然后把请求的源ip地址从CIP改为DIP,目的IP从VIP改为RIP,发送到后端服务器,后端服务器收到请求后,再向VS响应请求,发送到VS,然后VS在通过地址转换发送到客户端,整个过程就是这么简单~~~所以VS和RS之间只要网络可达,无论RS在世界的哪个角落都能收到VS转发过来的请求,然后通过VS响应客户端。完美解决异地问题。
嗯嗯,到此呢,LVS的类型就将完了~除了dr复杂点其他都还好把。我们从宏观的角度描述了各类的LVS,我们很明白其实整个LVS负载均衡集群的核心就是那个调度器啦、而调度器的核心就是各种调度策略咯。所以,接下来我们就再讲讲各种调度方法把~我们可以把调度方法分为静态方法和动态方法,静态方法就是仅仅根据算法本身调度,动态方法就是根据算法及各RS当前的负载状态进行调度。
静态方法有如下几种:
RR,Round Robin,轮询调度,也就是无论个RS的性能,一个一个的进行调度。
WRR,Weighted RR,加权轮询,就是根据每个RS的性能不同,给每个设备不同的权值,权值大的就调度次数更多,比如有两台机器一个权值是3,一个权值是2,那么可以简单理解为先调用第一台机器3此,在调用第二台机器两次,这样一直轮询。
SH,Source Hashing,源地址哈希,假如我们提供服务的网站要保存用户的信息,比如说session会话之类的东东,如果用户1第一次访问被分配到RS1响应,然后把session之类的信息保存到了RS1,然后用户1第二次访问时,被分配到了RS2响应,以前保存的用户相关的信息没了?!这写问题就闹大了。所以,为了解决这类问题,就出现了所谓的源地址哈希,就是如果某个请求是第一次收到,我们就对请求报文的源ip地址进行hash计算,保存到一个哈希表中,并对应这个请求的RS地址 ,当第后面收到请求时,查看哈希表找到这个请求对应的RS,就直接传给上次访问的RS。这样就解决了,上述问题啦~
DH,Destination Hashing,目标地址哈希
动态方法如下:
LC:least connections,最少连接:即后端服务器那个处理的请求连接数最少就选哪个RS,公式为OverHead=Active*250+Inactive,活动链接数乘以250加上非活动连接数,越小越优先选择。不过此方式的虽然考虑到了后端的负载,但是并没区别出不同性能的服务器,所以此方法还是有不足之处、
WLC,Weight LC,加权最少连接,既然要烤炉每个设备的不同性能,我们也可以给不同性能的RS以不同的权重值,公式为OverHead=(Active*256+inactive)/weight,假如有三台机器,权重值分别是5,2,1.当第一个请求进来时,因为三台机器都没有链接,都是0,调度器就会根据RS列表从上到下一次调度,但是如果放在最前面的是性能最差的机器怎么办,如果我们想从第一个请求开始就根据负载能力调度,那么就有了下面这种方法。
SED,Shortest Expection Delay,最短延迟预期,因为相对于活动连接来说,非活动链接的消耗,小的可以忽略不计,所以我们可以直接不计算非活动链接数,以此来减小计算,SED公式如下Overhead=(activeconns+1)*256/weight,这样权重值大的一定会被先选到。不过依然有个问题,假如有两台机器,一台权重为1,另一台权重为10,当地一个请求进来时,经过计算第一台为256,第二个为25.6,然后就把第一个请求给第二台服务器,第二请求过来,经过计算依然给第2台服务器,如果一次过来9个请求,都会全部给第二台主机,那么第一台就一直处于等待状态,就算能力再差也至少得处理一个请求把~所以有下面这种调度方法。
NQ,Never Queue,永不排队。假如有两台RS,从vs过来两个请求,第一个给权重大的那个RS,第二个给空闲的哪个,后面的再根据负载计算进行分配,这样就保证了即使权重小也不会一直控线啦。后面还有LBLS,Locality-Based LC,基于本地的LC和LBLCR,LBLC with copy 带复制功能的LBLC。