LVS:跑在Linux内核上的负载均衡器

LVS(Linux Virtual Server)即Linux虚拟服务器,是一个高性能的服务器集群系统,主要被用来做负载均衡的工作。它是由章文嵩博士于98年在国防科技大学读博的时候创建的开源项目,是中国国内最早出现的开源软件项目之一。从Linux 2.4开始,LVS的代码已经进入了官方内核中,并得到了广泛的应用。

在实际应用场景下,LVS常常与Keepalived搭配工作,实现高可用、高性能、可伸缩、可管理的服务器集群。本文是Keepalived与VRRP协议的续篇,主要介绍LVS的核心概念及一般用法。

LVS主要由内核模块IPVSKTCPVS与对应的管理程序ipvsadmtcpvsadm组成。IPVS(IP Virtual Server)负责IP负载均衡,即四层网络的交换。KTCPVS(Kernel TCP Virtual Server)是基于内容的负载均衡,即七层网络的交换。

IPVS的工作原理为:当SYN报文到达时,它就选择后边的一台服务器,将报文转发过去,在此之后的所有包含相同IP和TCP报文头地址的数据包都会被转到之前选择的服务器上,这个网络级别的转发效率最高。

因为在TCP握手之前,IPVS已经完成了转发的工作,所以它无法感知请求的内容。因此LVS又提供了KTCPVS模块,它可以根据请求的内容来选择合适的服务器,主要应用在WEB服务器的高级负载均衡场景下,比如动态内容与静态内容分离,将相同内容分发到同一服务器以提高缓存命中率等。

IPVSKTCPVS都是跑在内核空间里的,节省了内核空间与用户空间之间的内存拷贝与上下文切换的损耗,所以它的性能一般要比其他负载均衡软件如HAProxy高。

在我手边的Linux发行版中,Debian(Linux jessie 3.16.0-4-amd64)与CentOS(3.10.0-693.el7.x86_64)的内核都有ip_vs模块,需要手动开启。ArchLinux(4.13.11-1-ARCH)的内核中没有该模块,需要自行编译安装,Linux当前最新的官方4.14内核中也能找到ip_vs模块。

不论是官方还是非官方,都很难找到关于KTCPVS的资料,而且根据现有的资料推测,KTCPVS可能并不稳定,也没有真实的应用案例,所以本文着重介绍IPVS

转发方式

IPVS支持三种转发方式:

  • VS/NAT(Virtual Server via Network Address Translation):通过网络地址转换,调度器重写请求报文的目标地址,根据预设的调度算法,将请求分派给后端的真实服务器;真实服务器的响应报文通过调度器时,报文的源地址被重写,再返回给客户,完成整个负载调度过程。
  • VS/TUN(Virtual Server via IP Tunneling):调度器把请求报文通过IP隧道转发至真实服务器,而真实服务器将响应直接返回给客户。
  • VS/DR(Virtual Server via Direct Routing):通过改写请求报文的MAC地址,将请求发送到真实服务器,而真实服务器将响应直接返回给客户。

采用VS/NAT技术时,由于请求和响应报文都必须经过调度器地址重写,因此它的伸缩能力有限,当服务器结点数目升到20时,调度器本身有可能成为系统的新瓶颈。但是可以与DNS负载均衡协同工作,进一步增加集群的规模。

而使用VS/TUNVS/DR时,调度器只处理请求报文,真实服务器的响应是直接返回给客户端的,因此可以极大的提高转发的性能,由于一般网络服务应答比请求报文大许多,使用VS/TUNVS/DR时,最大吞吐量可以提高10倍,可以调度上百台服务器,本身不会成为系统的瓶颈。

VS/DRVS/TUN的性能要稍好一些,它没有IP隧道的开销,对集群中的真实服务器也没有必须支持IP隧道协议的要求,但是要求调度器与真实服务器都有一块网卡连在同一物理网段上,且服务器网络设备(或者设备别名)不作ARP响应。

调度算法

IPVS支持十种负载均衡调度算法:

  • 轮叫(Round Robin):以轮叫的方式依次将请求调度到不同的服务器,会略过权值是0的服务器。
  • 加权轮叫(Weighted Round Robin):按权值的高低和轮叫方式分配请求到各服务器。服务器的缺省权值为1。假设服务器A的权值为1,B的权值为2,则表示服务器B的处理性能是A的两倍。例如,有三个服务器A、B和C分别有权值4、3和2,则在一个调度周期内(mod sum(W(Si)))调度序列为AABABCABC。
  • 最少链接(Least Connections):把新的连接请求分配到当前连接数最小的服务器。
  • 加权最少链接(Weighted Least Connections):调度新连接时尽可能使服务器的已建立连接数和其权值成比例,算法的实现是比较连接数与加权值的乘积,因为除法所需的CPU周期比乘法多,且在Linux内核中不允许浮点除法。
  • 基于局部性的最少链接(Locality-Based Least Connections):主要用于Cache集群系统,将相同目标IP地址的请求调度到同一台服务器,来提高各台服务器的访问局部性和主存Cache命中率。LBLC调度算法先根据请求的目标IP地址找出该目标IP地址最近使用的服务器,若该服务器是可用的且没有超载,将请求发送到该服务器;若服务器不存在,或者该服务器超载且有服务器处于其一半的工作负载,则用“最少链接”的原则选出一个可用的服务器,将请求发送到该服务器。
  • 带复制的基于局部性最少链接(Locality-Based Least Connections with Replication):主要用于Cache集群系统,它与LBLC算法的不同之处是它要维护从一个目标IP地址到一组服务器的映射。LBLCR算法先根据请求的目标IP地址找出该目标IP地址对应的服务器组;按“最小连接”原则从该服务器组中选出一台服务器,若服务器没有超载,将请求发送到该服务器;若服务器超载;则按“最小连接”原则从整个集群中选出一台服务器,将该服务器加入到服务器组中,将请求发送到该服务器。同时,当该服务器组有一段时间没有被修改,将最忙的服务器从服务器组中删除,以降低复制的程度。
  • 目标地址散列(Destination Hashing):通过一个散列(Hash)函数将一个目标IP地址映射到一台服务器,若该服务器是可用的且未超载,将请求发送到该服务器,否则返回空。使用素数乘法Hash函数:(dest_ip* 2654435761UL) & HASH_TAB_MASK
  • 源地址散列(Source Hashing):根据请求的源IP地址,作为散列键(Hash Key)从静态分配的散列表找出对应的服务器,若该服务器是可用的且未超载,将请求发送到该服务器,否则返回空。
  • 最短期望延迟(Shortest Expected Delay Scheduling):将请求调度到有最短期望延迟的服务器。最短期望延迟的计算公式为(连接数 + 1) / 加权值。
  • 最少队列调度(Never Queue Scheduling):如果有服务器的连接数是0,直接调度到该服务器,否则使用上边的SEDS算法进行调度。

LVS允许运行时动态的增减负载均衡池中的服务器及调整其权值,所以可以自行通过软件实现一些动态反馈负载均衡算法,如根据服务器网络流量、CPU、内存、磁盘IO等资源的使用情况综合调整权重(Redhat Piranha提供了类似功能)。现在很多应框架也提供了友好的监控接口,如Spring Boot Actuator,JMX等,让自行实现动态反馈负载均衡更加简单。从性能方面考虑,官方建议权值调整时间间隔应该在5到20秒之间。

性能

IPVS需要记住每一个头地址对应的连接,其内部是使用一个哈希表来保存这个对应关系的,每一条记录需要128字节的存储空间,内存占用的计算公式为:连接数 x 128 x (往返时延 + 连接空闲时间) [bytes]。只要带宽与CPU计算能力允许,假设每个连接时延1秒,1G内存可以支持8M的并发。

在实际应用情境中,LVS常常与Keepalived搭配,做MySQL集群读写分离的负载均衡与故障时的主备切换,下边是一组常用的负载均衡软件对三台MySQL服务器做代理的对比评测:

blb_bench.png

图一:MySQL负载均衡TPS比较

blb_avg.png

图二:MySQL负载均衡平均TPS比较

可以看到,在评测中使用VS/DR模式的IPVS的性能与直接连接几乎没有差别,而其他的负载均衡软件都要慢40%左右。MySQL应用是典型的请求数据包小于应答数据包的情况,使用VS/DR可以极大的改善性能,另外IPVS运行在内核空间在性能上也更有优势。

使用ipvsadm管理集群

ipvsadmIPVS的命令行管理工具,下边使用一个实验来演示ipvsadm的基本用法,实验使用VirtualBoxVagrant,实验脚本可以在https://github.com/sean-liang/labs/tree/master/lvs-dr-nginx下载。

在开始之前先介绍几个概念:

  • Director Server(DS):负责负载均衡调度的LVS服务器
  • Real Server(RS):负载均衡池中真实工作的服务器
  • Director Server IP(DIP):DS的IP地址
  • Real Server IP(RIP):RS的IP地址
  • Virtual Server IP(VIP):虚拟服务器的IP地址,在VS/DR模式下,DSRS除了有自己的IP,同时还需要配置相同的VIP

下边是实验环境的拓扑结构:

topo.png

图三:实验环境的拓扑结构图

实验环境中,因为客户端与DSRS在同一个物理网网段中,所以会有ARP问题。因为DSRS都有相同的VIP,当客户端需要访问VIP地址时,RS有可能先于DSARP解析进行抢答,这样客户端就会直接与RS交互,DS就起不到负载均衡的作用了。所以要在RS的相应网络接口上禁用ARP功能,详细配置方法见对应路径下的bootstrap.sh文件。

下载测试环境后,首先在directorrealserver1realserver2目录下分别运行vagrant up命令,启动服务器,当看到director的终端下出现LVS集群状态时,再启动client

在客户端运行命令watch -n 1 curl 192.168.33.50,每秒发送一次http请求,因为LVS配置的是轮叫调度算法,所以可以看到客户端交替输出realserver1realserver2的网页内容,此时在director的命令行下输入sudo ipvsadm -l --stats,可以看到下边的输出:

1
2
3
4
5
6
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Conns InPkts OutPkts InBytes OutBytes
-> RemoteAddress:Port
TCP 192.168.33.50:http 38 228 0 15086 0
-> 192.168.33.20:http 19 114 0 7543 0
-> 192.168.33.30:http 19 114 0 7543 0

从输出可以看到,两个RS的负载是平均分配的,而且OutPkts与OutBytes一列都是0,说明使用VS/DR模式做转发,只有进入的数据包,返回的数据包由RS直接发给客户端,并不经过DS

如果需要维护一个运行中的服务器,可以使用命令ipvsadm -e -t 192.168.33.50:80 -r 192.168.33.30 -w 0将其权值设置为0,这样LVS就不会再向它转发请求,当维护结束后,将其权值还原即可。

可以使用命令ipvsadm -d -t 192.168.33.50:80 -r 192.168.33.30在运行时将一个RS从集群中删除,使用命令ipvsadm -a -t 192.168.33.50:80 -r 192.168.33.30可以将新机器加入集群。

不论是将权值置0还是移出集群,LVS都会立即停止转发新请求到该RS,但会等待现有的连接全部断开或者超时。

ipvsadm-saveipvsadm-restore也比较常用,可以保存与恢复LVS的配置。

另外,LVS还提供了主备DS之间的状态同步功能,即将所有连接的状态同步到备份DS上边,这样在故障切换的时候可以更加平顺。根据文档介绍,这个功能还在实验阶段,其实现是主DS不停的广播连接状态给所有备份DS,所以在负载大的情况下,会在一定程度上影响转发的性能。要使用该功能,可以在主DS上运行ipvsadm --start-daemon=master --mcast-interface=eth0,在备份DS上运行ipvsadm --start-daemon=backup --mcast-interface=eth0

参考