LVS(Linux Virtual Server) 是Linux内核中用于在OSI模型第四层提供负载均衡服务的机制。要使用LVS需要将其编译进内核或者成为内核模块。LVS类似于Linux的防火墙机制 netfilter。由内核提供功能框架,用户通过相应应用程序定义规则集。

LVS工作在netfilter的 INPUT 链上,这样说也许不太准确。内核在处理报文的流程中设置了5个hook (PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING), lvs 工作在 INPUT 这个hook上。
"到达Director的数据包首先会经过PREROUTING,而后经过路由发现其目标地址为本地某接口的地址,因此,接着就会将数据包发往INPUT(LOCAL_IN HOOK)。此时,正在运行内核中的ipvs(始终监控着LOCAL_IN HOOK)进程检查目标IP和端口或者防火墙标记,发现此数据包请求的是一个集群服务。于是,此数据包的本来到达本机(Director)目标行程被改变为经由POSTROUTING HOOK发往RealServer。这种改变数据包正常行程的过程是根据IPVS表(由管理员通过ipvsadm定义)来实现的。"

所以在LVS的director 上,配置防火墙规则应当小心以避免冲突。

LVS 分为 director 和 realserver。 director 上配置有 virtual ip, 是对外提供服务的IP地址。director 将客户端针对 virtual ip 的请求根据策略转发到事先定义好的realserver 上。director和后端 realserver 联系的IP地址叫DIP,realserver的ip叫 RIP。

LVS有四种工作模式或称 type:

NAT: 网络地址转换。director 选定一个realserver,将对 virtual ip请求报文修改目标IP为选定的realserver的RIP,然后转发给该realserver。然后realserver构建响应报文返回给客户端,由于realserver的响应报文的源IP是RIP,在经过director时源ip需被修改为VIP后才能发往client,否则client就会认为这是一个未曾请求的响应并丢弃。
NAT模型中报文在director中经过的hook
入站报文:prerouting---> input---> postrouting
响应报文:prerouting---> forward---> postrouting

NAT模型的特点:
realserver需将director的DIP设置成网关
由于响应报文也要经过director处理,而且通常响应报文比请求报文大得多,这样director的负载会比较大
支持端口映射

DR: Direct Routing
为了避免响应报文经由director处理时给director造成的负担,可以考虑让realserver直接以VIP为源IP构建响应报文。这就是所谓direct routing。
在DR模型下,后端realserver上也配置有VIP,director收到client发来的以VIP为目标IP的请求报文,director修改报文的2层帧首部,将目标MAC设置成选定的一个后端realserver的RIP对应的MAC后转发出去,realserver收到报文后,由于目标MAC是自己的,所以解开二层帧首部,发现目标IP也是自己的,就会继续解封装然后响应,响应报文以VIP为源IP发往client。

DR模型的特点:
director和realserver上都有配置VIP
需要特殊的策略避免多台机器上配置的VIP之间的冲突
realserver的响应是不用经过director直接发往client的
不支持端口映射

TUN
director将请求的IP报文封装到另一个以选定的realserver的RIP为目标IP的IP报文中(即IP隧道),realserver解封装外层IP头部之后发现目标IP是本机已有配置的VIP,realserver以VIP为源IP构建响应报文通过自己的路由返回给client

TUN模型的特点
TUN模型给LVS极大的扩展性,适用于分布各地的LVS集群
realserver上也要有配置VIP
不支持端口映射


FULL NAT
是NAT模型的一种扩展,比NAT模型多做的一点是,director在将请求转发给后端realserver时,还会修改源IP,确保当realserver的网关不是director时响应报文也会经过自己,director收到响应报文后,修改目标IP转发给client。

FULL NAT的特点:
director要处理realserver的响应报文
director和 realserver可以不在同一个IP网络内
支持端口映射


ARP问题:
DR模型中需要解决几个问题。首先要避免在前端网关将目标IP为VIP的报文发给realserver而不是director,这个有几种方法来实现:

1:禁止RealServer响应对VIP的ARP请求
2:在RealServer上隐藏VIP,以使得它们无法获知网络上的ARP请求;
3:基于“透明代理(Transparent Proxy)”或者“fwmark (firewall mark)”;
4:禁止ARP请求发往RealServers;

实践中常用的是通过修改两个内核参数(arp_announce和arp_ignore)来实现
以下是关于这两个内核参数的介绍:
arp_annouce:Define different restriction levels for announcing the local source IP address from IP packets in ARP requests sent on interface;
0 - (default) Use any local address, configured on any interface.
1 - Try to avoid local addresses that are not in the target's subnet for this interface.
2 - Always use the best local address for this target.

arp_ignore: Define different modes for sending replies in response to received ARP requests that resolve local target IP address.
0 - (default): reply for any local target IP address, configured on any interface.
1 - reply only if the target IP address is local address configured on the incoming interface.
2 - reply only if the target IP address is local address configured on the incoming interface and both with the sender's IP address are part from same subnet on this interface.
3 - do not reply for local address configured with scope host, only resolutions for golbal and link addresses are replied.
4-7 - reserved
8 - do not reply for all local addresses


arp_annouce是用来arp通告的级别的,而arp_ignore则是设置arp响应的模式。这里要设置arp_annouce=2,其实我不是很明白,意思大概是某个IP只通过IP与其在同一子网的接口通告出去,所以后面在lo上配置VIP时,如果VIP和RIP在同一个网络,必须将VIP的掩码配置成32位的
arp_ignore=1。这个很简单,某个网卡收到ARP请求后,如果该ARP请求的目标IP不是配置在该网卡上,即使本机其它网卡上有该目标IP,也不会响应。
通常realserver的VIP应该配置在本地回环接口lo上。假定RIP所在接口为eth0,则应该在eth0上限制对VIP的arp通告和响应。因此,因此,需要在sysctl.conf文件中定义如下配置:
#vim /etc/sysctl.conf
net.ipv4.conf.eth0.arp_ignore = 1
net.ipv4.conf.eth0.arp_announce = 2
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2

配置VIP
ifconfig lo:0 172.16.9.33 netmask 255.255.255.255 broadcast 172.16.9.33 up
注意,上面已经说过,不能让RIP和realserver上配置的VIP在同一子网内,所以有时候有必要将netmask长度配置成32位。我在做实验的时候就犯过这种错误,导致director无法和realserver通信的。




关于连接追踪:
如果有多台Realserver,在某些应用场景中,Director还需要基于“连接追踪”实现将由同一个客户机的请求始终发往其第一次被分配至的Realserver,以保证其请求的完整性等。其连接追踪的功能由Hash table实现。Hash table的大小等属性可通过下面的命令查看:

ipvsadm -Lcn

lvs的持久性连接有两方面:

1、把同一个client的请求信息记录到lvs的hash表里,保存时间使用persistence_timeout控制,单位为秒。persistence_granularity 参数是配合persistence_timeout的,在某些情况特别有用,他的值是子网掩码,表示持久连接的粒度,默认是255.255.255.255,也就是单独的client ip,如果改成,255.255.255.0就是client ip一个网段的都会被分配到同一个real server。

2、为了保证其时效性,Hash table中“连接追踪”信息被定义了“生存时间”。LVS为记录“连接超时”定义了三个计时器:
tcp的空闲超时时间;
lvs收到客户端tcp fin的超时时间;
无连接的UDP数据包(记录其两次发送数据包的时间间隔);
上面三个计时器的默认值可以由类似下面的命令修改,其后面的值依次对应于上述的三个计时器:
# ipvsadm --set 28800 30 600
查看tcp tcpfin udp的超时时间
ipvsadm -L -timeout
Timeout (tcp tcpfin udp): 900 120 30


LVS的持久连接的实现方法:
通常有三种,分别是PCC,PPC和基于防火墙标记的持久连接

pcc:
PCC用来实现把某个用户的所有访问在persistence_timeout内定向到同一台REALSERVER (0 表示所有端口)
ipvsadm -A -t $VIP:0 -s wlc -p 600

PPC
PPC用来把某个用户对同一服务的访问在超时时间内定向到同一台REALSERVER
ipvsadm -A -t $VIP:PORT -s wlc -p 600

基于防火墙标记的持久连接
通过给访问两个不同端口的报文打上同样的防火墙标记,可以定义两个端口的姻亲关系,然后在LVS上使用防火墙标记而非一个IP:PORT对来定义虚拟服务,这样可以实现将不同端口的服务定义为一个virtual server,在这个virtual server 上设置连接持久性

iptables -t mangle -A PREROUTING -d $VIP -p tcp --dport 80 -j MARK --set-mark 10
iptables -t mangle -A PREROUTING -d $VIP -p tcp --dport 443 -j MARK --set-mark 10
ipvsadm -A -f 10 -s wlc -p





存疑: 1、据说缺省情况下,Linux会以发送报文出去的接口的IP来构建响应报文。为此需要配置一条路由
route add -host $VIP dev lo:0
但我没能弄明白这条路由的作用,难道它能让响应报文通过lo口出去么?实践中不添加这条路由也是可行的。

2、我看到官网上关于ARP问题的介绍(http://www.linuxvirtualserver.org/docs/arp.html),里面讲述如何解决DR/TUN模型中的ARP问题。但我想
通常TUN模型中realserver和director之间是跨网络的,不存在ARP问题,如果多个realserver在同一网络,由于client的请求报文是通过IP隧道转发过来的,也不存在多个realserver争抢的问题。也许一个realserver检测到其它realsever上配置有相同的IP,就会导致不能正常使用该IP工作?

参考文档:
http://www.linuxvirtualserver.org/zh/lvs1.html
http://kb.linuxvirtualserver.org/wiki/Main_Page
http://kb.linuxvirtualserver.org/wiki/ARP_Issues_in_LVS/DR_and_LVS/TUN_Clusters
http://mageedu.blog.51cto.com/
http://w.gdu.me/wiki/sysadmin/lvs_persistence.html