LVS 全称 "Linux Virtual Server" 即 Linux 虚拟服务器,由前阿里云CTO章文嵩开发,已在国内外大型互联网公司广泛使用。LVS 主要用于 LB 负载均衡集群(load balance)。LVS 是基于 Linux 内核以模块的方式进行工作。LVS 的工作原理是,接受客户端请求,然后进行调度算法处理,将请求分发给后端服务器(Real Server)进行业务处理。LVS 本身只做流量转发,不涉及到业务处理,所以 LVS 是一种相对高效的软件负载均衡解决方案。
DS
"Director Server" 也就是前端 LVS 负载均衡服务器
RS
"Real Server" 指的是后端真实的工作服务器
VIP
"Virtual Server IP" 对外部客户端提供服务的 IP 地址,一般配置在 LVS 服务器上
DIP
"Director Server IP" LVS 服务器用于和后端 RS 服务器进行通信的 IP 地址
RIP
"Real Server IP" 后端 real server 真实服务器 IP 地址
CIP
"Client IP" 指的是客户端 IP 地址
- NAT 模式
通过网络地址转换,调度器重写请求报文的目标地址,根据预设的调度算法,将请求分派给后端的真实服务器,真实服务器的响应报文通过调度器时,报文的源地址被重写,再返回给客户,完成整个负载调度过程。
-
TUN 模式 采用NAT技术时,由于请求和响应报文都必须经过调度器地址重写,当客户请求越来越多时,调度器的处理能力将成为瓶颈。为了解决这个问题,调度器把请求报文通过IP隧道转发至真实服务器,而真实服务器将响应直接返回给客户,所以调度器只处理请求报文。由于一般网络服务应答比请求报文大许多,采用 TUN 技术后,集群系统的最大吞吐量可以提高10倍。
-
DR 模式 DR模式下通过改写请求报文的MAC地址,将请求发送到真实服务器,而真实服务器将响应直接返回给客户。同 TUN 模式一样,DR模式可极大地提高集群系统的伸缩性。这种方法没有IP隧道的开销,对集群中的真实服务器也没有必须支持IP隧道协议的要求,但是要求调度器与真实服务器都有一块网卡连在同一物理网段上。
-
Full-NAT 模式 LVS 的 Full-Nat 工作模式是淘宝开源的一个新的工作模式,需要编译 Linux 内核安装补丁。
-
轮叫(Round Robin) 调度器通过"轮叫"调度算法将外部请求按顺序轮流分配到集群中的真实服务器上,它均等地对待每一台服务器,而不管服务器上实际的连接数和系统负载。
-
加权轮叫(Weighted Round Robin) 调度器通过"加权轮叫"调度算法根据真实服务器的不同处理能力来调度访问请求。这样可以保证处理能力强的服务器处理更多的访问流量。调度器可以自动问询真实服务器的负载情况,并动态地调整其权值。
-
最少链接(Least Connections) 调度器通过"最少连接"调度算法动态地将网络请求调度到已建立的链接数最少的服务器上。如果集群系统的真实服务器具有相近的系统性能,采用"最小连接"调度算法可以较好地均衡负载。
-
加权最少链接(Weighted Least Connections) 在集群系统中的服务器性能差异较大的情况下,调度器采用"加权最少链接"调度算法优化负载均衡性能,具有较高权值的服务器将承受较大比例的活动连接负载。调度器可以自动问询真实服务器的负载情况,并动态地调整其权值。
-
基于局部性的最少链接(Locality-Based Least Connections) "基于局部性的最少链接"调度算法是针对目标IP地址的负载均衡,目前主要用于Cache集群系统。该算法根据请求的目标IP地址找出该目标IP地址最近使用的服务器,若该服务器是可用的且没有超载,将请求发送到该服务器;若服务器不存在,或者该服务器超载且有服务器处于一半的工作负载,则用"最少链接"的原则选出一个可用的服务 器,将请求发送到该服务器。
-
带复制的基于局部性最少链接(Locality-Based Least Connections with Replication) "带复制的基于局部性最少链接"调度算法也是针对目标IP地址的负载均衡,目前主要用于Cache集群系统。它与LBLC算法的不同之处是它要维护从一个目标IP地址到一组服务器的映射,而LBLC算法维护从一个目标IP地址到一台服务器的映射。该算法根据请求的目标IP地址找出该目标IP地址对应的服务器组,按"最小连接"原则从服务器组中选出一台服务器,若服务器没有超载,将请求发送到该服务器,若服务器超载;则按"最小连接"原则从这个集群中选出一 台服务器,将该服务器加入到服务器组中,将请求发送到该服务器。同时,当该服务器组有一段时间没有被修改,将最忙的服务器从服务器组中删除,以降低复制的 程度。
-
目标地址散列(Destination Hashing)
"目标地址散列"调度算法根据请求的目标IP地址,作为散列键(Hash Key)从静态分配的散列表找出对应的服务器,若该服务器是可用的且未超载,将请求发送到该服务器,否则返回空。
- 源地址散列(Source Hashing)
"源地址散列"调度算法根据请求的源IP地址,作为散列键(Hash Key)从静态分配的散列表找出对应的服务器,若该服务器是可用的且未超载,将请求发送到该服务器,否则返回空。
-
最短期望延迟(Shortest Expected Delay) 基于wlc算法,简单算法:(active + 1) * 256 / weight 也就是 "(活动连接数 + 1) * 256 / 权重"
-
永不排队 (Never Queue) 改进版的 sed 算法,无需队列,如果有台 realserver 的连接数等于 0 就直接分配过去,不需要再进行 sed 运算。
加载 ip_vs 内核模块
$ modprobe ip_vs
安装 LVS 的管理工具 ipvsadm
$ yum install -y ipvsadm
创建 LVS 虚拟服务器,调度算法为 =rr= 轮叫算法
$ ipvsadm -A -t 112.2.21.88:80 -s rr
添加后端 real server 服务器节点,使用 DR 模式
$ ipvsadm -a -t 112.2.21.88:80 -r 10.10.24.21:80 -g
删除一个 LVS 后端服务器节点
$ ipvsadm -d -t 112.2.21.88:80 -r 10.10.24.21:80
清空所有 LVS 虚拟服务器和后端服务器节点
$ ipvsadm -C
查看 LVS 状态
$ ipvsadm --list --stats
LVS 参数选项
-A 在 LVS 中添加一个新的虚拟服务器
-a 在虚拟服务器中添加一个后端真实服务器节点
-C 清除 LVS 中所有服务器
-d 删除一个后端真实服务器
-D 删除一个虚拟服务器
-e 修改后端真实服务器参数
-E 修改虚拟服务器参数
-l 显示 LVS 的所有列表信息
-t 表示为 tcp 服务
-u 表示为 udp 服务
-s 使用的调度算法 rr | wrr | lc | wlc | lblb | lblcr | dh | sh | sed | nq
-S 保存 LVS 服务器规则列表,默认输出到标准输出
-t 表示虚拟服务器提供tcp服务
-r 后端真实服务器地址
-R 恢复 LVS 的服务器规则列表,默认从标准输入读取
-m 指定LVS工作模式为NAT模式
-n 以数字形式显示 IP 地址和端口号
-w 后端真实服务器的权值
-g 指定LVS工作模式为直接路由器模式(也是LVS默认的模式)
-i 指定LVS的工作模式为隧道模式
-p 会话保持时间,同一个客户端被分发到同一个后端服务器的会话时间
-Z 将服务器相关计数器数据清零
--sort 对后端真实服务器进行排序输出
--rate 显示速率信息
--stats 显示统计信息
--daemon 显示同步守护进程状态
--timeout 显示 tcp tcpfin udp 的 timeout 值
--connection 显示连接统计信息
--thresholds 显示相关阀值信息
--persistent-conn 显示持久连接信息
网卡 LRO/GRO 功能
现在大多数网卡都具有 LRO/GRO 功能,也就是将多个小数据包合并成一个大数据包,当数据包大小超过 MTU 1500bytes 的时候,经过内核协议栈,LVS 就会直接丢弃。所以,有的时候我们用 LVS 来传输大文件,就很容易出现丢包,传输速度慢的现象,这个时候就需要关闭网卡的 LRO/GRO 功能。
首先需要安装 ethtool 工具
$ yum install -y ethtool
查看网卡 LRO/GRO 功能状态
$ ethtool -k eth0
关闭网卡 LRO/GRO 功能
$ ethtool -k eth0 lro off
$ ethtool -k eth0 gro off
增大连接 Hash 表大小
默认情况下 LVS 的最大并发数取决于它的连接 Hash 表的大小,默认为 4096
$ ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
通过修改 LVS 模块配置文件 /etc/modprobe.d/ip_vs.conf 来增大 LVS 的并发连接数
$ emacs /etc/modprobe.d/ip_vs.conf
options ip_vs conn_tab_bits=20
重启服务器后查看 LVS Hash 表大小
$ ipvsadm -ln
IP Virtual Server version 1.2.1 (size=1048576)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
禁用ARP增大backlog并发数
修改内核配置文件 /etc/sysctl.conf
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.core.netdev_max_backlog = 102400
tcp tcpfin udp 超时时间
在一些长连接应用场景中这 3 个参数非常重要,并且 tcp 参数必须大于 tcpfin,单位是秒。
理解 persistent 超时时间
LVS 的 persistent 超时时间指的是,在这个时间内相同 IP 地址的客户端会分发到同一台后端服务器,当这个时间变成 0 时,如果还有客户端连接,它会重新开始计时。如果没有客户端连接就会自动删除,相同 IP 地址客户端下次就会被分发到另外一台后端服务器节点,一般这个时间值大于 LVS 的 tcp + tcpfin 之和即可,但不要太大。
多队列网卡中断优化
使用 lspci 命令查看网卡是否支持多队列
$ lspci -vvv
查看 "Ethernet controller" 以太网控制器中,如果有 MSI-X 并且 Enable+ Count > 1 就表示支持多队列
00:04.0 Ethernet controller: Red Hat, Inc Virtio network device
Subsystem: Red Hat, Inc Device 0001
Physical Slot: 4
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR+ FastB2B- DisINTx+
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
Interrupt: pin A routed to IRQ 11
Region 0: I/O ports at c080 [size=32]
Region 1: Memory at febd2000 (32-bit, non-prefetchable) [size=4K]
Expansion ROM at feb80000 [disabled] [size=256K]
Capabilities: [40] MSI-X: Enable+ Count=3 Masked-
Vector table: BAR=1 offset=00000000
PBA: BAR=1 offset=00000800
Kernel driver in use: virtio-pci
Kernel modules: virtio_pci
查看所有网卡中断号
$ cat /proc/interrupts
IRQ CPU0 CPU1 CPU2 CPU3
122: 1709 25131 3548386 117109 PCI-MSI-X eth0-0
130: 441 2167083 205623005 7779272 PCI-MSI-X eth0-1
138: 418 4803724 109660398 7188985 PCI-MSI-X eth0-2
146: 389 5473623 97942351 6893766 PCI-MSI-X eth0-3
154: 397 4999666 83224034 6996673 PCI-MSI-X eth0-4
162: 399 4542680 84930436 8034803 PCI-MSI-X eth0-5
170: 406 4429835 132193602 8443930 PCI-MSI-X eth0-6
178: 449 2881815 172147095 5454282 PCI-MSI-X eth0-7
输出结果中的第 1 列表示中断号
手动设置将中断分配到不同的CPU核心上
例如将 eth0 网卡的 0 号队列,中断号为 122 绑定到第 1 个 CPU 核心上
$ echo 1 > /proc/irq/122/smp_affinity
例如将 eth0 网卡的 1 号队列,中断号为 130 绑定到第 2 个 CPU 核心上
$ echo 2 > /proc/irq/130/smp_affinity
例如将 eth0 网卡的 2 号队列,中断号为 138 绑定到第 3 个 CPU 核心上
$ echo 4 > /proc/irq/138/smp_affinity
smp_affinity 文件中使用十进制数表示 CPU 核心位
00000001 1 第 1 个 CPU
00000010 2 第 2 个 CPU
00000100 4 第 3 个 CPU
00001000 8 第 4 个 CPU
00010000 16 第 5 个 CPU
00100000 32 第 6 个 CUP
01000000 64 第 7 个 CPU
10000000 128 第 8 个 CPU
注意上例中的 eth0 网卡只能同时绑定到一个 CPU 核心上,不能同时平均分配到所有 CPU 上。假如有两块网卡 eth0 和 eth1,我们就可将 eth0 绑定到第 1 个 CPU 上,eth1 绑定到第 2 个 CPU 上。在手动绑定 CPU 中断前,必须停止 irqbalance 服务,否则手动配置会被覆盖。
By typefo [email protected] Update: 2017-04-29 本文档使用 CC-BY 4.0 协议