LVS 负载均衡技术
一、LVS 概述
Linux 虚拟服务器(LVS)是在 Linux 内核中实现的基于 IP 的数据请求负载均衡调度方案。使用 LVS 架设的服务器集群系统由负载均衡层(Loader Balancer) 、服务器群组层(Server Array) 、 数据共享存储层(Shared Storage) 三部分组成,对用户而言,所有应用透明,用户仅使用虚拟服务器提供的高性能服务。
二、LVS 核心组件
(一)负载均衡层(Load Balancer)
由一台或多台负载调度器(Director Server)组成,LVS 模块安装在 Director Server 上。Director 类似路由器,含 LVS 功能的路由表,负责将用户请求分发给 Server Array 层的应用服务器(Real Server)。同时,Director Server 安装 Ldirectord 模块,用于监测 Real Server 服务健康状况,在 Real Server 不可用时剔除,恢复时重新加入。
(二)服务器群组层(Server Array)
由一组实际运行应用服务的机器(Real Server)组成,Real Server 可以是 WEB、MAIL、FTP、DNS、视频等服务器。实际应用中,Director Server 也可兼任 Real Server 角色。
(三)数据共享存储层(Shared Storage)
为所有 Real Server 提供共享存储空间和内容一致性的存储区域,通常由磁盘阵列设备组成。为保证内容一致性,可通过 NFS 网络文件系统共享数据,但 NFS 在繁忙业务系统中性能欠佳,此时可采用集群文件系统(如 Red Hat 的 GFS、Oracle 的 OCFS2 等)。
(四)管理工具
- ipvs:LVS 软件核心,运行在负载均衡层,是基于 IP 层的负载均衡。其总体结构包含 IP 包处理、负载均衡算法、系统配置和管理模块,以及虚拟服务器与真实服务器链表,用于管理集群服务上的 Real Server(RS)。一个 ipvs 主机可同时定义多个集群服务器,但可能影响调度性能,且一个 ipvs 服务至少有一个 RS。
- ipvsadm:命令行工具,用于管理集群服务器,常用命令及选项如下:
-
- -A:添加一个集群服务;
-
- -E:修改已添加的集群服务;
-
- -D:删除虚拟服务;
-
- -C:清空整个表;
-
- -R:从标准输入重载;
-
- -S:保存值到标准输出;
-
- -a:向指定的集群服务器中添加 Real Server;
-
- -e:修改 RS;
-
- -d:删除真实服务;
-
- -L/-l:列出表;
-
- -t:指定 TCP 协议的服务器地址(格式:host[:port]);
-
- -u:指定 UDP 协议的服务器地址(格式:host[:port]);
-
- -r:指定服务器地址主机和端口,仅支持端口映射的 LVS 类型才允许此处端口与集群服务端口不同;
-
- -g:DR(直接路由)模式(默认);
-
- -i:TUN(隧道)模式;
-
- -m:NAT 模式;
-
- -w:指定实际服务器权重;
-
- -n:以数字格式显示 IP 和端口(需写在 -L 之后)。
三、LVS 工作模式
(一)NAT 模式
1. 原理
通过网络地址转换实现虚拟服务器,客户端(CIP)访问 LVS 端的虚拟 IP(VIP),LVS 根据算法将请求从与真实服务器通信的接口(DIP)发送给真实服务器,并将 VIP 改为真实服务器的 IP(RIP);真实服务器响应后将数据包发送给 LVS(RIP→DIP),LVS 再将 RIP 改为 VIP 发送给客户端。
2. 特点
- Real Server 的网关必须指向 LVS,否则报文无法送达客户端。
- NAT 技术需对请求和响应报文进行地址改写,网站访问量大时,负载均衡调度器易成为瓶颈,一般支持 10 - 20 台节点。
- 仅需在 LVS 上配置一个公网 IP 地址。
- 每台内部 Real Server 的网关地址必须是调度器 LVS 的内网地址。
- 支持 IP 地址和端口转换,用户请求端口与真实服务器端口可不一致。
3. 优缺点
- 优点:集群中的物理服务器可使用任何支持 TCP/IP 的操作系统,仅负载均衡器需合法 IP 地址。
- 缺点:扩展性有限,服务器节点过多时,负载均衡器会成为系统瓶颈,因所有请求和应答包都流经负载均衡器。
4. 配置示例
- 环境说明:
-
- 客户端:192.168.100.40(网卡 NAT 模式)。
-
- DR(Director Server):DIP: 192.168.100.10(网卡 NAT 模式),VIP: 192.168.0.10(网卡仅主机模式)。
-
- RS1:RIP: 192.168.100.20,gw: 192.168.100.10。
-
- RS2:RIP: 192.168.100.30,gw: 192.168.100.10。
-
- 均关闭防火墙和 SELinux。
-
DR 端网卡配置:
nmcli connection delete "有线连接 1"
nmcli connection add ifname ens36 con-name ens36 type ethernet
nmcli connection modify ens36 ipv4.addresses 192.168.0.10/24 ipv4.gateway 192.168.0.254 ipv4.method manual connection.autoconnect yes
nmcli connection up ens36
-
开启 IP 转发:
vim /etc/sysctl.conf
net.ipv4.ip_forward = 1
sysctl -p
-
安装 ipvsadm 工具:
yum -y install ipvsadm
-
RS1 和 RS2 配置 httpd 服务:
-
-
RS1:
yum -y install httpd
echo RS1 > /var/www/html/index.html
systemctl restart httpd
systemctl enable httpd
-
-
-
RS2:
yum -y install httpd
echo RS2 > /var/www/html/index.html
systemctl restart httpd
systemctl enable httpd
-
-
DR 上添加 LVS 规则:
ipvsadm -A -t 192.168.0.10:80 -s rr
ipvsadm -a -t 192.168.0.10:80 -r 192.168.100.20 -m
ipvsadm -a -t 192.168.0.10:80 -r 192.168.100.30 -m
ipvsadm -Ln # 列出规则
ipvsadm -Sn > /etc/sysconfig/ipvsadm # 保存规则
systemctl restart ipvsadm
systemctl enable ipvsadm
-
客户端测试:
curl http://192.168.0.10
可看到交替返回 RS1 和 RS2。

- HTTPS 配置(扩展):
-
-
DR 生成密钥与证书:
cd /etc/pki/CA/
(umask 077; openssl genrsa -out private/cakey.pem 2048)
openssl req -new -x509 -key private/cakey.pem -out cacert.pem -days 1024
touch index.txt
echo 01 > serial
-
-
-
RS1 生成并签名证书:
yum -y install mod_ssl
mkdir /etc/httpd/ssl
cd /etc/httpd/ssl
(umask 077; openssl genrsa -out httpd.key 2048)
openssl req -new -key httpd.key -out httpd.csr -days 1024
scp httpd.csr root@192.168.100.10:/root/
-
-
-
DR 签名证书并发送回 RS1:
cd
openssl ca -in httpd.csr -out httpd.crt -days 1024
scp httpd.crt root@192.168.100.20:/etc/httpd/ssl
scp /etc/pki/CA/cacert.pem root@192.168.100.20:/etc/httpd/ssl
-
-
-
RS2 配置证书(从 RS1 复制):
yum -y install mod_ssl
mkdir /etc/httpd/ssl
scp cacert.pem httpd.crt httpd.key root@192.168.100.30:/etc/httpd/ssl
-
-
-
RS1 和 RS2 修改 ssl.conf:
vim /etc/httpd/conf.d/ssl.conf
SSLCertificateFile /etc/httpd/ssl/httpd.crt
SSLCertificateKeyFile /etc/httpd/ssl/httpd.key
SSLCACertificateFile /etc/httpd/ssl/cacert.pem
systemctl restart httpd
-
-
-
DR 添加 HTTPS 规则:
ipvsadm -A -t 192.168.0.10:443 -s rr
ipvsadm -a -t 192.168.0.10:443 -r 192.168.100.20 -m
ipvsadm -a -t 192.168.0.10:443 -r 192.168.100.30 -m
ipvsadm -Sn > /etc/sysconfig/ipvsadm
systemctl restart ipvsadm
-
-
-
客户端测试 HTTPS:
curl -k https://192.168.0.10
-

(二)DR 模式
1. 原理
直接使用路由技术实现虚拟服务器,客户端访问 LVS 端的虚拟 IP(CIP→VIP),LVS 将 VIP 的 MAC 地址改为 RIP 的 MAC 地址,将 CIP 的 MAC 地址改为自己的 DIP 的 MAC 地址后发送给真实服务器;真实服务器发现请求的目标 MAC 地址是自己,接收请求并将结果通过环回接口(lo)直接发送给客户端。
2. 注意事项
需设置环回接口(lo)的 VIP 不响应本地网络内的 ARP 请求,可将 VIP 设置为 32 位网络位,且 lo 的 IP 与 VIP 一致。
3. 配置示例
- 环境:
-
- Lvs 服务器(DR):DIP: 192.168.100.10,VIP: 192.168.100.100。
-
- Apache 服务器(RS1):RIP: 192.168.100.20,VIP: 192.168.100.100。
-
- Apache 服务器(RS2):RIP: 192.168.100.30,VIP: 192.168.100.100。
-
- 客户端:IP: 192.168.100.40。
-
- 防火墙和 SELinux 均关闭。
- RS1 和 RS2 配置 httpd 服务:
-
-
RS1:
yum -y install httpd
echo RS1 > /var/www/html/index.html
systemctl restart httpd
systemctl enable httpd
-
-
-
RS2:
yum -y install httpd
echo RS2 > /var/www/html/index.html
systemctl restart httpd
systemctl enable httpd
-
-
DR 配置:
-
-
开启 IP 转发:
vim /etc/sysctl.conf
net.ipv4.ip_forward = 1
sysctl -p
-
-
-
安装 ipvsadm 工具:
yum -y install ipvsadm
-
-
-
添加 VIP 到环回接口:
ifconfig lo 192.168.100.100/32 broadcast 192.168.100.100 netmask 255.255.255.255 up
-
-
RS1 和 RS2 配置 ARP 内核参数:
vim /etc/sysctl.conf
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.lo.arp_ignore = 1
net.ipv4.conf.lo.arp_announce = 2
sysctl -p
-
RS1 和 RS2 配置 VIP 与路由:
-
-
配置 VIP:
ifconfig lo 192.168.100.100/32 broadcast 192.168.100.100 netmask 255.255.255.255 up
-
-
- 添加路由:
-
-
- RS1:
route add -host 192.168.100.100/32 dev lo
-
-
-
- RS2:
route add -host 192.168.100.100/32 dev lo
-
-
DR 添加 LVS 规则:
ipvsadm -A -t 192.168.100.100:80 -s rr
ipvsadm -a -t 192.168.100.100:80 -r 192.168.100.20 -m
ipvsadm -a -t 192.168.100.100:80 -r 192.168.100.30 -m
ipvsadm -Sn > /etc/sysconfig/ipvsadm
systemctl restart ipvsadm
systemctl enable ipvsadm
-
客户端测试:
可看到交替返回 RS1 和 RS2。

(三)TUN 模式
1. 原理
通过隧道方式实现虚拟服务,LVS 在请求报文上再封装一层 IP 报文(DIP(CIP→VIP)→RIP)发送给真实服务器;真实服务器收到后拆开第一层,发现里面 IP 首部的目标地址是自身环回接口的 VIP,处理请求报文后直接将结果发送给客户端。
2. 优点
负载均衡器仅负责请求包分发,RS 直接将应答包发给用户,减少了负载均衡器的数据流动,不再是系统瓶颈,能处理大量请求,且可在公网上跨地域分发。
3. 缺点
隧道模式的 RS 节点需要合法 IP,且所有服务器需支持 "IP Tunneling"(IP 封装)协议,服务器局限于部分 Linux 系统。
4. 配置示例
- 环境:
-
- Lvs 服务器(DR):DIP: 192.168.100.10,VIP: 192.168.100.55。
-
- Apache 服务器(RS1):RIP: 192.168.100.20,VIP: 192.168.100.55。
-
- Apache 服务器(RS2):RIP: 192.168.100.30,VIP: 192.168.100.55。
-
- 客户端:IP: 192.168.100.40。
-
- 防火墙和 SELinux 均关闭
- DR 端配置
-
开启 IP 转发:
vim /etc/sysctl.conf
net.ipv4.ip_forward = 1 # 开启内核 IP 转发功能
sysctl -p # 加载配置,使修改生效
2.安装 ipvsadm 工具:
yum -y install ipvsadm # 安装 LVS 管理工具
3.配置 VIP 到隧道接口:
使用 tunl0 作为隧道接口,添加 VIP 并设置广播地址(与 VIP 一致)和子网掩码(32 位,确保仅本机识别该 VIP):
ifconfig tunl0 192.168.100.55 broadcast 192.168.100.55 netmask 255.255.255.255 up
- RS1 和 RS2 端配置(操作完全一致)
- 部署 httpd 服务:
安装并启动 Web 服务,用于测试负载均衡效果:
yum -y install httpd # 安装 Apache 服务
# 写入差异化内容,便于区分不同 RS
echo "RS1" > /var/www/html/index.html # RS2 此处改为 "RS2"
systemctl restart httpd # 重启服务
systemctl enable httpd # 设置开机自启
2.加载 IP 隧道模块:
TUN 模式依赖 ipip 内核模块实现 IP 封装,需手动加载:
modprobe ipip # 加载 ipip 隧道模块
3.配置 VIP 到隧道接口:
与 DR 端一致,在 tunl0 接口添加 VIP,确保 RS 能识别目标为 VIP 的请求:
ifconfig tunl0 192.168.100.55 broadcast 192.168.100.55 netmask 255.255.255.255 up
4.修改内核参数(关键):
避免 RS 响应无关 ARP 请求、防止反向过滤隧道数据包,需调整以下参数:
vim /etc/sysctl.conf
# 禁止隧道接口响应 ARP 请求,避免 IP 冲突
net.ipv4.conf.tunl0.arp_ignore = 1
net.ipv4.conf.tunl0.arp_announce = 2
# 全局禁止无关 ARP 响应
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
# 关闭隧道接口的反向路径过滤,允许隧道数据包通过
net.ipv4.conf.tunl0.rp_filter = 0
net.ipv4.conf.all.rp_filter = 0
# 加载配置
sysctl -p
- DR 端添加 LVS 规则并测试
- 配置负载均衡规则:
指定 VIP: 端口(192.168.100.55:80)、调度算法(轮询 rr),并添加 RS 节点(使用 -i 指定 TUN 模式):
# 添加集群服务(TCP 协议,VIP:80)
ipvsadm -A -t 192.168.100.55:80 -s rr
# 添加 RS1,使用 TUN 模式
ipvsadm -a -t 192.168.100.55:80 -r 192.168.100.20 -i
# 添加 RS2,使用 TUN 模式
ipvsadm -a -t 192.168.100.55:80 -r 192.168.100.30 -i
# 保存规则到配置文件,避免重启丢失
ipvsadm -Sn > /etc/sysconfig/ipvsadm
# 重启 ipvsadm 服务,加载规则
systemctl restart ipvsadm
systemctl enable ipvsadm # 设置开机自启
2.客户端测试:
在客户端(192.168.100.40)多次执行 curl 命令,观察返回结果是否在 RS1 和 RS2 之间交替,验证负载均衡是否生效:
curl http://192.168.100.55
预期结果:第一次返回 RS1,第二次返回 RS2,循环交替。

四、LVS 调度算法
LVS 调度算法分为 静态算法 (不考虑 RS 负载,仅按固定规则分配)和 动态算法(根据 RS 实时负载调整分配策略),具体分类及特点如下:
(一)静态调度算法
|----------|------|-------------------------------------------------|-------------------------------|
| 算法名称 | 英文缩写 | 核心逻辑 | 适用场景 |
| 轮询调度 | RR | 均等分配请求到每台 RS,不考虑 RS 连接数、负载差异 | 所有 RS 配置一致、负载波动小的场景(如 Web 服务) |
| 加权轮询调度 | WRR | 按 RS 权重分配请求(权重越高,分配越多),可手动调整权重 | RS 配置不均衡(如 CPU / 内存差异)的场景 |
| 源地址散列调度 | SH | 根据客户端 IP 散列,固定将同一客户端请求分配到同一 RS | 需保持会话一致性的场景(如未做会话共享的登录服务) |
| 目标地址散列调度 | DH | 根据目标 IP(如 VIP 或后端服务 IP)散列,固定将同一目标 IP 请求分配到同一 RS | 后端服务为缓存服务器(如 Redis)的场景 |
(二)动态调度算法
|--------------|-------|---------------------------------------------------|-----------------------|
| 算法名称 | 英文缩写 | 核心逻辑 | 适用场景 |
| 最少连接 | LC | 动态将请求分配到当前连接数最少的 RS | RS 负载波动较大的场景 |
| 加权最少连接 | WLC | 结合 RS 权重和连接数,优先分配到 "权重 / 连接数" 比值最大的 RS(权重高、连接少优先) | RS 配置不均衡且负载波动大的场景 |
| 基于局部性的最少连接 | LBLc | 根据客户端 IP 局部性,优先将请求分配给曾处理过该客户端请求且负载较低的 RS | 客户端请求具有地域 / IP 聚集性的场景 |
| 复杂的基于局部性最少连接 | LBLcr | 维护 "客户端 IP-RS 组" 映射,避免单台 RS 因局部性请求过载 | 大规模集群、客户端 IP 分布广的场景 |
| 最少期望延迟 | SED | 不考虑非活动连接,按 "(当前连接数 + 1)/ 权重" 计算优先级,值越小越优先 | 需优先调度高权重 RS 的场景 |
| 永不排队 | NQ | 若存在连接数为 0 的 RS,直接分配请求;无空闲 RS 时按 SED 规则调度 | 需减少请求排队延迟的场景 |
五、LVS 规则保存与重载
为避免服务器重启后 LVS 规则丢失,需将规则保存到配置文件,并通过服务重载:
1. 保存规则
# 方式 1:使用 ipvsadm 自带保存命令
ipvsadm -Sn > /etc/sysconfig/ipvsadm
# 方式 2:通过系统服务保存(部分系统支持)
systemctl save ipvsadm
2. 重载规则
# 方式 1:从配置文件重载
ipvsadm -R < /etc/sysconfig/ipvsadm
# 方式 2:重启 ipvsadm 服务(自动加载配置文件)
systemctl restart ipvsadm
3. 验证规则
# 查看当前 LVS 规则(数字格式显示 IP/端口)
ipvsadm -Ln
# 查看规则详情(含权重、连接数等)
ipvsadm -Lnv
六、常见问题排查
- RS 无法接收请求:
-
- 检查 DR 和 RS 的 VIP 是否配置正确(需在同一接口,如 lo 或 tunl0)。
-
- 验证 ARP 内核参数(arp_ignore/arp_announce)是否生效,可通过 sysctl -a | grep arp 查看。
-
- 确认防火墙 / SELinux 已关闭(或开放相关端口,如 80、443)。
2.负载均衡不轮询:
-
- 检查调度算法是否正确(如 rr/wrr),可通过 ipvsadm -Ln 确认。
-
- 若使用动态算法(如 LC),需检查 RS 连接数是否真实差异,可通过 ipvsadm -Lnv 查看。
3.TUN 模式数据包丢失:
-
- 确认 ipip 模块已加载(lsmod | grep ipip)。
-
- 检查反向路径过滤参数(rp_filter)是否设置为 0,避免隧道数据包被过滤。