ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

iptables端口转发与负载均衡实战:DNAT/SNAT及statistic模块详解

iptables端口转发与负载均衡实战:DNAT/SNAT及statistic模块详解 1. 案例引入为什么端口转发和负载均衡要放在一起讲iptables 在 Linux 服务器上的地位说白了就是一道保安闸口。日常工作中大家用得最多的是封 IP、限端口、挡扫描也就是所谓的安全策略。但 iptables 的能力远不止“拦”这一项它还能做流量调度把进到本机的数据包按规则扔给别的机器去处理。端口转发和负载均衡就属于这种“放”和“导”的操作。我接手过不少业务场景最典型的一个后端用三台 Web 服务器跑同样的服务前端一台 Linux 网关做统一入口。外部用户只知道网关的 IP所有请求先落到这台网关再由网关把流量分发给后端三台机器。这里既要实现端口转发也要让每次请求能比较均匀地分摊到不同后端避免某一台被压垮。这就是标题里“端口转发 负载均衡”同时出现的意义。本案例面向的是有一定 iptables 基础、想深入理解 NAT 和流量调度的朋友。看完之后你不仅能手动敲出端口转发规则还能用 statistic 模块做简单的轮询分发搞清楚 DNAT、SNAT、PREROUTING、POSTROUTING 各自的职责边界。对于刚接触防火墙的人来说这篇文章也会把一些容易踩坑的概念一次性讲透。2. 动手之前先把 NAT 流程和链的关系理清楚2.1 数据包进入 Linux 主机的完整路径iptables 规则不是随便往哪个链里一塞就完事的写规则之前得先在脑子里把数据包的行走路线画出来。一个从外部网络进入网关的 TCP 包会依次经历这几个关键检查点网卡接收数据包后进入 PREROUTING 链。此时包的目标地址还是原始的公网地址或虚拟 IP。如果 PREROUTING 里有 DNAT 规则包的 dest IP 会被改写变成内网后端服务器的地址。路由决策发生在这个改写之后。内核会根据新的目标地址决定把包从哪张网卡送出去。如果是发往本机某个端口的会经过 INPUT 链进入本地进程如果是要转发的则经过 FORWARD 链。包离开本机之前还会经过 POSTROUTING 链。在这里做 SNAT源地址转换把包的源 IP 从网关内网口地址改成网关自身这样后端服务器回包时才知道往哪里回。这个流程是理解所有端口转发规则的基础。很多人在本机写了 DNAT 规则却不生效就是因为漏了 FORWARD 链放行或者忘了做 SNAT。2.2 DNAT 和 SNAT 的分工很多人搞反了DNAT 全称 Destination Network Address Translation作用是把目标地址改写。比如外部访问 203.0.113.10:8080DNAT 把它改成 192.168.1.10:80。改写动作发生在 PREROUTING 链因为这个阶段还没做路由决策改完目标之后内核才能正确选择合适的出口。SNAT 全称 Source Network Address Translation作用是把来源地址改写。在多机转发的场景下后端服务器的回包目标是 203.0.113.10 这个外网 IP但它拿不到路由所以必须在网关发出数据包时把源地址改成网关自己的内网 IP。这样后端回包会先回到网关网关再根据之前的连接跟踪记录把回包转给外网客户端。一句话记忆进包改目标出包改来源。前者帮你知道“要去哪”后者帮对方知道“从哪来”。2.3 为什么开启转发还需要额外放行Linux 内核出于安全考虑默认是关闭 IP 转发的。如果 net.ipv4.ip_forward 不是 1就算你写了一大堆 DNAT、SNAT 规则数据包到了主机后也会被直接丢弃根本不会走 FORWARD 链。有的朋友已经开启了 ip_forward但规则还是不生效这时候要检查 FORWARD 链的默认策略。如果默认策略是 DROP你需要显式放行从内网口到外网口的数据流。常见的情形是外部客户端 —— 网关 eth0 —— 内网后端服务器。这种情况下FORWARD 链至少要有两条规则配合一条放行外部进来的方向一条放行后端回包的方向或者直接用状态匹配放行已建立的连接。提示检查 ip_forward 是否开启可以执行sysctl net.ipv4.ip_forward。如果输出的是 0运行sysctl -w net.ipv4.ip_forward1临时开启。如果要永久生效在/etc/sysctl.conf里加上net.ipv4.ip_forward 1。3. 端口转发的标准落地步骤场景与命令逐条解析3.1 一个最简单的转发场景外部端口映射到内网 Web 服务先从一个最常见的需求说起服务器只有一个公网 IPv4 地址但你内网有两台服务一个跑在 80 端口一个跑在 8080 端口你需要让外部用户通过不同端口访问不同内网服务。假设网关公网网卡是 eth0IP 为 203.0.113.10内网网卡是 eth1IP 为 192.168.1.254。内网有一台 Web 服务器 192.168.1.100监听 80 端口。你的目标是让外部用户访问 203.0.113.10:80 时实际访问到 192.168.1.100:80。需要配置的规则其实只有三条核心步骤# 1. 开启内核转发 sysctl -w net.ipv4.ip_forward1 # 2. PREROUTING 链上做 DNAT改写目标地址 iptables -t nat -A PREROUTING -d 203.0.113.10 -p tcp --dport 80 -j DNAT --to-destination 192.168.1.100:80 # 3. POSTROUTING 链上做 SNAT改写源地址 iptables -t nat -A POSTROUTING -d 192.168.1.100 -p tcp --dport 80 -j SNAT --to-source 192.168.1.254这里先解释一下第三条规则为什么要限制目标地址和端口。如果不加-d 192.168.1.100 -p tcp --dport 80说明所有从本机出去的数据包只要源地址是内网网段的都会把源地址改成 192.168.1.254。这在本例中问题不大因为网关一般只服务这些内网主机。但更严谨的写法是只在去往特定后端服务器的流量上做 SNAT这样能避免影响其他正常的 NAT 流量。很多教程到这里就结束了但实战中还有个容易漏掉的关键点FORWARD 链放行。如果 FORWARD 默认策略是 ACCEPT那么上述三条规则就够了。但如果你的 FORWARD 链是被收紧过的还需要增加放行规则。# 放行外部访问内网 Web 的数据包 iptables -A FORWARD -i eth0 -o eth1 -d 192.168.1.100 -p tcp --dport 80 -j ACCEPT # 放行内网 Web 回包数据包 iptables -A FORWARD -i eth1 -o eth0 -s 192.168.1.100 -p tcp --sport 80 -m state --state ESTABLISHED,RELATED -j ACCEPT有些环境里回包方向的状态匹配并不好写因为涉及连接跟踪状态下 SNAT 的处理。更稳妥的写法是把回包方向也做成无条件放行只限制源地址为内网 Web 服务器、目标地址为任意地址的数据包iptables -A FORWARD -i eth1 -o eth0 -s 192.168.1.100 -p tcp --sport 80 -j ACCEPT实测下来对于安全要求不高的内网环境后一种写法故障率低排查起来也简单。3.2 多端口批量转发用 REDIRECT 还是多写几条 DNAT如果内网有多个服务都需要映射出来比如 80、443、3306 都要对外开放有些人的第一反应是复制好几条 DNAT 规则。如果你的公网 IP 和端口分配明确这样做是没问题的。但如果服务数量多写起来烦维护也累。还有一种场景是透明代理式的端口跳转。比如网关本机开了某个代理端口想强制把所有 HTTP 流量导入到代理里这就是 REDIRECT 的应用场景。REDIRECT 可以理解为 DNAT 的一个特例目标地址固定为本机地址不需要指定外部目标 IP内核会自动处理。# 把所有进入 eth0 的 TCP 80 端口的流量重定向到本机 8080 端口 iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 8080REDIRECT 适合的场景是“端口替换”常用于透明代理、透明网关、调试环境。而 DNAT 适合的场景是“跨主机转发”目标机器是另一台服务器。两者不要混用。比如你把 DNAT 目标写成 127.0.0.1虽然内核也能处理但这等于绕了一大圈直接用 REDIRECT 反而更干净。3.3 用 conntrack 状态匹配提升转发效率iptables 的连接跟踪机制会在内核里记录每个连接的状态最常用的四个状态是 NEW、ESTABLISHED、RELATED、INVALID。在 FORWARD 链中合理的利用状态匹配可以减少规则数量也能避免不必要的二次判断。比如你放行了外部到内网 Web 的访问后续回包方向如果还要做一堆严格限制不仅啰嗦而且容易出错。更聪明的做法是放行所有目标为本机的外部访问连接然后在反向方向放行 ESTABLISHED 状态。# 放行新建连接外部到内网 Web iptables -A FORWARD -i eth0 -o eth1 -d 192.168.1.100 -p tcp --dport 80 -m state --state NEW -j ACCEPT # 放行所有已建立连接的双向流量 iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT这里有个细节需要留意SNAT/DNAT 对连接跟踪是有额外影响的。因为 NAT 会改写数据包内容conntrack 条目里不仅记录五元组还会记录 NAT 转换前后各是什么。用conntrack -L命令可以看到src203.0.113.10 dst192.168.1.100 sportxxxx dport80这样的转换信息。当你不确定某条连接是否被正确转换时用这个命令排查非常直观。4. 基于 iptables 的负载均衡statistic 模块的使用思路4.1 为什么选 iptables 做负载均衡而不是 Nginx、LVS看到“负载均衡”四个字很多人的第一反应是 Nginx、HAProxy、LVS。确实这些专业组件在应用层和四层负载均衡上有很强的优势支持健康检查、会话保持、动态权重调整。但你也要明白它们都要求额外安装服务并且有一定的学习成本。在某些最小化部署的环境里你不能或者不想装额外的软件包。比如一个只跑基础系统的网关正好承担了防火墙职责再用 iptables 的 statistic 模块做数据包层级的负载均衡是最轻量、侵入性最小的方案。它不需要用户态进程参与完全在内核里完成流量分发性能开销极低。不过iptables 做负载均衡也有明显短板它不会检查后端服务的健康状态。如果某一台后端服务挂了流量还是会被硬性分发过去客户端就会看到连接超时或拒绝。所以这个方案适合“后端服务本身有限制重试机制”的场景或者搭配外部的健康检查脚本定时清理不可用节点。4.2 statistic 模块的三种分发策略iptables 的 statistic 模块提供了三种基本策略nth、random、every。nth 策略可以理解为轮询模式。你可以控制每 N 个包中取第几个包做某种动作。例如# 每 3 个包取 1 个。 iptables -t nat -A PREROUTING -d 203.0.113.10 -p tcp --dport 80 -m statistic --mode nth --every 3 --packet 0 -j DNAT --to-destination 192.168.1.100:80这里的--packet 0表示从第 0 个包开始命中。如果你有三台后端通常写法是第 0 个包给后端 A第 1 个包给后端 B第 2 个包给后端 C。random 策略按概率分发适合后端机器性能不一致、想按权重分配流量的场景。例如一台机器性能好承担 50% 流量另一台承担 30%第三台承担 20%可以这样写先按 50% 概率转发到后端 A剩下的 50% 里再按 60% 概率转发到后端 B最后剩下的全给后端 C。every 策略也常被理解为均匀分发但它的计数粒度是按“匹配机会”来的和 nth 有些微差别。在实际使用中nth 更容易理解成轮询every 更适合做周期性的抽取。4.3 nth 轮询实现三台后端机器分发假设场景如下外部访问网关的 80 端口网关将流量尽量均匀地分发给三台后端 Web 服务器分别是 192.168.1.101、192.168.1.102、192.168.1.103。三台后端都监听 80 端口。规则如下iptables -t nat -A PREROUTING -d 203.0.113.10 -p tcp --dport 80 -m statistic --mode nth --every 3 --packet 0 -j DNAT --to-destination 192.168.1.101:80 iptables -t nat -A PREROUTING -d 203.0.113.10 -p tcp --dport 80 -m statistic --mode nth --every 3 --packet 1 -j DNAT --to-destination 192.168.1.102:80 iptables -t nat -A PREROUTING -d 203.0.113.10 -p tcp --dport 80 -m statistic --mode nth --every 3 --packet 2 -j DNAT --to-destination 192.168.1.103:80注意这三条规则之间是有顺序依赖的。第一条规则命中第 0 个包第二条规则命中第 1 个包第三条规则命中第 2 个包。因为 iptables 规则是按顺序匹配的如果把第二条放到第一条前面实际效果就会错乱。你最好严格维持这个顺序或者干脆写到一个脚本里固化下来。另外一个容易忽略的点--every 3里的 3 代表计数周期而不是包的总数。只要计数器达到 3 就会重置所以三条规则必须使用相同的--every 3否则轮询会失去意义。4.4 random 分发实现权重比例以三台后端为例假设 192.168.1.101 性能最好给它 50% 流量192.168.1.102 次之给它 30%192.168.1.103 最弱给它 20%。# 50% 流量给 192.168.1.101 iptables -t nat -A PREROUTING -d 203.0.113.10 -p tcp --dport 80 -m statistic --mode random --probability 0.50 -j DNAT --to-destination 192.168.1.101:80 # 剩余流量中 60% 给 192.168.1.102即 30% 总流量 iptables -t nat -A PREROUTING -d 203.0.113.10 -p tcp --dport 80 -m statistic --mode random --probability 0.60 -j DNAT --to-destination 192.168.1.102:80 # 剩余流量给 192.168.1.103 iptables -t nat -A PREROUTING -d 203.0.113.10 -p tcp --dport 80 -j DNAT --to-destination 192.168.1.103:80这里的关键是概率的计算链第一条规则命中后后面规则不再处理第一条没命中继续执行第二条。所以第二条写的是在剩余流量中概率 60%换算成总流量就是 (1-0.5) * 0.6 0.3正好是 30%。第三条不设概率承接所有剩余流量。还有一种做法是给每条规则单独设置绝对概率但因为 iptables 是顺序匹配后一条规则只能在“前一条没命中”的前提下运行所以必须用条件概率去换算。我见过不少朋友在这里写错直接用 0.5、0.3、0.2 三个值结果第三条永远没有流量进入。这是 random 模式最常见的坑。5. 动手实操从环境准备到规则验证5.1 环境准备清单如果你要完整复现这个案例建议准备三台机器或者用虚拟化平台开三个虚拟机。网络拓扑如下网关机Linux两张网卡eth0 连接模拟外网IP 203.0.113.10eth1 连接内网IP 192.168.1.254。后端服务器 1192.168.1.101Web 服务监听 80 端口。后端服务器 2192.168.1.102Web 服务监听 80 端口。后端服务器 3192.168.1.103Web 服务监听 80 端口。在开始配置 iptables 之前先把后端服务器的 Web 服务跑起来。最简单的验证方式是用 Python 起一个临时 HTTP 服务# 在后端 1 上执行 python3 -m http.server 80 --bind 0.0.0.0为了让结果可区分你可以把每台后端服务的默认页面内容改成不同文字比如“Server 1 response”。这样通过网关访问时能看到每次返回的服务器标识方便验证负载均衡是否生效。5.2 一次性脚本完整规则下发与解释以下脚本适合直接部署在网关上。注意脚本内会先清理旧的 NAT 规则避免重复追加导致规则膨胀。#!/bin/bash # 网关内网 IP LAN_IP192.168.1.254 # 网关公网 IP WAN_IP203.0.113.10 # 后端服务器 BACKEND_1192.168.1.101 BACKEND_2192.168.1.102 BACKEND_3192.168.1.103 # 1. 开启 IP 转发 sysctl -w net.ipv4.ip_forward1 # 2. 清空 nat 表规则避免历史规则影响 iptables -t nat -F iptables -t nat -X # 3. 清空 filter 表 FORWARD 链规则谨慎如果你还有其他过滤规则不要直接 F建议逐条删除 # iptables -F FORWARD # 4. 设置 filter 表 FORWARD 链默认策略为 ACCEPT方便测试 iptables -P FORWARD ACCEPT # 5. 在 nat 表 PREROUTING 中配置 DNAT 负载均衡 iptables -t nat -A PREROUTING -d $WAN_IP -p tcp --dport 80 -m statistic --mode nth --every 3 --packet 0 -j DNAT --to-destination $BACKEND_1:80 iptables -t nat -A PREROUTING -d $WAN_IP -p tcp --dport 80 -m statistic --mode nth --every 3 --packet 1 -j DNAT --to-destination $BACKEND_2:80 iptables -t nat -A PREROUTING -d $WAN_IP -p tcp --dport 80 -m statistic --mode nth --every 3 --packet 2 -j DNAT --to-destination $BACKEND_3:80 # 6. 在 nat 表 POSTROUTING 中配置 SNAT使后端服务器能从网关回包 iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE这里用 MASQUERADE 而不是 SNAT是因为 MASQUERADE 能自动获取 eth0 的当前 IP适合动态 IP 的场景。如果你确定公网 IP 永远不会变用 SNAT 性能略好一点。两者本质区别在于 MASQUERADE 每次发送数据包时要查询出口网卡的 IP 地址开销略高一点点。5.3 从外网客户端验证轮询效果模拟外网客户端可以直接用网关的网络命名空间、虚拟机或者其他机器。验证步骤在外网客户端执行curl http://203.0.113.10连续多次。查看每次返回内容中的服务器标识。正常情况下三次请求会分别落到 Server 1、Server 2、Server 3。如果每次都落在同一台服务器优先检查 PREROUTING 链规则顺序是否正确。如果某个后端从来收不到流量可以检查 DNAT 规则里--packet值是否写错。还有一种情况curl 本身会复用连接如果服务端开启了 HTTP keep-alive连续多次 curl 时可能使用同一条 TCP 连接导致 nth 计数器只在连接建立初期生效。换句话说负载均衡是按连接粒度分流不是按 HTTP 请求粒度分流。如果你想要“每个请求都均匀分发”那就需要在应用层做调度Nginx 的 upstream 更适合这个需求。iptables 的 nth 模式只能保证 TCP 连接建立时尽量均匀地分散到不同后端。5.4 查看连接跟踪表确认 NAT 是否生效当规则配置完成且客户端发起访问后可以在网关上执行conntrack -L | grep 203.0.113.10如果系统没有安装 conntrack 命令可以用cat /proc/net/nf_conntrack查看原始内容或者先加载模块modprobe nf_conntrack modprobe nf_conntrack_ipv4输出里能看到一行类似这样的内容tcp 6 431999 ESTABLISHED src192.168.1.100 dst203.0.113.10 sport34567 dport80 src203.0.113.10 dst192.168.1.100 sport80 dport34567 [ASSURED] mark0 use1重点看后半段的 NAT 转换信息src203.0.113.10 dst192.168.1.100表示原始的数据包从外部客户端发往网关经过 DNAT 后目标是 192.168.1.100。这说明 PREROUTING 链的 DNAT 规则已经生效。如果只看到前半段而没有后半段说明 NAT 没有做成功大概率是因为 PREROUTING 链里没有匹配到对应规则。6. 踩坑记录端口转发和负载均衡最容易翻车的地方6.1 本机访问 DNAT 不生效的真正原因许多人在配置完 PREROUTING 链的 DNAT 后直接在网关本机上执行curl http://203.0.113.10:80来测试发现根本不通。这不是规则写错了而是因为本机访问本机 IP 时数据包并不走 PREROUTING 链。它走的是路由决策之后的 OUTPUT 链。Linux 里有一个专门用于本机发出流量改写的链叫 OUTPUT位于 nat 表。如果确实需要让网关本机也通过公网 IP 访问后端服务可以额外在 OUTPUT 链加一条类似的 DNAT 规则iptables -t nat -A OUTPUT -d 203.0.113.10 -p tcp --dport 80 -j DNAT --to-destination 192.168.1.101:80但这会破坏负载均衡的轮询逻辑因为只有一条规则流量会全部打到这一台后端。所以更推荐的做法是测试时从外部客户端发起请求而不是在网关上自测。6.2 连接跟踪模块未加载导致规则忽好忽坏iptables 的 NAT 功能依赖连接跟踪机制。如果内核没有加载nf_conntrack相关模块执行 DNAT 匹配时可能会报错或者规则虽然写进去了但流量不转换。可以用以下命令确认模块是否加载lsmod | grep nf_conntrack如果发现没有加载手动加载modprobe nf_conntrack modprobe nf_nat在 Ubuntu/Debian 上通常不需要手动干预因为 iptables 服务启动时会自动加载。但在精简内核或 Docker 容器场景下这个问题容易暴露。如果conntrack -L输出为空或命令不存在先排查这一步。6.3 回程路由不对称导致响应数据包丢失端口转发的本质是双向的外网发进来的包要正确改写目标后端返回的包也要正确改写源。有一个常见的工程场景是“多网关”环境即后端服务器的默认网关不是这台 Linux 网关。举个例子后端服务器 192.168.1.100 的默认网关被设为另一台路由器而不是 192.168.1.254。当网关把外部请求转发给 192.168.1.100 后后端处理完直接找自己的默认网关把响应发出去了。此时响应包会绕过 Linux 网关直接回到外部客户端但这个包的源 IP 是 192.168.1.100外部客户端根本不认识它连接直接失败。解决办法有二修改后端服务器默认路由让下一跳指向 Linux 网关。如果后端服务器不能改路由可以在后端加策略路由确保发往外部客户端网段的流量走 Linux 网关。这个问题的排查方式也简单在后端服务器上tcpdump -i eth0 port 80如果能看到请求进来但响应发出去的下一跳不对立刻就能发现抓包里的源 IP 和目标 IP 异常。6.4 某些协议如 FTP的 NAT 特殊处理常见的 HTTP、HTTPS、SSH 在 NAT 场景下都没有问题。但 FTP 比较特殊它使用两个连接一个控制连接21 端口一个数据连接通常是动态端口。在 DNAT 转发 FTP 时如果没有加载nf_conntrack_ftp模块数据连接将无法正确建立。如果你在内网跑 FTP 服务且需要通过网关转发建议提前加载modprobe nf_conntrack_ftp然后在规则放行时开放足够的数据端口范围或者在 FORWARD 链使用状态 RELATED 放行。这个坑我踩过不止一次如果不留意FTP 会表现得时好时坏登录正常但列目录或传文件卡死。6.5 规则的持久化重启后一切归零很多人手动敲完规则后觉得大功告成结果一重启服务器规则全部没了因为 iptables 规则默认是不持久化的。解决方式取决于你的发行版。在 CentOS/RHEL 系列上可以用iptables-save /etc/sysconfig/iptables保存系统启动时由 iptables 服务自动加载。在 Ubuntu/Debian 上可以安装iptables-persistent包它会读取/etc/iptables/rules.v4和/etc/iptables/rules.v6。如果你的环境是 Docker 或 Kubernetes 节点建议把规则写到系统启动脚本里或者用 systemd unit 文件统一管理。单纯依赖iptables-save在某些容器场景下会因为 Docker 自己维护链而出现冲突。7. 进阶技巧负载均衡 端口转发的组合玩法7.1 把本机多个端口分别转到不同后端集群有时候你只有一台公网服务器但内网有多个不同业务。比如 80 端口给 Web 集群443 端口给另一个 HTTPS 集群3306 端口给数据库集群。你可以在同一台网关的 PREROUTING 链上分别写多组 DNAT 规则互不干扰。iptables -t nat -A PREROUTING -d 203.0.113.10 -p tcp --dport 80 -m statistic --mode nth --every 3 --packet 0 -j DNAT --to-destination 192.168.1.11:80 iptables -t nat -A PREROUTING -d 203.0.113.10 -p tcp --dport 80 -m statistic --mode nth --every 3 --packet 1 -j DNAT --to-destination 192.168.1.12:80 iptables -t nat -A PREROUTING -d 203.0.113.10 -p tcp --dport 443 -m statistic --mode nth --every 2 --packet 0 -j DNAT --to-destination 192.168.1.21:443 iptables -t nat -A PREROUTING -d 203.0.113.10 -p tcp --dport 443 -m statistic --mode nth --every 2 --packet 1 -j DNAT --to-destination 192.168.1.22:443这里的关键是每一组规则使用自己独立的计数周期。因为 iptables 的 statistic 模块是分别在每条规则自己的计数器上工作的不会跨端口造成干扰。不过要注意规则顺序--dport 80的规则和--dport 443的规则不要在字段上写反否则计数会串线。7.2 源地址哈希让同一个客户端始终访问同一台后端nth 轮询的代价是同一个客户端的多个连接可能被分发到不同后端。如果后端之间有共享 session 的依赖比如用户登录状态存在单机内存里就会出问题。这时可以给同一来源 IP 做哈希或固定映射。iptables 本身没有内置源地址哈希的 NAT 模块但可以使用--match hashlimit做流量限制严格说它并不适合做源地址一致的负载均衡。更实用的做法是借助 Linux 策略路由或 ip rule 实现。不过这属于另一个话题了。用 iptables 实现最简单的会话保持可以尝试 random 模式并提高粒度但也不能完全保证。如果业务确实需要 sticky session建议考虑 LVS 的 SH 调度算法Source Hashing或者 Nginx upstream 的 ip_hash 参数。iptables 方案在这个场景下并不优雅。7.3 健康检查不存在的替代方案用 conntrack 快速隔离故障节点前面提到过iptables 自身的 load balancing 不感知后端健康状态。但我们可以利用 conntrack 数据做一定程度的故障隔离。思路是写一个定时检查脚本探测后端端口是否连通不通时动态删除或插入规则。#!/bin/bash # 简单健康检查每 10 秒检查一次后端 80 端口失败则临时移除 DNAT 规则 BACKEND(192.168.1.101 192.168.1.102 192.168.1.103) while true; do for ip in ${BACKEND[]}; do if ! timeout 2 nc -zv $ip 80 /dev/null 21; then echo $ip down fi done sleep 10 done实际生产环境里我见过不少运维直接用 keepalived LVS 替代这套轻量方案因为健康检查的要求越加越多脚本越来越复杂最终不如用成熟方案。如果你只是临时过渡或者后端就三五台机器也不追求自动故障转移那么 iptables 方案完全够用。注意动态删除规则时要尽量避免误删其他规则。建议为每条 DNAT 规则增加注释例如-m comment --comment web-backend-101。删除时先iptables -t nat -L PREROUTING -n --line-numbers找到规则编号再按编号删除这样最稳妥。8. 写在最后这套方案的边界与取舍iptables 端口转发和基于 statistic 模块的负载均衡很适合小规模、低成本、不想额外引入组件的场景。它最大的优势是零依赖、性能高、规则透明出了问题看一眼规则就能明白。但它也不是万能的没有健康检查没有精细的会话保持没有动态权重调整。做演示环境、做实验、做小型办公网络完全够用做大规模高并发生产系统我建议还是老老实实上 LVS 或 Nginx。我个人在实际操作中的体会是写 iptables 规则千万不能只动手不动脑每一行命令都要想清楚它挂在哪个链、作用在哪个方向、影响哪些流量。把这个案例完整做一遍你不仅会明白端口转发和负载均衡怎么配更重要的是能建立一套“数据包走到哪一步会被怎么处理”的心智模型。后续不管碰到 TUN 模式代理、Docker 端口映射还是 Kubernetes 的 Service 转发排查思路都大同小异。最后分享一个调试小技巧在所有 iptables 规则之前不要加-j DROP这种终止动作先用-j LOG记录到内核日志再用tail -f /var/log/kern.log观察匹配情况。等确认流量方向和规则逻辑都正确之后再把 LOG 规则移除换成 ACCEPT 或 DNAT。这样调试效率会高很多也能避免因为规则顺序问题把自己锁在外面。
返回列表