LVS(Linux Virtual Server)即Linux虚拟服务器,是一个高性能的服务器集群系统,主要被用来做负载均衡的工作。它是由章文嵩博士于98年在国防科技大学读博的时候创建的开源项目,是中国国内最早出现的开源软件项目之一。从Linux 2.4开始,LVS的代码已经进入了官方内核中,并得到了广泛的应用。
在实际应用场景下,LVS常常与Keepalived搭配工作,实现高可用、高性能、可伸缩、可管理的服务器集群。本文是Keepalived与VRRP协议的续篇,主要介绍LVS的核心概念及一般用法。
LVS主要由内核模块IPVS
、KTCPVS
与对应的管理程序ipvsadm
、tcpvsadm
组成。IPVS
(IP Virtual Server)负责IP负载均衡,即四层网络的交换。KTCPVS
(Kernel TCP Virtual Server)是基于内容的负载均衡,即七层网络的交换。
IPVS
的工作原理为:当SYN报文到达时,它就选择后边的一台服务器,将报文转发过去,在此之后的所有包含相同IP和TCP报文头地址的数据包都会被转到之前选择的服务器上,这个网络级别的转发效率最高。
因为在TCP握手之前,IPVS
已经完成了转发的工作,所以它无法感知请求的内容。因此LVS又提供了KTCPVS
模块,它可以根据请求的内容来选择合适的服务器,主要应用在WEB服务器的高级负载均衡场景下,比如动态内容与静态内容分离,将相同内容分发到同一服务器以提高缓存命中率等。
IPVS
、KTCPVS
都是跑在内核空间里的,节省了内核空间与用户空间之间的内存拷贝与上下文切换的损耗,所以它的性能一般要比其他负载均衡软件如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/TUN
与VS/DR
时,调度器只处理请求报文,真实服务器的响应是直接返回给客户端的,因此可以极大的提高转发的性能,由于一般网络服务应答比请求报文大许多,使用VS/TUN
与VS/DR
时,最大吞吐量可以提高10倍,可以调度上百台服务器,本身不会成为系统的瓶颈。
VS/DR
比VS/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服务器做代理的对比评测:
图一:MySQL负载均衡TPS比较
图二:MySQL负载均衡平均TPS比较
可以看到,在评测中使用VS/DR
模式的IPVS
的性能与直接连接几乎没有差别,而其他的负载均衡软件都要慢40%左右。MySQL应用是典型的请求数据包小于应答数据包的情况,使用VS/DR
可以极大的改善性能,另外IPVS
运行在内核空间在性能上也更有优势。
使用ipvsadm管理集群
ipvsadm
是IPVS
的命令行管理工具,下边使用一个实验来演示ipvsadm
的基本用法,实验使用VirtualBox与Vagrant,实验脚本可以在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
模式下,DS
与RS
除了有自己的IP,同时还需要配置相同的VIP
下边是实验环境的拓扑结构:
图三:实验环境的拓扑结构图
实验环境中,因为客户端与DS
、RS
在同一个物理网网段中,所以会有ARP问题。因为DS
、RS
都有相同的VIP
,当客户端需要访问VIP
地址时,RS
有可能先于DS
对ARP
解析进行抢答,这样客户端就会直接与RS
交互,DS
就起不到负载均衡的作用了。所以要在RS
的相应网络接口上禁用ARP功能,详细配置方法见对应路径下的bootstrap.sh
文件。
下载测试环境后,首先在director
、realserver1
、realserver2
目录下分别运行vagrant up
命令,启动服务器,当看到director
的终端下出现LVS集群状态时,再启动client
。
在客户端运行命令watch -n 1 curl 192.168.33.50
,每秒发送一次http请求,因为LVS配置的是轮叫调度算法,所以可以看到客户端交替输出realserver1
与realserver2
的网页内容,此时在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-save
与ipvsadm-restore
也比较常用,可以保存与恢复LVS的配置。
另外,LVS还提供了主备DS
之间的状态同步功能,即将所有连接的状态同步到备份DS
上边,这样在故障切换的时候可以更加平顺。根据文档介绍,这个功能还在实验阶段,其实现是主DS
不停的广播连接状态给所有备份DS
,所以在负载大的情况下,会在一定程度上影响转发的性能。要使用该功能,可以在主DS
上运行ipvsadm --start-daemon=master --mcast-interface=eth0
,在备份DS
上运行ipvsadm --start-daemon=backup --mcast-interface=eth0
。
参考
- LVS 中文文档
- LVS HowTo
- Job Scheduling Algorithms in Linux Virtual Server
- LVS FAQ
- IBM Eserver BladeCenter, Linux, and Open Source - Blueprint for e-business on demand
- Benchmark of Load Balancers for MySQL/MariaDB Galera Cluster
- 百度百科:章文嵩