一台机器的CPU和带宽顶不住的时候,很多人第一反应是在前面挂一层 Nginx 做反向代理。真挂上去之后发现,Nginx 那一层自己先变成了瓶颈:连接数上不去、worker 进程调度开销大、TLS 握手吃掉一半 CPU,后端加机器解决不了入口转发这一层的问题。这种场景下就该把视线往协议栈下面挪一层,回到四层转发,而 LVS(Linux Virtual Server)就是这个位置上最经典的一套方案——它跑在内核里,只做数据包的地址改写,不理解 HTTP,也不需要在用户态复制数据,一台普通配置的机器就能顶住几十万并发连接。这篇内容写给两类人:一类是刚接触负载均衡、想搞清楚 LVS 到底在内核里做了什么动手又怕搞坏环境的同学;另一类是已经在用 Nginx 或 HAProxy、想补上四层这块短板的从业者。我以 LVS 集群的基本原理打底,重点把 NAT 模式的部署链路一条一条走通,包括网络规划、ipvsadm 命令的逐条解释、验证手段,以及我实际部署中踩过的几个坑。所有配置都是可以在虚拟机里复现的,跟着做一遍就能跑起来。
1. LVS 在整条请求链路上究竟改动了什么
1.1 从一个数据包出发理解四层转发
很多人学 LVS 卡住,不是命令记不住,而是脑子里没有数据包的走向图。四层负载均衡的本质其实非常简单:一个请求从客户端发出来,目标地址写的是对外的那个 IP(习惯上叫 VIP),LVS 所在的这台机器(Director)收到包之后,把 IP 头里的目的地址悄悄改成某台后端真实服务器(Real Server,下面简称 RS)的地址,再把包转出去;后端处理完,响应沿着原路回去,或者按另一种方式直接回给客户端。整个过程里,客户端始终认为自己是在和 VIP 通信,后端也知道自己接到的请求是给 VIP 的,中间那台机器对两端都是透明的。这个"偷偷改地址"的动作就是 NAT,在网络层就是 DNAT 和 SNAT 的组合。
之所以叫四层,是因为 LVS 只看 IP 头里的源地址、目的地址、协议号、源端口、目的端口这五元组,它不拆 TCP 报文里的应用层内容,不知道请求的是/api/login还是/static/logo.png,也不会解析 Host 头。这个限制让它在功能上不如 Nginx 灵活,但同时换来了极高的转发效率——它做的事情越少,单位时间内能处理的包就越多。这个取舍后面会反复提到,先把链路走通更重要。
1.2 内核里的 ip_vs 模块才是真正的干活主体
跑ipvsadm命令的时候,容易产生一个误解,以为ipvsadm是个守护进程,像 Nginx 那样常驻在后台处理请求。其实不是。ipvsadm只是一个控制面工具,它的作用相当于往内核里写配置,真正的转发逻辑全部由内核模块ip_vs完成。这一点理解清楚之后,很多疑惑会自动消失:比如为什么ipvsadm命令执行完就退出了、为什么系统重启之后规则没了、为什么用ps看不到 LVS 进程、为什么卸载ipvsadm软件包原先的转发规则还在生效。
可以用lsmod | grep ip_vs确认模块是否加载。一般来说,主流发行版的内核都编译了ip_vs,执行modprobe ip_vs就能拉起来。一些依赖模块,比如ip_vs_rr(轮询调度)、ip_vs_wrr(加权轮询)、ip_vs_wlc(加权最少连接)是按需加载的,你用了哪种算法,对应的模块才会出现。如果某次配置完之后发现调度的行为和预期不符,先看看对应的算法模块有没有加载成功,这是一个很省时间的检查点。
内核里的连接状态记录用一张哈希表来存,每条转发连接在表里占一个条目。这个表的容量决定了能同时维持多少活跃连接,可以通过ip_vs_conn_tab_bits这个模块参数调整。大部分中小规模场景默认值够用,但如果做的业务长连接特别多(比如推送服务、数据库代理),或者并发连接已经到十万量级,就值得关注一下这个参数。修改方法是在/etc/modprobe.d/下建一个配置文件写入options ip_vs conn_tab_bits=20,然后重启模块或者重启机器生效。注意这个是 2 的指数,20 意味着约 100 万个桶位。
1.3 LVS、Nginx、HAProxy 不是替代关系而是分工关系
经常看到有人争论"负载均衡到底该用哪个",这个问题本身问得不对。它们工作在协议栈的不同层次,解决的是不同问题。LVS 工作在第四层,只看 IP 和端口,速度最快,但不能根据 URL 分流、不能做 TLS 卸载、不能改请求头;Nginx 和 HAProxy 工作在第七层,能看到完整的 HTTP 语义,可以做基于路径的路由、灰度、限流、鉴权,但转发成本明显更高。
实际生产里常见的是分层组合:最外层用 LVS 把流量摊到几台 Nginx 上,Nginx 再根据业务路径分发到各组后端。这样 LVS 承担的是抗量、抗并发、做入口高可用,Nginx 承担的是业务路由。我见过一些团队直接省掉 LVS、只用 Nginx 做入口,在流量规模不大的时候完全没问题,但量级上去之后要么需要给 Nginx 换更高配的机器,要么堆更多层,反而不划算。
| 维度 | LVS | Nginx | HAProxy |
|---|---|---|---|
| 工作层次 | 四层(TCP/UDP) | 七层为主,也支持四层 | 七层为主,也支持四层 |
| 转发路径 | 内核态完成 | 用户态代理 | 用户态代理 |
| 单机并发能力 | 极高,数十万级 | 万级到数万级 | 数万级 |
| 按 URL/Host 路由 | 不支持 | 支持 | 支持 |
| 端口映射 | 支持(NAT 模式) | 支持 | 支持 |
| TLS 卸载 | 不支持 | 支持 | 支持 |
| 健康检查粒度 | 较粗,端口/连接级 | 细,可自定义 | 细,可自定义 |
这张表不是要分高下,而是帮你判断当前场景该把哪一层放在前面。如果需求里出现了"按域名分流""按路径灰度""做 HTTPS 卸载",那入口一定要有七层组件;如果需求是"一个端口扛住巨大流量、后端只是无差别的同构服务",那四层在前面会更省资源。
2. 三种工作模式的取舍逻辑与 NAT 的适用边界
2.1 NAT 模式的完整数据包走向
LVS 有三种经典的工作模式:NAT、DR(直接路由)和 TUN(IP 隧道)。三种模式的差别全部体现在"请求包怎么进去、响应包怎么出来"这两件事上。NAT 模式是最容易理解、对网络环境要求最低的一种,也是新手入门首选。
完整走一遍:客户端(假设地址 10.0.0.5)向 VIP(假设 10.0.0.100:80)发起 TCP 连接。请求包到达 Director,Director 根据调度算法挑一台 RS,比如 192.168.10.11,把目的 IP 从 10.0.0.100 改成 192.168.10.11,目的端口保持 80,然后从内网网卡发给 RS。RS 收到包,源地址还是 10.0.0.5,目的地址是自己,于是正常处理并返回响应,响应包的目的地址是 10.0.0.5,源地址是 192.168.10.11。这个包要回到客户端,就必须经过 RS 的默认网关,而 RS 的网关被配置成了 Director 的内网地址 192.168.10.1,于是响应包回到 Director。Director 收到后把源地址从 192.168.10.11 改回 10.0.0.100,再发给客户端。
这条链路的关键在于:请求和响应都要穿过 Director,所以 Director 必须同时能到达客户端所在网段和后端网段,这是"双网卡"这个前提的由来;同时 RS 的默认网关必须指向 Director,否则响应包会走别的路径直接出去,客户端收到一个源地址是内网地址的包,TCP 三次握手就断了。这两条是 NAT 模式成立的硬性条件,缺一不可,后面部署环节所有的配置都是围绕这两条展开的。
2.2 DR 与 TUN 到底差在哪一句话
DR 模式的核心思路是"只改 MAC,不改 IP"。请求包进来后,Director 把目的 MAC 改成某台 RS 的 MAC 转发出去,IP 头里的目的地址仍然是 VIP。RS 为了能接收这个包,需要在自己的回环接口上额外配一个 VIP,同时抑制它对外发送 ARP 响应(否则整个网络里就会有多个设备声称自己拥有 VIP,形成地址冲突),相关的两个内核参数是arp_ignore和arp_announce。响应包由 RS 直接发给客户端,不再经过 Director,源地址天然就是 VIP,客户端不会有任何察觉。因为响应不走 Director,DR 模式能支撑的吞吐量远高于 NAT,但要求 Director 和所有 RS 处在同一个二层网络里。
TUN 模式在 IP 层做封装,把原始请求包整个塞进一个新的 IP 包里发给 RS,RS 拆封后处理,响应直接回客户端。它和 DR 一样响应不经过 Director,但可以跨网段部署,代价是需要内核支持 IPIP 隧道,且 RS 上同样要配 VIP 和抑制 ARP,配置复杂度更高。
| 对比项 | NAT | DR | TUN |
|---|---|---|---|
| 请求路径 | 经 Director 改目的地址 | 经 Director 改目的 MAC | 经 Director 封装后转发 |
| 响应路径 | 必须经 Director | 直接回客户端 | 直接回客户端 |
| Director 是否成为带宽瓶颈 | 是 | 否 | 否 |
| 网络位置要求 | 无特殊要求,可跨网段 | 必须同一二层网络 | 可跨网段 |
| RS 是否需要配 VIP | 否 | 是 | 是 |
| 是否支持端口映射 | 支持 | 不支持 | 不支持 |
| 部署复杂度 | 低 | 中 | 高 |
2.3 NAT 模式的真实短板与它仍然值得部署的理由
看到表格里"Director 成为带宽瓶颈"这一条,不少人会直接放弃 NAT。但实际情况要分场景看。NAT 模式的瓶颈主要在两个地方:一是进出流量都要过 Director,网卡带宽被双向占用;二是连接跟踪和地址改写本身消耗 CPU。对多数内部系统、中小型对外服务、以及镜像站、包仓库这类读多写少但单请求数据量不大的场景,一台普通服务器的网卡完全撑得住,用 NAT 换来的部署简单性远比那点带宽损失划算。
更关键的一点是端口映射。NAT 模式允许 VIP 的端口和 RS 的端口不一样,比如对外暴露 80,后端实际监听 8080,只要在添加规则时写清楚就行。DR 和 TUN 做不到这一点,因为它们不改端口,RS 必须监听和 VIP 完全一致的端口。对于容器化环境或者历史遗留系统端口不统一的情况,这个能力非常实用,也是我在不少迁移项目里优先选 NAT 的原因。
还有一个容易被忽略的点:NAT 模式下 RS 完全不用感知自己处在负载均衡后面,不需要配 VIP、不需要动内核参数、不需要处理 ARP 抑制,就当作一台监听端口的普通服务器用。这意味着你可以随时把它从 LVS 后面摘下来直连调试,排查问题时少了一层干扰。对于刚开始搭建集群、还在反复试错的阶段,这个特性省心程度很高。
3. 部署前的拓扑设计与网络规划
3.1 双网卡 Director 的地址规划
先把拓扑在纸上画清楚,比直接上手敲命令效率高得多。以虚拟机实验环境为例,规划如下:Director 两块网卡,外网侧eth0配 10.0.0.100/24(这就是 VIP,同时也是客户端能访问到的地址),内网侧eth1配 192.168.10.1/24;两台 RS 分别配 192.168.10.11 和 192.168.10.12,掩码 24,网关全部指向 192.168.10.1;客户端机器配 10.0.0.5/24,网关指向 10.0.0.1(实验环境里可以就是物理路由,也可以让 Director 兼任)。
这里有一个细节容易被忽略:Director 上不需要单独再配一个"管理 IP",VIP 直接绑在网卡上就行。有些资料会建议用ifconfig eth0:0或ip addr add加一个别名地址来承载 VIP,这种做法在需要做 VIP 漂移(配合 keepalived)的场景下是必要的,但单纯跑 NAT 模式时没必要多此一举,直接让网卡主地址就是 VIP 更清爽。等到后面要引入高可用时再拆开也不迟。
另外要确认路由表是正确的。在 Director 上执行ip route,应该看到默认路由指向外网网关,192.168.10.0/24 这条直连路由指向eth1。如果两条网卡的默认路由冲突(比如eth1也被配了默认网关),就会出现回包方向错误,表现为"能连上但很快断开"这种诡异现象。部署前花两分钟检查路由,能省掉后面半小时的排查。
3.2 真实服务器的网关指向是硬约束
RS 的网关必须指向 Director 内网地址,这条前面强调过,这里说清楚为什么以及怎么验证。TCP 连接是双向的,客户端发出去的 SYN 到达 RS 后,RS 回的 SYN-ACK 包源地址是 192.168.10.11。如果这个包走了别的出口,客户端会收到一个源地址与自己请求的目的地址(10.0.0.100)不一致的包,绝大多数操作系统都会直接丢弃,连接建立失败。只有在 Director 上把源地址改写回 VIP,客户端才会认。
验证方法很直接,在 RS 上执行ip route get 10.0.0.5,输出里应该显示via 192.168.10.1。如果显示的是别的地址或者直接 unreachable,说明网关配错了。另一种常见的配置事故是:RS 上为了让内网通信正常,手工加了一条明细路由走别的网关,结果这条路由的优先级高于默认路由,导致回包跑偏。遇到"部分客户端能连、部分不能"这种不规律现象时,一定要在 RS 上把路由表完整看一遍,而不是只盯着 LVS 配置。
还有一点值得提醒:如果实验环境里 Director 和 RS 都在同一台宿主机上的虚拟机里,注意虚拟网络编辑器的网关设置是否和你在系统内配的网关冲突,这类环境问题会伪装成 LVS 配置错误,排查时容易被带偏。
3.3 转发开关与必要内核参数的最小集合
Director 本质上是在做路由转发,所以必须打开 IP 转发。这是最容易漏的一步,漏了之后的症状是:ipvsadm -Ln显示规则一切正常,连接表里也有条目,但客户端就是连不上,后端也收不到包。开启方法有两种,临时生效用sysctl -w net.ipv4.ip_forward=1,持久化写进/etc/sysctl.d/下的配置文件里,然后sysctl -p加载。
除了转发开关,还有几个参数和 NAT 模式的稳定性相关。net.ipv4.vs.conntrack控制是否让 ip_vs 的连接跟踪与 netfilter 的 conntrack 联动,这个参数在有些内核版本上默认是 0;如果同时用了 iptables 做端口过滤,可能需要打开它才能正常工作,但打开之后性能开销会上升,纯粹做 LVS 转发时没必要开。net.ipv4.vs.conn_reuse_mode涉及端口复用行为,在某些高并发短连接场景下会影响新建连接的成功率,遇到"大量 TIME_WAIT 或连接被拒"时值得关注一下。
还有一组参数是net.ipv4.conf.all.rp_filter和对应网卡的rp_filter。这是反向路径过滤,用来防源地址欺骗,但在双网卡的不对称路由场景下可能把合法的回包丢掉。如果部署后发现偶发丢包、抓包能看到包进来了但系统不处理,可以尝试把相关网卡的rp_filter设为 0 或 2 验证一下。注意这是排查手段,不是无脑关掉就好,确认原因之后再决定长期配置。
4. ipvsadm 命令逐条拆解与配置落地
4.1 建立虚拟服务规则
安装ipvsadm之后,第一条命令是创建虚拟服务。格式如下:
ipvsadm -A -t 10.0.0.100:80 -s wlc这条命令的意思是:新增(-A)一个 TCP 服务(-t,如果是 UDP 就用-u),监听 10.0.0.100 的 80 端口,调度算法用加权最少连接(-s wlc)。执行完之后用ipvsadm -Ln应该能看到这条规则,只是下面还没有 RS,状态是空的。
这里要解释一个新手常见的困惑:-t后面跟的地址必须是本机上的地址。如果写了一个不属于本机任何网卡的 IP,ipvsadm可能会报错或者规则建了但不生效。因为在 NAT 模式下,Director 需要在自己的地址上"接住"这个请求,才能进一步转发。所以 VIP 一定得真实绑定在网卡上,这一点和 DR 模式不一样(DR 模式下 VIP 也可以在别处)。
调度算法这里选wlc是默认值,适合后端机器性能不均的情况。如果后端配置完全一致、请求处理时间也比较均匀,rr(轮询)其实更简单直观;如果请求处理时间差异极大(比如有的请求几十毫秒、有的要几秒),wlc的表现会更好,因为它会优先把新连接分配给当前活跃连接数少的机器。
4.2 调度算法的选择依据
调度算法不是随便挑一个就行的,选错了会出现"某台机器压力明显偏高"或者"长连接把一台打满"的情况。常用的几种可以对照下面这张表理解。
| 算法 | 简写 | 适用场景 | 不适用场景 |
|---|---|---|---|
| 轮询 | rr | 后端配置一致、请求耗时均匀 | 后端性能差异大 |
| 加权轮询 | wrr | 后端配置有差异,可按比例分配 | 请求耗时差异极大 |
| 最少连接 | lc | 请求处理时间差异较大 | 后端性能不一致 |
| 加权最少连接 | wlc | 综合以上,默认首选 | 无 |
| 源地址哈希 | sh | 需要同一客户端固定落到同一后端 | 客户端 IP 分布不均或经过 NAT |
| 目标地址哈希 | dh | 缓存类场景,同一资源命中同一后端 | 请求无资源维度特征 |
sh这个算法需要单独提醒一句。它按源 IP 做哈希计算,能保证同一个客户端始终落到同一台 RS,适合有会话状态但又不方便做集中式会话存储的场景。但它的前提是客户端的源 IP 能被正确识别。如果客户端和 LVS 之间还有一层 NAT 设备,那么所有客户端在 LVS 看来都是同一个源 IP,哈希结果全部一样,流量会全部压到一台机器上。这种事故我见过不止一次,现场表现就是"另外几台后端 CPU 一直是 0",排查方向很容易跑偏到算法模块没加载上去。
4.3 挂载后端节点与权重设置
规则建好之后,把后端挂上去:
ipvsadm -a -t 10.0.0.100:80 -r 192.168.10.11:80 -m -w 3 ipvsadm -a -t 10.0.0.100:80 -r 192.168.10.12:80 -m -w 1-a表示新增后端,-r指定后端地址和端口,-m表示使用 NAT(masquerading)模式,-w是权重。这两条命令把 11 这台机器的权重设成 3、12 设成 1,在wlc或wrr算法下大致就是 3:1 的流量分配比例。
-m这个参数是整个 NAT 模式的核心开关,如果写成了-g(DR 模式)或者-i(TUN 模式),那么前面所有的网络规划都要跟着变。初次部署时最容易犯的错误就是照着某个教程抄了-g,然后配的是 NAT 的拓扑,结果就是规则显示正常但流量完全不通。检查方法很简单,ipvsadm -Ln输出里每台 RS 后面会带一个字母标识,NAT 模式会显示Masq,看到这个字样就说明模式对了。
权重的含义要说清楚:它不是"流量百分比",而是一个相对值。权重 3 和 1 在加权轮询里就是每 4 个新连接里 3 个给前者。调整权重用ipvsadm -e命令修改即可,不需要先删除再加回来,改动是立即生效的,正在进行的连接不受影响。这个特性在做灰度发布或者临时摘掉某台机器时非常有用——把权重设为 0 就等于不再分配新连接,但已有连接还能自然结束,比直接删除后端温和得多。
4.4 持久连接与配置保存
持久连接用-p参数开启,单位是秒:
ipvsadm -A -t 10.0.0.100:80 -s wlc -p 300开启之后,同一个客户端在 300 秒内的请求会被强制分配到同一台 RS。这个功能在需要会话保持、又不想用源地址哈希的时候很有价值。不过它有个副作用需要知道:持久连接会压缩调度算法的效果,如果某个客户端的请求量特别大,它会在 300 秒内把压力集中在一台机器上。所以超时时间不宜设得太长,按业务实际会话时长来定比较合理。
配置保存是另一件必须做的事。ipvsadm的规则存在内核里,重启就没了。不同发行版的持久化方式不一样,一类是通过ipvsadm-save把规则导出到文件,再配合ipvsadm-restore或者对应的 systemd 服务开机加载;另一类是把命令直接写进开机脚本或 systemd unit 里。我倾向的做法是写一个简单的启动脚本,把ip_forward的开启和ipvsadm的规则添加都放在里面,这样配置的完整逻辑一目了然,换机器时直接复制脚本就行,比分散在好几个配置文件里更好维护。
#!/bin/bash sysctl -w net.ipv4.ip_forward=1 ipvsadm -C ipvsadm -A -t 10.0.0.100:80 -s wlc -p 300 ipvsadm -a -t 10.0.0.100:80 -r 192.168.10.11:80 -m -w 3 ipvsadm -a -t 10.0.0.100:80 -r 192.168.10.12:80 -m -w 1脚本开头的ipvsadm -C是清空所有规则,保证每次执行的结果是幂等的,不会因为重复执行而叠加出错误的规则。这一点在调试阶段尤其重要,改配置之前先清空再加,能避免很多"规则看起来对不对不上"的困惑。
5. 怎么确认链路真的通了:从连接表到抓包
5.1 ipvsadm -Lcn 的连接表怎么读
ipvsadm -Ln看的是规则,ipvsadm -Lcn看的是实时连接,这是两个完全不同的视图。排查问题时后者往往更有用,因为它能告诉你请求到底有没有进来、被分配到了哪台后端、当前处于什么状态。
输出大致是这样几列:Pro(协议)、Expire(剩余超时)、State(状态)、Source(客户端地址)、Virtual(VIP 和端口)、Destination(选中的后端)。状态列里ESTABLISHED表示连接已建立且还在活跃,SYN_RECV表示只收到了请求方向的 SYN、还没看到后端响应,TIME_WAIT是正常关闭后的残留。
这个状态信息非常关键。如果客户端报连接超时,而连接表里看到大量卡在SYN_RECV的条目,基本可以断定是请求能到后端但响应回不来——大概率就是 RS 的网关配错了,或者后端服务压根没在监听。如果连接表里根本没有条目,说明请求根本没到 Director,问题在更前面,可能是 VIP 没绑好、防火墙拦了、或者客户端路由不对。这两种情况排查方向完全不同,先看一眼连接表能省下大量时间。
5.2 抓包确认地址改写的落点
配置文件看不出来的问题,抓包基本都能看出来。在 Director 的内网网卡上抓:
tcpdump -i eth1 -nn host 192.168.10.11 and port 80正常情况应该看到源地址是客户端、目的地址是 192.168.10.11 的包,说明 DNAT 生效了。同时可以在外网网卡上抓:
tcpdump -i eth0 -nn host 10.0.0.5 and port 80这里应该看到目的地址是 10.0.0.100 的请求包,以及源地址是 10.0.0.100 的响应包。如果响应包的源地址显示的是 192.168.10.11 而不是 VIP,说明 SNAT 没生效,客户端那边一定会丢弃。这个对比是最直接的验证方式,比翻日志快得多。
在 RS 上抓包同样有价值。正常应该看到目的地址是自己的包,源地址是客户端地址(而不是 Director 的地址,因为 NAT 模式只改了目的地址,请求方向的源地址保持不变)。如果看到的源地址是 192.168.10.1,说明配置的可能是别的模式或者有额外的 SNAT 规则干扰,值得回头核对一下。
5.3 用压测验证权重是否真的生效
规则配完、单次访问通了,还不算完。至少要验证两件事:权重分配是否符合预期,以及后端有一台挂掉时的表现。用ab或wrk这类工具跑一轮并发请求,然后看两台 RS 的访问日志条数比例,权重 3:1 的情况下应该接近这个比例。如果比例明显不对,先确认调度算法是不是wlc(有时候会误用lc,它不识别权重),再看是不是开了持久连接把分配固定住了。
后端故障的表现更值得关注。直接在某台 RS 上停掉服务,然后继续压测,观察请求是否会持续失败。默认情况下 LVS 不做主动健康检查,它只根据连接建立的成功与否来更新后端状态,所以在服务刚停的头几秒,仍然可能有请求被分配过去然后失败。如果业务对这块敏感,要么在 LVS 前面配合 keepalived 做健康检查,要么接受短暂失败由客户端重试兜底。这一点必须提前和业务方对齐预期,不然线上出现偶发 502 会很难解释。
6. NAT 模式部署里最容易翻车的几个地方
6.1 RS 网关漏配导致的"半通"现象
前面反复提过网关这件事,这里完整还原一次排查链路,因为这个坑太典型了。现象是:用浏览器访问 VIP,页面一直转圈最后超时;ping VIP是通的;在 Director 上curl后端的内网地址也是通的,看起来一切正常。
第一步,在 Director 上执行ipvsadm -Lcn,发现有大量SYN_RECV状态的条目,说明请求已经进来了、也分配出去了,但没等到后端的响应。第二步,在 RS 上抓包,能看到请求包正常到达,也能看到 RS 发出的响应包,但响应包的目的地址是客户端,且从内网网卡直接发出去,没有经过 Director。第三步,在 RS 上执行ip route get <客户端IP>,果然显示via 192.168.1.254,也就是配了一个错误的网关。
根因就是 RS 的默认网关没指向 Director。修复很简单,改网关重启网络即可。但这里有个经验:改完之后如果还是不通,检查一下 ARP 缓存或者让 Director 那边的连接表自然超时后再试,因为旧的连接条目还在,可能继续命中错误的转发路径。执行ipvsadm -C清一次再试,能排除这种残留干扰。
6.2 端口不通和连接被拒的几种可能
同一类表象背后可能是完全不同的原因,需要按链路顺序排除。客户端报 "Connection refused",说明包到了某处并收到了 RST。可能性包括:VIP 没绑在网卡上(ip addr确认)、LVS 规则没建或者被清空了(ipvsadm -Ln确认)、后端端口没监听(在 RS 上ss -lntp确认)、以及后端所在机器的防火墙拦了请求。
如果报 "Connection timed out",那就是包丢了,方向性更强:要么请求没到 Director,要么请求到了但响应回不来。前者查客户端的路由和中间的网络策略,后者查 RS 的网关、Director 的转发开关和rp_filter设置。
还有一种比较隐蔽的情况是"时通时不通"。这通常和rp_filter、多网卡路由选择、或者后端机器的负载过高有关。排查时可以连续发几百个请求统计失败率,然后在失败的那一刻同步抓包,定位是丢在哪个环节。这种方式比反复改配置盲猜有效得多。
6.3 带宽瓶颈的位置判断与规避思路
NAT 模式下所有流量双向经过 Director,所以当业务流量涨起来之后,最先出问题的往往不是 CPU 而是网卡带宽。判断方法是在 Director 上持续观察网卡流量,如果接近链路带宽上限,那再优化调度算法也没意义了,得换方案。
规避思路有几种。一种是把 NAT 换成 DR 模式,让响应绕开 Director,这要求网络拓扑支持同二层。另一种是在 NAT 前面再加一层,用多台 Director 分担不同业务端口,本质上是把瓶颈拆散。还有一种更彻底的做法是在架构设计阶段就区分开——把大文件下载、视频流这类带宽消耗型流量从 LVS 后端池里剥离出去,走独立的出口,让 LVS 专注处理小而多的 API 请求。这个思路在实际项目里效果最好,因为 NAT 对短小请求的处理效率确实很高,而大流量场景本来就不太适合让它扛。
6.4 arp_ignore 和 arp_announce 什么时候才需要动
这两个参数在 DR 模式的教程里几乎是必配项,导致很多人学 NAT 的时候也顺手抄了过来。实际上 NAT 模式完全不需要动这两个参数,因为 RS 上没有绑定 VIP,不存在 VIP 地址冲突的问题。乱改这两个参数反而可能影响正常的网络行为,尤其是arp_announce设成 2 的时候会限制本机对外通告的地址范围,在有多个网卡或额外地址的机器上可能引发意外。
判断标准很简单:如果你在 RS 的回环接口或者别的网卡上配了和 VIP 相同的地址,那就必须处理 ARP 抑制;如果没配,就不用管。我在实际部署中养成的习惯是,配置之前先问一句"这个参数是为哪个模式服务的",把参数和模式的对应关系理清楚,比盲目照抄教程稳得多。
最后分享一个我个人觉得挺有用的小技巧:整套配置在虚拟机里跑通之后,不要急着把命令一条条记到文档里,而是把整个流程写成一个可以重复执行的脚本,脚本里包含清空规则、设置转发、建规则、挂后端、打印结果这几步。每次换环境或者调整参数,重新跑一遍脚本就行,出错概率会低很多。同时脚本本身就是一份不会过时的配置文档,比写一堆说明文字实用。
另外一个体会是关于学习路径的。LVS 这个东西命令不多,难点全在“包是怎么走的”这个认知上。我建议的做法是先在单机或者两台虚拟机里把 NAT 模式跑通,然后用抓包工具在三个位置(客户端侧、Director 内外网卡、RS)各抓一次,把同一个连接的完整旅程看一遍。这个过程走下来之后,DR 模式和 TUN 模式基本上一看就懂,因为它们只是在这条路径上改了一两个环节而已。跳过这一步直接上 DR,往往会被 ARP 抑制和 VIP 配置搞得一头雾水,反而更慢。