ipvs详解
lvs详解
简介
- LVS是Linux Virtual Server的简称,即Linux虚拟服务器。从内核版本2.4开始,已经完全内置了LVS的各个功能模块,无需给内核打任何补丁,可以直接使用LVS提供的各种功能。通过LVS + haproxy + keeplived可以让Linux操作系统变成一个高可用、高性能的负载均衡器
组件和专业术语
组件:
- ipvsadm:用户空间的客户端工具,用于管理集群服务及集群服务上的RS等;
- ipvs:工作于内核上的netfilter INPUT钩子之上的程序,可根据用户定义的集群实现请求转发;
专业术语:
- 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
工作模型
VS工作在内核空间,基于内核包处理框架Netfilter实现的一种负责均衡技术,其在工作模式如上图,大致过程:
- 当客户端的请求到达负载均衡器的内核空间时,首先会到达PREROUTING链。
- 当内核发现请求数据包的目的地址是本机时,将数据包送往INPUT链。
- LVS由用户空间的ipvsadm和内核空间的IPVS组成,ipvsadm用来定义规则,IPVS利用ipvsadm定义的规则工作,IPVS工作在INPUT链上,当数据包到达INPUT链时,首先会被IPVS检查,如果数据包里面的目的地址及端口没有在规则里面,那么这条数据包将被放行至用户空间。
- 如果数据包里面的目的地址及端口在规则里面,那么这条数据报文将被修改目的地址为事先定义好的后端服务器,并送往POSTROUTING链。
- 最后经由POSTROUTING链发往后端服务器。
负载均衡模式
LVS-NAT模式
原理
- NAT模式全称Virtualserver via Network address translation(VS/NAT),是通过网络地址转换的方法来实现调度的。首先调度器(Director)接收到客户的请求数据包时(请求的目的IP为VIP),根据调度算法决定将请求发送给哪个后端的真实服务器(RS)。然后调度器就把客户端发送的请求数据包的目标IP地址及端口改成后端真实服务器的IP地址(RIP)和端口,这样真实服务器(RS)就能够接收到客户的请求数据包了。真实服务器响应完请求后,查看默认路由(NAT模式下我们需要把RS的默认路由设置为DS服务器。)把响应后的数据包发送给DS,DS在接收到响应包后,把包的源地址改成虚拟地址(VIP)然后发送回给客户端
-
- 当用户请求到达DirectorServer,此时请求的数据报文会先到内核空间的PREROUTING链。 此时报文的源IP为CIP,目标IP为VIP
- PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链
- IPVS比对数据包请求的服务是否为集群服务,若是,修改数据包的目标IP地址为后端服务器IP,然后将数据包发至POSTROUTING链。 此时报文的源IP为CIP,目标IP为RIP ,在这个过程完成了目标IP的转换(DNAT)
- POSTROUTING链通过选路,将数据包发送给Real Server
- Real Server比对发现目标为自己的IP,开始构建响应报文发回给Director Server。 此时报文的源IP为RIP,目标IP为CIP ,由于Real Server关于CIP的路由指向Director Server,由此会将相应数据包发送给Director Server
- Director Server在响应客户端前,此时会将源IP地址修改为自己的VIP地址(SNAT),然后响应给客户端。 此时报文的源IP为VIP,目标IP为CIP
NAT模式优缺点:
NAT技术将请求的报文和响应的报文都需要通过DS进行地址改写,因此网站访问量比较大的时候DS负载均衡调度器有比较大的瓶颈,一般要求最多只能10-20台节点
节省IP,只需要在DS上配置一个公网IP地址就可以了
NAT模式支持对IP地址和端口进行转换。即用户请求的端口和真实服务器的端口可以不一致
LVS-DR模式
- Virtual Server via Direct Routing(VS-DR),也叫直接路由模式,用直接路由技术实现虚拟服务器。当参与集群的计算机和作为控制管理的计算机在同一个网段时可以用此方法,控制管理的计算机接收到请求包时直接送到参与集群的节点。直接路由模式比较特别,很难说和什么方面相似,前种模式基本上都是工作在网络层上(三层),而直接路由模式则应该是工作在数据链路层上(二层)
- 原理
- DS和RS都使用同一个IP对外服务。但只有DS对ARP请求进行响应,所有RS对本身这个IP的ARP请求保持静默(对ARP请求不做响应),也就是说,网关会把对这个服务IP的请求全部定向给DS,而DS收到数据包后根据调度算法,找出对应的 RS,把目的MAC地址改为RS的MAC并发给这台RS。这时RS收到这个数据包,则等于直接从客户端收到这个数据包无异,处理后直接返回给客户端。由于DS要对二层包头进行改换,所以DS和RS之间必须在一个广播域,也可以简单的理解为在同一台交换机上
- 工作流程
- 当用户请求到达Director Server,此时请求的数据报文会先到内核空间的PREROUTING链。 此时报文的源IP为CIP,目标IP为VIP
- PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链
- IPVS比对数据包请求的服务是否为集群服务,若是,将请求报文中的源MAC地址修改为DIP的MAC地址,将目标MAC地址修改RIP的MAC地址,然后将数据包发至POSTROUTING链。 此时的源IP和目的IP均未修改,仅修改了源MAC地址为DIP的MAC地址,目标MAC地址为RIP的MAC地址
- 由于DS和RS在同一个网络中,所以是通过二层,数据链路层来传输。POSTROUTING链检查目标MAC地址为RIP的MAC地址,那么此时数据包将会发至Real Server
- RS发现请求报文的MAC地址是自己的MAC地址,就接收此报文。处理完成之后,将响应报文通过lo接口传送给eth0网卡然后向外发出。 此时的源IP地址为VIP,目标IP为CIP
- 响应报文最终送达至客户端
-
- 在前端路由器做静态地址路由绑定,将对于VIP的地址仅路由到Director Server
- 在arp的层次上实现在ARP解析时做防火墙规则,过滤RS响应ARP请求。修改RS上内核参数(arp_ignore和arp_announce)将RS上的VIP配置在网卡接口的别名上,并限制其不能响应对VIP地址解析请求
- RS可以使用私有地址;但也可以使用公网地址,此时可以直接通过互联网连入RS以实现配置、监控等
- RS的网关一定不能指向DIP
- 因为DR模式是通过MAC地址改写机制实现转发,RS跟Dirctory要在同一物理网络内(不能由路由器分隔)
- 请求报文经过Directory,但响应报文一定不经过Director
- 不支持端口映射
- RS可以使用大多数的操作系统
- RS上的lo接口配置VIP的IP地址
LVS-UN模式
- 在VS/NAT 的集群系统中,请求和响应的数据报文都需要通过负载调度器,当真实服务器的数目在10台和20台之间时,负载调度器将成为整个集群系统的新瓶颈。大多数 Internet服务都有这样的特点:请求报文较短而响应报文往往包含大量的数据。如果能将请求和响应分开处理,即在负载调度器中只负责调度请求而响应直 接返回给客户,将极大地提高整个集群系统的吞吐量
- IP隧道(IP tunneling)是将一个IP报文封装在另一个IP报文的技术,这可以使得目标为一个IP地址的数据报文能被封装和转发到另一个IP地址。IP隧道技 术亦称为IP封装技术(IP encapsulation)。IP隧道主要用于移动主机虚拟私有网络(Virtual Private Network),在其中隧道都是静态建立的,隧道一端有一个IP地址,另一端也有唯一的IP地址
- 在TUN模式下,利用IP隧道技术将请求报文封装转发给后端服务器,响应报文能从后端服务器直接返回给客户。但在这里,后端服务器有一组而非一个,所以我们不可能静态地建立一一对应的隧道,而是动态地选择 一台服务器,将请求报文封装和转发给选出的服务器
-
- 客户端将请求发往前端的负载均衡器,请求报文源地址是CIP,目标地址为VIP
- 负载均衡器收到报文后,发现请求的是在规则里面存在的地址,那么它将在客户端请求报文的首部再封装一层IP报文,将源地址改为DIP,目标地址改为RIP,并将此包发送给RS
- RS收到请求报文后,会首先拆开第一层封装,然后发现里面还有一层IP首部的目标地址是自己lo接口上的VIP,所以会处理次请求报文,并将响应报文通过lo接口送给eth0网卡直接发送给客户端。注意:需要设置lo接口的VIP不能在共网上出现
FULL-NAT模式
ULL-NAT模式可以实际上是根据LVS-NAT模式的一种扩展。在NAT模式下DS需要先对请求进行目的地址转换(DNAT),然后对响应包进行源地址转换(SNAT),先后进行两次NAT,而 FULL-NAT则分别对请求进行和响应进行DNAT和SNAT,进行4次NAT,当然这样多次数的NAT会对性能大大削减,但是由于对请求报文的目的地址和源地址都进行了转换,后端的RS可以不在同一个VLAN下。
-
- 首先client 发送请求package给VIP
- VIP 收到package后,会根据LVS设置的LB算法选择一个合适的RS,然后把package 的目地址修改为RS的ip地址,把源地址改成DS的ip地址
- RS收到这个package后发现目标地址是自己,就处理这个package ,处理完后把这个包发送给DS
- DS收到这个package 后把源地址改成VIP的IP,目的地址改成CIP(客户端ip),然后发送给客户端
优缺点
- RIP,DIP可以使用私有地址
- RIP和DIP可以不再同一个网络中,且RIP的网关未必需要指向DIP
- 支持端口映射
- RS的OS可以使用任意类型
- 请求报文经由Director,响应报文也经由Director
- FULL-NAT因为要经过4次NAT,所以性能比NAT还要低
- 由于做了源地址转换,RS无法获取到客户端的真实IP
各模式对比
lvs-nat与lvs-fullnat:请求和响应报文都经由Director
- lvs-nat:RIP的网关要指向DIP
- lvs-fullnat:RIP和DIP未必在同一IP网络,但要能通信
lvs-dr与lvs-tun:请求报文要经由Director,但响应报文由RS直接发往Client
- lvs-dr:通过封装新的MAC首部实现,通过MAC网络转发
- lvs-tun:通过在原IP报文外封装新IP头实现转发,支持远距离通信
调度算法
- LVS在内核中的负载均衡调度是以连接为粒度的。在内核中的连接调度算法上,IPVS已实现了以下八种调度算法:
- 轮叫调度rr(Round-Robin Scheduling)
- 轮询调度:这种是最简单的调度算法,调度器把用户请求按顺序1:1的分配到集群中的每个Real Server上,这种算法平等地对待每一台Real Server,而不管服务器的压力和负载状况。
- 加权轮叫调度wrr(Weighted Round-Robin Scheduling)
- 加权轮叫调度(Weighted Round-Robin Scheduling)算法可以解决服务器间性能不一的情况,它用相应的权值表示服务器的处理性能,服务器的缺省权值为1。假设服务器A的权值为1,B的 权值为2,则表示服务器B的处理性能是A的两倍。加权轮叫调度算法是按权值的高低和轮叫方式分配请求到各服务器。权值高的服务器先收到的连接,权值高的服 务器比权值低的服务器处理更多的连接,相同权值的服务器处理相同数目的连接数
- 最小连接调度lc(Least-Connection Scheduling)
- 最小连接调度(Least-Connection Scheduling)算法是把新的连接请求分配到当前连接数最小的服务器。最小连接调度是一种动态调度算法,它通过服务器当前所活跃的连接数来估计服务 器的负载情况。调度器需要记录各个服务器已建立连接的数目,当一个请求被调度到某台服务器,其连接数加1;当连接中止或超时,其连接数减一。
- 加权最小连接调度wlc(Weighted Least-Connection Scheduling)
- 加权最小连接调度(Weighted Least-Connection Scheduling)算法是最小连接调度的超集,各个服务器用相应的权值表示其处理性能。服务器的缺省权值为1,系统管理员可以动态地设置服务器的权 值。加权最小连接调度在调度新连接时尽可能使服务器的已建立连接数和其权值成比例。
- 基于局部性的最少链接LBLC(Locality-Based Least Connections Scheduling)
- 这个算法主要用于Cache集群系统,因为Cache集群的中客户请求报文的目标IP地址的变化,将相同的目标URL地址请求调度到同一台服务器,来提高服务器的访问的局部性和Cache命中率。从而调整整个集群的系统处理能力。但是,如果realserver的负载处于一半负载,就用最少链接算法,将请求发送给活动链接少的主机。
- 带复制的基于局部性最少链接LBLCR(Locality-Based Least Connections with Replication Scheduling)
- 该算法首先是基于最少链接的,当一个新请求收到后,一定会将请求发给最少连接的那台主机的。但这样又破坏了cache命中率。但这个算法中,集群服务是cache共享的,假设A的PHP跑了一遍,得到缓存。但其他realserver可以去A那里拿缓存,这是种缓存复制机制。
- 目标地址散列调度DH(Destination Hashing Scheduling)
- 目标地址散列调度(Destination Hashing Scheduling)算法也是针对目标IP地址的负载均衡,但它是一种静态映射算法,通过一个散列(Hash)函数将一个目标IP地址映射到一台服务器。
- 源地址散列调度SH(Source Hashing Scheduling)
- 源地址散列:主要是实现将此前的session(会话)绑定。将此前客户的源地址作为散列键,从静态的散列表中找出对应的服务器,只要目标服务器是没有超负荷的就将请求发送过去。就是说某客户访问过A,现在这个客户又来了,所以客户请求会被发送到服务过他的A主机
- 轮叫调度rr(Round-Robin Scheduling)
管理用具使用
持久连接
- 什么是持久连接
- 在LVS中,持久连接是为了用来保证当来自同一个用户的请求时能够定位到同一台服务器,目的是为了会话保持,而通常使用的会话保持技术手段就是cookie与session。
- cookie和session
- 在Web服务通信中,HTTP本身是无状态协议,不能标识用户来源,当用户在访问A网页,再从A网页访问其他资源跳转到了B网页,这对于服务器来说又是一个新的请求,之前的登陆信息都没有了,怎么办?为了记录用户的身份信息,开发者在浏览器和服务器之间提供了cookie和session技术,简单说来在你浏览网页时候,服务器建立session用于保存你的身份信息,并将与之对应的cookie信息发送给浏览器,浏览器保存cookie,当你再次浏览该网页时候,服务器检查你的浏览器中的cookie并获取与之对应的session数据,这样一来你上次浏览网页的数据依然存在
4层负载均衡导致的问题
- 由于cookie和session技术是基于应用层(七层),而LVS工作在4层,只能根据IP地址和端口进行转发,不能根据应用层信息进行转发,所以就存在了问题。比如LVS集群RS是三台,A用户登陆后请求在由第一台处理,而A用户跳转到了另一个页面请求经过DS转发到第二台服务器,但是此时这台服务器上并没有session,用户的信息就没有了,显然这是不能接受的。为了避免上述的问题,一般的解决方案有三种:
- 将来自于同一个用户的请求发往同一个服务器(例如nginx的ip_hash算法)
- 将session信息在服务器集群内共享,每个服务器都保存整个集群的session信息
- 建立一个session存,所有session信息都保存到存储池中
- LVS会话保持实现方式就是通过将来自于同一个用户的请求发往同一个服务器,具体实现分为sh算法和持久连接:
- sh算法:使用SH算法,SH算法在内核中会自动维护一个哈希表,此哈希表中用每一个请求的源IP地址经过哈希计算得出的值作为键,把请求所到达的RS的地址作为值。在后面的请求中,每一个请求会先经过此哈希表,如果请求在此哈希表中有键值,那么直接定向至特定RS,如没有,则会新生成一个键值,以便后续请求的定向。但是此种方法在时间的记录上比较模糊(依据TCP的连接时长计算),而且其是算法本身,所以无法与算法分离,并不是特别理想的方法
- 持久连接:此种方法实现了无论使用哪一种调度方法,持久连接功能都能保证在指定时间范围之内,来自于同一个IP的请求将始终被定向至同一个RS,还可以把多种服务绑定后统一进行调度。
- 详细一点说来:当用户请求到达director时。无论使用什么调度方法,都可以实现对同一个服务的请求在指定时间范围内始终定向为同一个RS。在director内有一个LVS持久连接模板,模板中记录了每一个请求的来源、调度至的RS、维护时长等等,所以,在新的请求进入时,首先在此模板中检查是否有记录(有内置的时间限制,比如限制是300秒,当在到达300秒时依然有用户访问,那么持久连接模板就会将时间增加两分钟,再计数,依次类推,每次只延长2分钟),如果该记录未超时,则使用该记录所指向的RS,如果是超时记录或者是新请求,则会根据调度算法先调度至特定RS,再将调度的记录添加至此表中。这并不与SH算法冲突,lvs持久连接会在新请求达到时,检查后端RS的负载状况,这就是比较精细的调度和会话保持方法
lvs三种持久连接方式
ipvs连接复用问题
问题现象
某个客户在Kubernetes中部署了大规模的业务,因此选用IPVS作为kube-proxy
的负载均衡转发模式。以追求更高的转发性能和更新规则的速度。
但是,IPVS有一个存在了很久的连接重用问题,当客户发布服务,因为存在大量TCP短连接,客户端出现了No route to host
报错,业务服务出现故障。
这是一个存在已久的issue:kubernetes#81775。我们今天就来简单分析一下并介绍解决方案。
分析原因
当业务进行缩容或者滚动迭代过程中,一定有旧的Pod被销毁。No route to host
的直接原因是,IPVS把访问流量转发到了一个被销毁的Pod上面。
具体地说,当一个Pod被销毁后,kube-controller-manager
中的Endpoint Controller
会随之把对应的Pod Ip从Endpoints
对象中摘除。kube-proxy随即通过informer获取到旧Pod Ip被摘除的事件,相应地更新ipvs转发规则:
- 先将旧的IP对应的RS权重置为0,防止新的连接调度到这个IP上。
- 等存量连接归零后(ActiveConn+InactiveConn=0),再彻底摘除掉这个旧IP。
从上面ipvsadm -Ln
的结果也可以看到,几个权重为0的RS都还有连接,所以没有被摘除。
kube-proxy不直接摘除RS是为了实现Pod的优雅退出,以让Pod在处理完所有请求之后再退出。
但我们碰到的问题实际上是,权重被置为0后,依然有新连接被调度到旧IP上。这样存量连接计数永远不会归零,旧RS永远无法被摘除。而由于旧的IP实际已经不存在,就出现了No route to host
报错。只有把流量撤走一段时间后,连接计数终于为0,旧RS被摘除。
这一切的罪魁祸首是一个内核参数:net.ipv4.vs.conn_reuse_mode
。
众所周知,在大量TCP短连接的场景下,会有很多连接处于TIME_WAIT
状态,为了减少资源占用,内核会重用这些连接的端口。
而如果net.ipv4.vs.conn_reuse_mode
为0,并且客户端的源端口被复用了,那么IPVS会将流量直接转发到之前RS,而绕过了正常的负载均衡调度。这样即使RS的权重为0,也可能会有流量被转发上去。
在大量TCP短连接时,端口复用的频率会非常高,这样即使RS的权重被设为了0,还是会有部分流量打上来,而其背后的Pod已经被删除了(优雅退出有超时时间,超过之后Kubernetes会强制删除Pod),就导致了流量被转发到了一个不存在的IP上,出现No route to host
错误。
那么其实只要将net.ipv4.vs.conn_reuse_mode
设为1,强制让复用的连接也经过负载均衡转发不就可以了?
可惜,在5.9
之前的内核版本中,net.ipv4.vs.conn_reuse_mode
设为1并使用了conntrack
时,如果出现了连接复用,IPVS会 DROP 掉第一个 SYN 包,导致 SYN 的重传,有 1s 延迟。而 Kube-proxy 在 IPVS 模式下,使用了 iptables 进行MASQUERADE
,也正好开启了net.ipv4.vs.conntrack
。
所以:
- 如果将
net.ipv4.vs.conn_reuse_mode
设为0,当出现大量短TCP连接时,会出现部分流量被转发到销毁Pod的问题。 - 如果将
net.ipv4.vs.conn_reuse_mode
设为1,当出现大量短TCP连接时,会有部分连接多出了1s的延迟的问题。、
解决方案
也就是说,在以前的内核版本中,理想的情况是:
net.ipv4.vs.conn_reuse_mode
设为1,强制复用连接走负载均衡。net.ipv4.vs.conntrack
设为0,防止IPVS对复用连接进行DROP SYNC操作。
但是,很可惜,这实现不了。因此,如果你的内核版本低于5.9
,并且存在大量TCP短连接场景,不建议使用IPVS,建议替换为iptables模式。
5.9
的一个patch修复了在启用conntrack
时的1s延迟问题,因此,如果内核版本大于等于5.9
,将net.ipv4.vs.conn_reuse_mode
设为1是安全的,保持这个内核参数为1即可解决问题。
事实上,新的kube-proxy会自动根据你的内核版本来判断是否要修改这个内核参数:
- 点赞
- 收藏
- 关注作者
评论(0)