前言
前一篇文章中,我们已经知道 LVS-DR 的基本链路:
客户端
↓
访问 VIP
↓
LVS 选择一台 Real Server
↓
修改二层目标 MAC
↓
Real Server 直接响应客户端
理论看起来并不复杂:
请求经过 LVS
响应绕过 LVS
但真正开始搭建实验时,会立刻遇到一连串问题。
为什么 LVS 和所有 Real Server 都要配置同一个 VIP?
多台机器配置同一个 VIP,不会发生地址冲突吗?
VIP 为什么要配置在
lo上,而不是物理网卡上?
为什么 VIP 通常使用
/32掩码?
arp_ignore=1到底忽略了什么?
arp_announce=2是禁止发送 ARP 吗?
为什么修改
arp_ignore还不够?
IPVS 既然不终止 TCP 连接,为什么还会记录
SYN_RECV和ESTABLISHED?
使用轮询算法后,浏览器不断刷新,为什么不一定轮流访问两台服务器?
IPVS 出现
SYN_RECV,就一定是 Real Server 的网线断了吗?
DR 只能在同一局域网使用,那跨机房怎么办?
这些问题说明,LVS-DR 真正困难的地方并不是执行三条 ipvsadm 命令,而是理解三组关系:
VIP 与本地地址
ARP 与 Linux 主机模型
IPVS 连接表与真实 TCP 连接
本文将从一个可执行的实验开始,把这三组关系彻底串起来。
一、先规划实验
准备三台 Linux 主机:
Director:
192.168.150.11
Real Server 1:
192.168.150.12
Real Server 2:
192.168.150.13
它们位于同一个二层网络:
192.168.150.0/24
对外提供的虚拟服务地址为:
VIP:
192.168.150.100
服务端口:
8080
完整拓扑:
客户端
192.168.150.1
│
│ 访问 192.168.150.100:8080
▼
LVS Director
DIP:192.168.150.11
VIP:192.168.150.100
│
├── RS1:192.168.150.12
│
└── RS2:192.168.150.13
地址含义:
| 名称 | 含义 | 实验地址 |
|---|---|---|
| CIP | Client IP | 192.168.150.1 |
| VIP | Virtual IP | 192.168.150.100 |
| DIP | Director IP | 192.168.150.11 |
| RIP | Real Server IP | .12、.13 |
客户端永远只访问:
192.168.150.100:8080
它不知道后面存在几台 Real Server。
二、一次 SYN 怎么走?
客户端发起 TCP 连接:
源:
192.168.150.1:51000
目标:
192.168.150.100:8080
第一次进入 Director 的以太网帧可以抽象为:
Ethernet Header
源 MAC:客户端 MAC
目标 MAC:Director MAC
IP Header
源 IP:CIP
目标 IP:VIP
TCP Header
源端口:51000
目标端口:8080
标志位:SYN
IPVS 匹配到虚拟服务:
TCP
192.168.150.100:8080
假设轮询算法选择 RS1。
DR 模式不会把目标 IP 改成:
192.168.150.12
它保留原来的网络层数据:
CIP:51000
→
VIP:8080
只让当前二层帧发给 RS1:
目标 MAC:
Director MAC
↓
RS1 MAC
重新封装后的帧:
Ethernet Header
源 MAC:Director MAC
目标 MAC:RS1 MAC
IP Header
源 IP:CIP
目标 IP:VIP
TCP Header
源端口:51000
目标端口:8080
标志位:SYN
因此,DR 的关键不是:
把 VIP 转换成 RIP
而是:
保留 VIP
通过二层地址把包送到选中的 Real Server
经典 LVS-DR 的 Real Server 会直接回复客户端,从而让大量响应流量绕过 Director;与此同时,它也带来了同一二层网络和 VIP ARP 管理等额外要求。
三、为什么 RS 必须拥有 VIP?
RS1 收到以太网帧后,发现:
目标 MAC = 自己的 MAC
于是网卡接收该帧并移除链路层 Header。
接下来,IP 层看到:
目标 IP = 192.168.150.100
但 RS1 的物理网卡地址是:
192.168.150.12
如果 RS1 完全不知道 VIP,它会认为:
这个 IP 数据包不是发给本机的
数据不会正常进入本地 TCP 监听端口。
因此,每一台 Real Server 都必须把 VIP 配置成本地地址:
RS1 本地地址:
192.168.150.12
192.168.150.100
RS2 本地地址:
192.168.150.13
192.168.150.100
此时 RS1 收到目标为 VIP 的数据包,就能识别:
192.168.150.100 是本机地址
然后继续按照端口查找:
8080
↓
本地 Web Server
这就是 Real Server 必须拥有 VIP 的根本原因:
DR 没有把目标地址转换成 RIP,所以 Real Server 必须主动认领 VIP,才能接收目标为 VIP 的数据包。
四、为什么放在 lo?
常见配置是:
sudo ip addr add \
192.168.150.100/32 \
dev lo
lo 是回环接口。
把 VIP 配置在 lo 上,不代表外部数据包真的从网线进入后,还必须绕到 lo 设备走一遍。
它的核心作用是告诉 Linux:
VIP 属于本机
真实数据包依然从物理网卡进入:
eth0
↓
Linux IP 层
↓
本地地址判断
↓
TCP
↓
8080 端口
这里要区分两个概念:
数据从哪个接口进入
和:
这个目标 IP 是否属于本机
VIP 配置在 lo 上,解决的是第二个问题。
五、为什么使用 /32?
Real Server 常见配置是:
192.168.150.100/32
而不是:
192.168.150.100/24
/32 表示只为这个单独地址建立主机路由:
192.168.150.100/32
如果在 lo 上配置 /24:
192.168.150.100/24
内核可能同时得到一条覆盖整个网段的直连路由:
192.168.150.0/24 dev lo
而物理接口上可能已经存在:
192.168.150.0/24 dev eth0
结果是同一目标网络出现多个本地路由候选:
192.168.150.0/24 dev eth0
192.168.150.0/24 dev lo
这可能影响出站路由选择,使本应从物理网卡发送的数据被错误地判定为本地路径。
因此 /32 的准确作用是:
只声明 VIP 这个单独地址属于本机,不要让回环接口额外声明整个物理网段都与自己直连。
不能简单概括为:
不用 /32 就一定形成死循环
是否发生故障还取决于已有路由、优先级和具体系统配置,但 /32 能避免为 lo 创建不必要的整段直连路由。
六、真正麻烦的是 ARP
现在 Director 和两台 Real Server 都拥有 VIP:
Director:
192.168.150.100
RS1:
192.168.150.100
RS2:
192.168.150.100
当客户端准备访问 VIP 时,会先查询:
谁拥有 192.168.150.100?
理想结果应该是:
Director 回答:
VIP 对应 Director MAC
请求先进入 Director,才能执行 IPVS 调度。
如果 RS1 也回答:
VIP 对应 RS1 MAC
客户端可能绕过负载均衡器,直接把流量发给 RS1:
客户端
↓
RS1
此时会发生:
IPVS 不再参与调度
流量可能全部落到某台 RS
后端扩缩容对客户端不可控
所以 Real Server 上的 VIP 必须同时满足两条要求:
对内可见:
Linux 知道 VIP 属于本机
对外隐藏:
Real Server 不抢答 VIP 的 ARP
“隐藏 VIP”隐藏的不是 IP Header,也不是让操作系统忘记这个地址,而是:
不让 Real Server 在外部二层网络中把自己宣告成 VIP 的拥有者。
七、Linux 为什么会抢答?
很多人会自然认为:
VIP 配在 lo 上
ARP 请求从 eth0 进来
所以 eth0 不会替 lo 回答
但 Linux 默认采用的是较宽松的主机地址模型。
在 Linux 中,IP 地址通常被视为属于整个主机,而不是严格绑定到某一块网卡。正因为如此,收到 ARP 请求的接口有可能为配置在其他接口上的本地地址作出响应;内核文档也明确指出,这种默认行为在负载均衡等复杂拓扑中可能造成问题。
所以仅执行:
ip addr add 192.168.150.100/32 dev lo
并不能保证 VIP 自动隐藏。
还要调整:
arp_ignore
arp_announce
八、arp_ignore 控制什么?
arp_ignore 控制:
收到 ARP Request 时,本机在什么条件下发送 ARP Reply。
默认值:
arp_ignore = 0
含义是:
只要目标 IP 是任意本地地址
就可以响应
即使请求从 eth0 进入,而目标地址配置在 lo 上,也可能回答。
设置:
arp_ignore = 1
表示:
只有目标 IP
配置在收到 ARP 请求的那个接口上
才响应
在 DR 的 Real Server 中:
ARP Request 从 eth0 进入
VIP 配置在 lo 上
因此:
eth0 不响应 VIP 的 ARP Request
Linux 内核对 arp_ignore=1 的定义正是:仅当目标 IP 是请求进入接口上的本地地址时才应答。all 与具体接口的配置会共同影响最终生效值。
九、arp_announce 控制什么?
arp_announce 经常被错误解释成:
是否允许发送免费 ARP
或者:
是否允许向外宣布 IP
它更准确地控制:
本机发送 ARP Request 时,应如何选择 ARP 报文中的源 IP 地址。
默认:
arp_announce = 0
系统可以使用任意本地地址作为 ARP 请求中的源地址。
在多网卡、多地址环境中,这可能导致系统把 VIP 当成 ARP 请求的源地址带出去:
Who has 192.168.150.1?
Tell 192.168.150.100
周围设备可能因此学习到:
VIP
→
Real Server MAC
设置:
arp_announce = 2
表示系统应尽量选择与当前目标和出口接口最匹配的本地源地址,而不是随意使用其他接口上的 VIP。
因此二者分工不同:
arp_ignore
控制收到请求后要不要回答
arp_announce
控制主动发请求时使用哪个源 IP
只设置其中一个,并不能完整解决 VIP 泄露问题。
十、配置隐藏 VIP
以下命令在两台 Real Server 上执行。
先创建持久化配置:
sudo tee /etc/sysctl.d/99-lvs-dr.conf \
> /dev/null <<'EOF'
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.eth0.arp_ignore = 1
net.ipv4.conf.eth0.arp_announce = 2
EOF
加载:
sudo sysctl --system
再配置 VIP:
sudo ip addr add \
192.168.150.100/32 \
dev lo
检查:
ip addr show lo
应看到:
inet 192.168.150.100/32 scope global lo
Red Hat 的 LVS-DR 文档也使用 arp_ignore=1 和 arp_announce=2,目的就是阻止 Real Server 宣布 VIP 或回答 VIP 的 ARP 请求。
实际环境中的物理接口不一定叫 eth0,也可能是:
ens33
ens160
enp0s3
必须根据:
ip link show
替换配置中的接口名称。
十一、为什么不用 echo 临时修改?
也可以直接执行:
echo 1 | sudo tee \
/proc/sys/net/ipv4/conf/eth0/arp_ignore
echo 2 | sudo tee \
/proc/sys/net/ipv4/conf/eth0/arp_announce
这会立即修改内核参数,但通常在重启后丢失。
更适合长期使用的是:
/etc/sysctl.d/*.conf
↓
sysctl --system
/proc/sys/net 暴露的是内核网络参数接口,不是普通磁盘配置文件。修改这些伪文件会改变当前内核状态。
资料中提到“不能用 vi,因为 vi 会创建新变量”,这个解释不准确。
问题并不在于 vi 创建了新的内核变量,而在于:
/proc/sys 文件不是普通磁盘文件
编辑器常用的临时文件、重命名和原子替换流程
不一定适用于这种伪文件
因此应使用:
sysctl
echo
tee
等直接写入方式。
十二、启动 Real Server
为了方便观察,让两台 Real Server 返回不同内容。
RS1:
mkdir -p ~/lvs-web
echo 'response from rs1' \
> ~/lvs-web/index.html
cd ~/lvs-web
python3 -m http.server \
8080 \
--bind 0.0.0.0
RS2:
mkdir -p ~/lvs-web
echo 'response from rs2' \
> ~/lvs-web/index.html
cd ~/lvs-web
python3 -m http.server \
8080 \
--bind 0.0.0.0
这里只是实验,所以使用 Python 简易 HTTP Server。
生产环境可以换成:
Nginx
Tomcat
Jetty
Netty
其他 TCP 服务
分别验证 RIP:
curl http://192.168.150.12:8080/
curl http://192.168.150.13:8080/
预期:
response from rs1
response from rs2
然后检查监听:
ss -lntp | grep 8080
应确认服务监听:
0.0.0.0:8080
或者明确监听 VIP:
192.168.150.100:8080
如果只监听:
192.168.150.12:8080
目标为 VIP 的数据包不一定能被该监听 Socket 接收。
十三、配置 Director
先配置 VIP:
sudo ip addr add \
192.168.150.100/32 \
dev eth0
加载 IPVS 相关模块:
sudo modprobe ip_vs
sudo modprobe ip_vs_rr
清理旧实验规则:
sudo ipvsadm -C
添加虚拟服务:
sudo ipvsadm -A \
-t 192.168.150.100:8080 \
-s rr
添加 RS1:
sudo ipvsadm -a \
-t 192.168.150.100:8080 \
-r 192.168.150.12:8080 \
-g \
-w 1
添加 RS2:
sudo ipvsadm -a \
-t 192.168.150.100:8080 \
-r 192.168.150.13:8080 \
-g \
-w 1
参数含义:
-A
添加虚拟服务
-a
为虚拟服务添加 Real Server
-t
TCP 服务
-s rr
Round Robin 调度
-r
Real Server 地址
-g
Direct Routing
-w
权重
IPVS 是 Linux 内核中的四层负载均衡机制,按照连接粒度为 TCP、UDP 等服务选择 Real Server;NAT、TUN 和 DR 是它支持的主要转发方式。
十四、查看规则
执行:
sudo ipvsadm -L -n
预期结果类似:
IP Virtual Server version ...
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight
TCP 192.168.150.100:8080 rr
-> 192.168.150.12:8080 Route 1
-> 192.168.150.13:8080 Route 1
其中:
Route
表示 Direct Routing。
查看统计:
sudo ipvsadm -L -n --stats
查看速率:
sudo ipvsadm -L -n --rate
查看连接表:
sudo ipvsadm -L -n -c
规则表与连接表不是同一回事:
规则表
描述可以把新连接分给谁
连接表
描述某条连接已经被分给了谁
十五、怎么正确验证轮询?
很多实验使用:
浏览器打开 VIP
不断按 F5
然后期望:
rs1
rs2
rs1
rs2
这并不可靠。
原因是 IPVS 典型情况下调度的是:
TCP 连接
而不是:
每一次 HTTP 请求
IPVS 项目文档将其描述为连接粒度的调度:同一客户端 Socket 与服务端 Socket 之间的通信会在连接有效期内继续进入同一 Real Server。
HTTP/1.1 默认支持持久连接,一条 TCP 连接可以承载多次请求和响应。浏览器刷新页面时,可能继续复用已有连接。
于是可能出现:
一条 TCP 连接
↓
被分给 RS1
↓
多个 HTTP 请求仍然进入 RS1
更稳定的实验方式是每次主动关闭连接:
for i in $(seq 1 6); do
curl -s \
-H 'Connection: close' \
http://192.168.150.100:8080/
done
预期:
response from rs1
response from rs2
response from rs1
response from rs2
即使如此,真实结果仍可能受到:
客户端连接复用
调度算法
连接持久化规则
失败节点
连接状态超时
影响。
十六、LVS 不握手,为什么有连接表?
经典 LVS-DR 不像七层反向代理那样建立两条独立连接:
客户端 TCP
↓
代理终止
↓
代理重新连接后端
TCP 的真实端点仍然是:
客户端
↔
Real Server
但 Director 会看到并转发握手数据包:
SYN
SYN+ACK
ACK
还会观察后续:
FIN
RST
超时
IPVS 必须记录:
这条连接分给了哪台 RS
否则同一条连接的后续数据包可能被分给不同服务器。
因此:
IPVS 不终止 TCP
不等于:
IPVS 完全不关心 TCP 状态
更准确的描述是:
IPVS 不拥有业务 Socket,也通常不作为 TCP 代理终止连接;但它会维护自己的连接映射状态,用于调度和后续数据包转发。
Linux 内核为 IPVS 维护连接状态、超时和同步机制,DR、NAT 等模式下的状态观察还可能存在差异。
十七、调度算法看什么?
1. RR
Round Robin
按照顺序把新连接分配给各节点:
连接 1 → RS1
连接 2 → RS2
连接 3 → RS1
连接 4 → RS2
适合:
节点性能接近
连接成本大致接近
2. WRR
Weighted Round Robin
加入权重:
RS1 权重 3
RS2 权重 1
长期来看,RS1 会获得更多新连接。
3. LC
Least Connection
把新连接分给当前连接较少的节点。
它不是统计:
HTTP 请求数量
CPU 使用率
业务线程数
主要依据 IPVS 掌握的连接状态。
4. WLC
Weighted Least Connection
同时考虑连接数与节点权重。
需要注意:
连接少
不一定等于:
负载低
例如:
RS1:
1000 条空闲长连接
RS2:
10 条正在传输大文件的连接
只看连接数,RS2 更空闲;但真实带宽压力可能恰恰更高。
因此 IPVS 调度算法提供的是网络连接层面的近似分配,不会自动理解 Java 堆使用率、数据库压力或业务复杂度。
十八、SYN_RECV 说明什么?
执行:
sudo ipvsadm -L -n -c
可能看到:
TCP SYN_RECV
CIP:51000
VIP:8080
RIP:8080
它表示 IPVS 已经为这次连接创建记录,但握手尚未在预期时间内推进到已建立状态。
这时不能直接下结论:
LVS 一定正常
后端网线一定有问题
可能原因包括:
Director 无法获得 RS 的 MAC
请求没有到达 RS
RS 没有配置 VIP
RS 错误响应 VIP 的 ARP
RS 的 8080 端口没有监听
防火墙丢弃 SYN
响应没有正确返回客户端
反向路径检查丢包
交换机或虚拟网络配置错误
SYN_RECV 只能告诉我们:
IPVS 已经看到连接开始,但完整握手尚未完成。
它不能单独指出故障一定在哪一台机器。
十九、第一步看 Director
先检查 VIP:
ip addr show eth0
检查规则:
sudo ipvsadm -L -n
检查连接:
sudo ipvsadm -L -n -c
检查 Director 能否找到 RS 的二层地址:
ip neigh show \
192.168.150.12
ip neigh show \
192.168.150.13
异常状态可能是:
INCOMPLETE
FAILED
再测试:
ping -c 1 192.168.150.12
ping -c 1 192.168.150.13
抓包:
sudo tcpdump -ni eth0 \
'arp or tcp port 8080'
重点观察:
客户端 SYN 是否到达 Director
Director 是否发出指向 RS MAC 的 SYN
RS 的 SYN+ACK 是否直接返回
二十、第二步看 Real Server
检查 RIP 和 VIP:
ip addr show eth0
ip addr show lo
必须同时存在:
RIP:
192.168.150.12/24
VIP:
192.168.150.100/32
检查 ARP 参数:
sysctl \
net.ipv4.conf.all.arp_ignore \
net.ipv4.conf.all.arp_announce \
net.ipv4.conf.eth0.arp_ignore \
net.ipv4.conf.eth0.arp_announce
检查端口:
ss -lntp | grep 8080
检查本机是否能访问 VIP:
curl http://192.168.150.100:8080/
抓包:
sudo tcpdump -ni eth0 \
'arp or tcp port 8080'
如果能够看到客户端 SYN,却没有 SYN+ACK:
检查 VIP
检查监听地址
检查防火墙
检查本地路由
如果能够看到 SYN+ACK 发出,但客户端收不到:
检查默认路由
检查上游网络
检查源地址是否为 VIP
检查反向路径过滤
二十一、别忘了 rp_filter
DR 的返回路径是:
请求:
客户端 → Director → RS
响应:
RS → 客户端
这是非对称路径。
在多网卡或复杂路由环境中,Linux 的反向路径检查可能认为某些数据包来源路径不符合预期,从而丢弃数据。
rp_filter=1 是严格模式:如果入站接口不是内核认为到该源地址的最佳反向路径,数据包可能被丢弃。复杂路由或非对称路径中,内核文档建议考虑宽松模式,但不能在不了解安全影响时直接全局关闭。
查看:
sysctl \
net.ipv4.conf.all.rp_filter \
net.ipv4.conf.eth0.rp_filter
这里不建议把排错习惯写成:
sysctl -w \
net.ipv4.conf.all.rp_filter=0
然后永久忘记。
正确做法是先确认:
实际回程路由
接口数量
源地址验证要求
是否真的发生 rp_filter 丢包
二十二、Director 为什么看不到 Socket?
在 Director 上执行:
ss -ntp
可能看不到:
192.168.150.100:8080
对应的本地业务 Socket。
这是正常的。
DR 模式下,Director 没有运行监听 8080 端口的 Web Server,也没有通过 accept() 接收客户端连接。
它维护的是:
IPVS 连接映射
而不是:
应用 Socket
所以排查时应同时查看:
ss -ntp
和:
ipvsadm -L -n -c
二者回答的问题不同:
ss
本机 TCP Socket 是什么状态?
ipvsadm
IPVS 转发连接是什么状态?
不能因为 ss 中没有连接,就断定 LVS 没有收到数据。
二十三、“MAC 欺骗”准确吗?
资料中将 DR 描述为:
MAC 欺骗
这个说法有助于形象理解:
目标 IP 明明是 VIP
却通过 RS 的 MAC 把包送到了 RS
但从技术表达上,更准确的说法是:
二层目标地址重写
或者:
重新封装目标 MAC
“欺骗”容易让人联想到:
ARP Spoofing
MAC 地址攻击
伪造身份
而 LVS-DR 的行为是受控的负载均衡转发:
Director 根据规则选择 RS
↓
使用 RS 的链路层地址发送 Frame
这里真正需要防止的,反而是 Real Server 向外错误宣告 VIP,导致外部邻居绕过 Director。
二十四、DR 为什么不能随意跨网段?
Director 要把请求交给 RS 时,需要构造:
目标 MAC = RS MAC
MAC 地址只在当前二层网络中有效。
如果拓扑变成:
Director
↓
路由器
↓
远端 RS
Director 当前能直接发送的二层目标只能是:
下一跳路由器 MAC
不能直接把远端 RS 的 MAC 写入本地帧,并期待中间路由器把原链路层 Header 原样带到另一个网络。
所以经典 DR 通常要求:
Director 与 RS
处于同一二层广播域
这也是为什么 DR 虽然能让响应直接返回,但会增加网络拓扑约束。
如果 Real Server 必须位于其他网络,就需要另一种封装方式。
二十五、扩展:TUN 怎么跨网络?
LVS-TUN 的思路是:
在原始 IP 数据包外
再套一层 IP Header
原始数据包:
内层 IP:
源 IP = CIP
目标 IP = VIP
Director 选择远端 Real Server 后,在外面增加:
外层 IP:
源 IP = DIP
目标 IP = RIP
协议 = IP-in-IP
完整结构:
┌──────────────────────────────┐
│ 外层 IP Header │
│ DIP → RIP │
├──────────────────────────────┤
│ 内层 IP Header │
│ CIP → VIP │
├──────────────────────────────┤
│ TCP Header │
├──────────────────────────────┤
│ Application Data │
└──────────────────────────────┘
外层负责:
把数据送到远端 Real Server
Real Server 解封装后看到原始内层包:
CIP → VIP
于是仍然可以使用 VIP 接收连接,并直接响应客户端。
IPVS 官方资料把 NAT、TUN 和 DR 列为三种主要负载均衡方式;TUN 通过 IP 隧道把请求送到不同网络中的 Real Server,同时允许响应绕过 Director。
二十六、TUN 的代价
TUN 解决了 DR 的同一二层限制,但增加了新的要求:
Real Server 支持隧道解封装
Real Server 配置 VIP
网络允许隧道协议通过
需要考虑额外 Header
需要考虑 MTU
排错路径更复杂
每增加一层外部 Header,可用于承载原始数据的有效空间就会减少。
如果链路 MTU 为:
1500
又增加外层 IP Header,就要关注:
分片
Path MTU Discovery
TCP MSS
中间防火墙
因此不能简单认为:
TUN = 没有任何约束的远程 DR
更准确的选择关系是:
NAT
拓扑灵活
请求与响应都经过 Director
DR
响应直返
通常要求同一二层网络
TUN
响应直返
可以跨三层网络
增加隧道与 MTU 复杂度
二十七、手工配置为什么不够?
前面的配置依靠:
ip addr
sysctl
ipvsadm
手工完成。
机器重启后,可能丢失:
VIP
IPVS 规则
部分临时参数
更重要的是,IPVS 本身只按规则转发。
如果 RS1 的 Web Server 已经崩溃,但 IPVS 规则中仍然存在 RS1:
新连接
↓
仍可能被分给 RS1
这时需要:
健康检查
自动移除故障节点
故障恢复后重新加入
Director 主备切换
VIP 漂移
Keepalived 可以通过健康检查维护 IPVS 后端池,并通过 VRRP 管理浮动 VIP 和 Director 故障切换;其内部会把配置转换成 IPVS 规则写入内核。
关系可以画成:
Keepalived
├── VRRP
│ 管理 VIP 主备切换
│
├── Health Checker
│ 判断 RS 是否可用
│
└── IPVS Wrapper
写入内核负载均衡规则
所以:
LVS / IPVS
解决流量怎么分
Keepalived
解决规则怎么维护、节点怎么检查、VIP 怎么切换
二十八、常见误区
1. VIP 配在 lo,数据就从 lo 接收
错误。
数据仍从物理网卡进入;lo 上的 VIP 用于让内核把目标为 VIP 的包认作本地数据。
2. VIP 配在 lo 就自动隐藏
错误。
Linux 默认可能为其他接口上的本地地址回答 ARP,需要调整 ARP 行为。
3. arp_ignore 和 arp_announce 是同一个功能
错误。
arp_ignore
控制是否回复 ARP Request
arp_announce
控制 ARP Request 使用哪个本地源 IP
4. arp_announce=2 表示禁止免费 ARP
错误。
它主要约束发送 ARP Request 时的源地址选择。
5. /32 是为了让 VIP 不能通信
错误。
/32 是为了只声明单个主机地址,避免 lo 为整个物理网段生成不必要的直连路由。
6. LVS 完全不知道 TCP 状态
错误。
IPVS 不终止业务 TCP 连接,但会维护连接映射、状态和超时。
7. 看到 SYN_RECV 就说明 LVS 一定正常
错误。
它只能说明 IPVS 已创建连接记录但握手没有完成,仍需检查 Director、RS、ARP、监听端口、防火墙和回程路由。
8. 浏览器刷新一次就会重新轮询
不一定。
HTTP/1.1 可以复用 TCP 连接,而 IPVS 通常按照连接粒度调度。
9. Director 上没有 Socket 就说明没收到请求
错误。
DR 模式下应查看 IPVS 连接表和抓包,而不是只看本地应用 Socket。
10. DR 会把 VIP 修改成 RIP
错误。
DR 保留目标 VIP,主要重新封装目标 MAC。
11. Real Server 可以不配置 VIP
错误。
RS 必须把目标为 VIP 的数据识别为本地数据。
12. Real Server 可以公开响应 VIP 的 ARP
错误。
这样会使客户端绕过 Director,直接把请求发给某台 Real Server。
13. DR 可以直接把远端 RS 的 MAC 写进数据包
错误。
MAC 地址只在当前二层链路有效,经典 DR 通常要求 Director 和 RS 位于同一二层网络。
14. TUN 只是把 MAC 换成远端服务器 MAC
错误。
TUN 是 IP-in-IP 等三层隧道封装,不是跨互联网传递一个 Ethernet MAC。
15. 最少连接一定代表最低负载
错误。
连接数量无法直接代表 CPU、带宽、业务耗时和数据库压力。
16. 手工执行 ipvsadm 就实现了高可用
错误。
它只建立当前内核中的负载均衡规则,还缺少健康检查、规则持久化和 Director 故障切换。
总结
LVS-DR 的实验拓扑是:
客户端
↓
VIP
↓
Director
↓
Real Server
客户端发送:
CIP:ClientPort
→
VIP:ServicePort
Director 选择 Real Server 后:
IP Header
保持 CIP → VIP
Ethernet Header
目标 MAC 改为 RS MAC
Real Server 必须拥有 VIP,否则无法接收目标为 VIP 的数据包:
VIP
配置在 lo
使用 /32
/32 的目的不是让 VIP 失去网络能力,而是:
只声明单个 VIP 属于本机
避免 lo 声明整个物理网段
因为 Linux 默认把 IP 地址视为属于整台主机,所以 VIP 配在 lo 后,物理接口仍可能替它响应 ARP。
因此需要:
arp_ignore=1
不替其他接口上的 VIP 回答 ARP
arp_announce=2
发送 ARP 请求时避免使用不合适的 VIP 作为源地址
完整请求链路:
客户端发送 SYN
↓
目标 IP = VIP
↓
目标 MAC = Director
↓
IPVS 选择 RS
↓
目标 MAC 改为 RS
↓
目标 IP 仍然是 VIP
↓
RS 因本地配置 VIP 而接收
↓
TCP 与客户端完成连接
响应链路:
Real Server
↓
源 IP = VIP
↓
直接返回客户端
IPVS 不会像七层代理那样终止客户端连接,但会维护:
连接映射
TCP 状态
超时
调度结果
因此调试时需要同时查看:
ipvsadm -L -n
规则表
ipvsadm -L -n -c
IPVS 连接表
ss -lntp
本地 Socket
tcpdump
真实数据包路径
ip neigh
二层邻居状态
如果 Director 与 Real Server 无法位于同一二层网络,可以考虑 TUN:
外层:
DIP → RIP
内层:
CIP → VIP
它允许请求跨三层网络到达远端 Real Server,同时保持响应直返,但会引入隧道、MTU 和运维复杂度。
最后,可以用一句话概括 LVS-DR 的搭建逻辑:
VIP 配在 Real Server 上,是为了让内核接收目标为 VIP 的数据;VIP 对外隐藏,是为了让所有新请求仍先经过 Director;而 IPVS 通过改写当前链路的目标 MAC,把同一个 VIP 背后的连接分配给不同服务器。
评论区