手搓 LVS-DR 负载均衡:从原理到 6 节点双机轮询(附 ARP 抑制详解)
前段时间从零搭了一套 6 节点高可用集群:2 台 LVS 调度器 + 2 台 Nginx + 2 台 BIND DNS,用 Keepalived 做双活互备。
本篇是这个系列的第一篇:不用任何自动化工具,纯手工把 LVS-DR 配起来——因为只有手工做过一遍,才知道 Keepalived 到底替你做了什么。
全程 Rocky Linux 9,所有 IP 都是真实实验环境。
一、先看最终效果
在任意客户端上连续访问 VIP:
$curlhttp://192.168.88.200<h1>Web Node: web1(192.168.88.21)</h1>$curlhttp://192.168.88.200<h1>Web Node: web2(192.168.88.22)</h1>$curlhttp://192.168.88.200<h1>Web Node: web1(192.168.88.21)</h1>两次请求落在两台不同的后端机器上——这就是负载均衡成立的铁证。
但更值得讲的是:为什么这个"改一下 MAC 地址"的转发模式,能成为生产机房扛大流量的标配?
二、集群拓扑
客户端 │ ┌───────────────┴───────────────┐ │ ①www.yhy.com? ②HTTP请求 │ ▼ │ DNS VIP 192.168.88.50 Web VIP 192.168.88.200 │ │ ┌──────┴──────┐ VRRP心跳 ┌────────┴──────┐ │ lvs2 (.12) │◀───────────▶│ lvs1 (.11) │ │ DNS主/WEB备 │ │ WEB主/DNS备 │ └──────┬──────┘ └──────┬────────┘ │ DR转发 │ DR转发(仅改MAC) ▼ ▼ dns1(.31) ⇄ dns2(.32) web1(.21) web2(.22) BIND主从同步 Nginx Nginx| 主机 | IP | 角色 |
|---|---|---|
| lvs1 / lvs2 | .11 / .12 | LVS 调度器(Keepalived 双活,本篇先只看 lvs1) |
| web1 / web2 | .21 / .22 | Nginx 后端 |
| dns1 / dns2 | .31 / .32 | BIND 主从 |
| Web VIP | .200 | 对外提供 Web 服务的虚拟 IP |
| DNS VIP | .50 | 对外提供 DNS 服务的虚拟 IP |
后端首页写入自己的节点名,是整个实验最重要的"小动作":负载均衡的难点从来不是配通,而是证明请求真的被分发了。
三、LVS 三种模式,为什么选 DR
| 模式 | 数据路径 | 特点 |
|---|---|---|
| NAT | 请求、响应都过调度器(双向改 IP) | 简单,但调度器是带宽瓶颈 |
| DR | 只有请求过调度器(仅改 MAC),响应直连客户端 | 性能之王,要求同网段 |
| TUN | IP 隧道封装 | 跨网段,配置复杂 |
DR 高性能的秘密:HTTP 场景下请求是小流量、响应(网页/图片)是大流量。DR 让调度器只处理进门的小包,出门的大包由后端直接发往客户端——调度器压力极小。
DR 模式一次请求的完整旅程
客户端 (.1) │ ① 发包:目标IP=192.168.88.200,目标MAC=lvs1的MAC(ARP解析来的) ▼ lvs1:IPVS 命中 VIP:80 → 按 rr 算法选中 web1 │ ② 只改帧的【目标MAC】→ web1的MAC(源/目标IP都不动!) ▼ web1:lo:0 上也挂着 .200 │ ③ 目标IP是我 lo 上的地址 → 收下,交给 Nginx │ ④ 响应:源IP=.200,从 ens160 直接发回客户端 ★完全不经过 lvs1★ ▼ 客户端收到响应(全程以为在和 .200 说话)四、灵魂三问:DR 模式的三个"必须"
Q1:为什么后端必须绑 VIP?
包的目标 IP 是 .200,web1 如果不认识这个 IP,内核直接丢包。所以 RS 必须把 VIP 配在自己身上——绑在lo 回环口。
Q2:为什么是 /32 掩码?
如果给 lo 配 .200/24,内核会认为整个 88 网段都在 lo 上,路由直接乱套。/32 表示"这就是一个孤立地址,只用于收包,不参与路由"。
Q3:为什么必须压 ARP?(DR 模式的灵魂)
现在网段里有 3 台机器都有 .200(lvs1 的 ens160、web1/web2 的 lo)。客户端 ARP 广播"谁是 .200?"——三台都会抢答,流量可能直连后端绕过调度器,负载均衡直接失效。
解法:让后端"闭嘴":
# web1 / web2 上执行cat>/etc/sysctl.d/99-arp.conf<<'EOF' net.ipv4.conf.all.arp_ignore = 1 net.ipv4.conf.all.arp_announce = 2 net.ipv4.conf.default.arp_ignore = 1 net.ipv4.conf.default.arp_announce = 2 EOFsysctl--systemarp_ignore=1:收到 ARP 请求时,只有"目标 IP 恰好配在收到请求的那块网卡上"才应答。ARP 从 ens160 进来,VIP 在 lo 上 → 不应答 ✅arp_announce=2:对外发 ARP 时只用本网卡自己的 IP 做源地址,不拿 lo 上的 VIP 说事
结果:全网只有调度器应答 VIP 的 ARP → 流量必然先进调度器 → IPVS 才有分发权。
五、动手:三步配出双机轮询
第 1 步:后端(web1、web2 执行相同内容)
# VIP 绑到 lo,掩码必须 /32ipaddradd192.168.88.200/32 dev lo# ARP 抑制(上面已写 sysctl 文件的这里会自动生效)sysctlnet.ipv4.conf.all.arp_ignore# 应输出 = 1第 2 步:调度器(lvs1)
# VIP 挂到真实网卡ipaddradd192.168.88.200/32 dev ens160# 安装 IPVS 管理工具dnf-yinstallipvsadm# 建虚拟服务:-t TCP服务,-s rr 轮询算法ipvsadm-A-t192.168.88.200:80-srr# 挂两台后端:-r 真实服务器,-g 表示 DR 模式ipvsadm-a-t192.168.88.200:80-r192.168.88.21:80-gipvsadm-a-t192.168.88.200:80-r192.168.88.22:80-g# 查看规则表ipvsadm-Lnipvsadm -Ln的关键输出:
TCP 192.168.88.200:80 rr -> 192.168.88.21:80 Route 1 0 0 -> 192.168.88.22:80 Route 1 0 0Route即 DR 转发(直接路由)。
第 3 步:验证
foriin1234;docurl-shttp://192.168.88.200;done# web1 → web2 → web1 → web2 交替出现再在 lvs1 上看分发计数,两台的 Conns 都在涨,说明真的在分:
ipvsadm-Ln--stats加餐 1:会话保持(生产常用)
# -p 60:同一客户端 60 秒内粘在同一台后端ipvsadm-E-t192.168.88.200:80-srr-p60加餐 2:眼见为实——响应真的不过调度器
lvs1 上抓包,然后客户端访问 VIP:
tcpdump-nn-iens160host客户端IP and port80你会看到只有请求方向的包,没有响应包——响应从 web1/web2 直接连回客户端了。这就是 DR 的性能来源。
六、两个我亲手踩过的坑
坑 1:在持有 VIP 的机器上 curl VIP,会无限挂起
配完后我在 lvs1 上验证curl http://192.168.88.200,一直卡死。原因很反直觉:
- lvs1 自己持有 .200 → 本机 curl 时源 IP 被内核选成 .200
- 请求被 IPVS 转发给 web1,web1 回包给"客户端"——客户端 IP 是 .200
- web1 一看:.200 是我自己 lo 上的地址!→ 回包走 loopback,永远出不了网卡
- SYN-ACK 消失,curl 挂死
结论:DR 模式下,调度器和后端都不能当自己 VIP 的客户端。验证请用干净的第三方机器。(后端上 curl VIP 也是同理——会被本机 Nginx 劫持,永远只返回自己。)
坑 2:手工配置重启即失
ip addr add和ipvsadm -A都是内存态,重启全没。生产上的做法是用 Keepalived 把它们变成配置文件,还能附赠健康检查和 VIP 漂移——这正是系列第二篇的内容。
七、本篇命令速查
ipvsadm-A-tVIP:80-srr# 添加虚拟服务(rr 轮询)ipvsadm-a-tVIP:80-rRS:80-g# 挂后端(-g = DR 模式)ipvsadm-E-tVIP:80-srr-p60# 改为带会话保持ipvsadm-Ln/-Ln--stats# 规则表 / 分发计数ipvsadm-C# 清空所有规则ipaddraddVIP/32 dev lo# RS 侧绑 VIPsysctlnet.ipv4.conf.all.arp_ignore# 验证 ARP 抑制生效系列预告
- 第二篇:《Keepalived 双活互备实战:两组 VRRP、健康检查与故障切换》——手工命令如何变成配置文件、TCP_CHECK 自动摘除故障节点、两组 VRRP 实现双活、priority-0 通告与 GARP 的切换细节
- 第三篇:《一次 DNS 全链路排障:健康检查全绿但 VIP 不通》——listen-on 盲区,一个健康检查永远测不出的问题
如果这篇对你有帮助,欢迎点赞收藏;实验环境为 VMware + Rocky Linux 9,6 台 1C1G 虚拟机即可完整复现。