Skip to content

Keepalived + HAProxy + iptables 边缘节点转发实战

在边缘网关场景中,常需要一对转发节点对外提供统一入口:客户端访问一个 VIP,由 keepalived 保证 VIP 在两台节点间漂移实现高可用,HAProxy 负责四层/七层负载分发到后端服务,同时用 iptables DNAT 处理临时端口转发需求。本文基于生产环境真实配置整理,覆盖 keepalived VRRP 配置、HAProxy TCP 转发配置、iptables DNAT 批量管理脚本,以及验证、切换演练和典型故障排查。

示例环境规划(文中地址均为示例):

角色地址说明
转发节点 1(LB-1)192.168.10.11keepalived MASTER,haproxy + iptables
转发节点 2(LB-2)192.168.10.12keepalived BACKUP,配置与 LB-1 对称
VIP192.168.10.100对外统一入口,绑定在 bond0
后端服务节点192.168.10.21网关服务,业务端口 31201
DNAT 转发目标192.168.10.22另一网段服务,按需映射端口

一、整体架构

数据流按入口协议分为两种路径:

text
客户端
   │  访问 VIP:端口(192.168.10.100:41200)

┌───────────────────────────────────────┐
│  转发节点(VIP 当前持有者,如 LB-1)      │
│  ├─ keepalived:VRRP 心跳,VIP 漂移      │
│  ├─ haproxy:监听 41200 → 后端 31201     │   ← 长期稳定的负载均衡转发
│  └─ iptables DNAT:指定端口 → 远端 IP     │   ← 临时/固定的一对一端口映射
└───────────────────────────────────────┘
   │                    │
   ▼                    ▼
haproxy 后端        DNAT 目标
192.168.10.21:31201  192.168.10.22:PORT

选型原则:需要健康检查、负载均衡、七层能力的用 HAProxy;只需要把本机某端口原样转发到另一台机器的(如临时打通某网段服务),用 iptables DNAT,开销最小、无需用户态进程。

二、keepalived 配置

两台节点均安装 keepalived,配置除 statepriorityrouter_id 外保持一致。

LB-1(MASTER,/etc/keepalived/keepalived.conf):

conf
global_defs {
   notification_email {
     ops@example.com
   }
   notification_email_from keepalived@example.com
   smtp_server localhost
   smtp_connect_timeout 30
   router_id LB_NODE_01
}

vrrp_instance VI_1 {
    state MASTER
    interface bond0
    virtual_router_id 60
    priority 120
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass xxx
    }
    virtual_ipaddress {
        192.168.10.100
    }
}

LB-2(BACKUP)差异项:

conf
    router_id LB_NODE_02        # global_defs 中
    state BACKUP
    priority 100

关键参数说明:

参数说明注意点
interfaceVRRP 心跳和 VIP 绑定的网卡bond 环境填 bond0;必须与对端在同一二层广播域
virtual_router_idVRRP 实例 ID同一网段内必须唯一(0-255),与其他集群的 keepalived 冲突会导致 VIP 异常
priority优先级,高者持有 VIPMASTER 120 / BACKUP 100,差值建议 ≥20,避免边界抖动
advert_intVRRP 通告间隔(秒)1 秒,故障检测约 3 秒
auth_passVRRP 认证密码最长 8 字符,超出部分被截断;仅防误接入,不防恶意攻击

启动并验证:

bash
systemctl enable --now keepalived
# MASTER 上应能看到 VIP
ip addr show bond0 | grep 192.168.10.100
# BACKUP 上看不到 VIP 为正常

三、HAProxy 配置

四层 TCP 转发配置(/etc/haproxy/haproxy.cfg),将入口 41200 轮询分发到后端网关的 31201:

conf
global
    log         127.0.0.1 local2
    chroot      /usr/local/haproxy
    pidfile     /usr/local/haproxy/haproxy.pid
    maxconn     32768
    user        haproxy
    group       haproxy
    daemon

defaults
    mode  tcp
    log  global
    timeout client 3600s
    timeout connect 2s
    timeout server 3600s
    maxconn  32768

listen outside-gateway
    bind 0.0.0.0:41200
    balance roundrobin
    server gateway-1 192.168.10.21:31201  weight 1  check inter 3000 fall 3 rise 2

说明:

  • mode tcp 为四层转发,不关心应用层协议,适用于 gRPC、数据库、私有协议等。若需基于域名/路径分发,改为 mode http 并使用 frontend/backend + ACL。
  • 生产环境建议显式加健康检查参数(check inter 3000 fall 3 rise 2),后端宕机后 9 秒内摘除,恢复后 6 秒加回。
  • timeout connect 2s 控制建连超时,避免后端不可达时连接长时间挂起;长连接业务(WebSocket、隧道)把 client/server 超时放大到 3600s。
  • 多个后端直接追加多行 serverweight 按机器规格分配。
  • haproxy 绑定 0.0.0.0:41200 后,VIP 漂移到哪台,哪台的 haproxy 就能接收流量,因此两台转发节点的 haproxy 配置必须完全一致并常驻运行。

配置校验与热加载:

bash
haproxy -c -f /etc/haproxy/haproxy.cfg   # 语法检查
systemctl reload haproxy                  # 热加载,不断现有连接

四、iptables DNAT 端口转发

4.1 适用场景

  • 把本机(或 VIP 所在节点)的一批端口一对一转发到另一个网段的机器,例如本机 8080-8090 全部映射到 192.168.10.22 的同端口。
  • 跨网段临时打通某个服务,不适合为其部署 haproxy 实例的场合。

4.2 前置条件

bash
# 开启内核转发
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.d/99-forward.conf
sysctl --system

4.3 批量创建转发规则

维护一份端口清单 port-list.txt(每行一个端口号),脚本批量下发 PREROUTING(DNAT)、POSTROUTING(MASQUERADE)、FORWARD 三类规则:

bash
#!/bin/bash
# create-iptables-forward.sh —— 按端口清单批量创建 DNAT 转发
REMOTE_IP="192.168.10.22"
for PORT in $(cat port-list.txt); do
  # PREROUTING 规则(用于外部访问本机端口时改写目标地址)
  iptables -t nat -A PREROUTING -p tcp --dport $PORT -j DNAT --to-destination $REMOTE_IP:$PORT
  iptables -t nat -A PREROUTING -p udp --dport $PORT -j DNAT --to-destination $REMOTE_IP:$PORT

  # POSTROUTING 规则(地址伪装,保证回包经过本机,避免不对称路由)
  iptables -t nat -A POSTROUTING -p tcp -d $REMOTE_IP --dport $PORT -j MASQUERADE
  iptables -t nat -A POSTROUTING -p udp -d $REMOTE_IP --dport $PORT -j MASQUERADE

  # 允许转发
  iptables -A FORWARD -p tcp -d $REMOTE_IP --dport $PORT -j ACCEPT
  iptables -A FORWARD -p udp -d $REMOTE_IP --dport $PORT -j ACCEPT
  echo "已创建端口 $PORT 的转发规则"
done

三类规则缺一不可的原因:

  1. PREROUTING DNAT:入站包的目标地址从本机改为远端 IP。
  2. POSTROUTING MASQUERADE:把源地址改成本机地址,使远端机器的回包一定回到本机,否则回包直连客户端,客户端收到的包源地址不对会丢弃(三角路由问题)。
  3. FORWARD ACCEPT:默认 FORWARD 策略为 DROP 的环境(如装了 Docker/K8s 的节点)必须显式放行。

4.4 批量删除转发规则

删除时用对应的 -D 命令,参数必须与创建时完全一致:

bash
#!/bin/bash
# remove-iptables-forward.sh —— 按端口清单删除 DNAT 转发
REMOTE_IP="192.168.10.22"
for PORT in $(cat rm-port-list.txt); do
  iptables -t nat -D PREROUTING -p tcp --dport $PORT -j DNAT --to-destination $REMOTE_IP:$PORT
  iptables -t nat -D PREROUTING -p udp --dport $PORT -j DNAT --to-destination $REMOTE_IP:$PORT

  iptables -t nat -D POSTROUTING -p tcp -d $REMOTE_IP --dport $PORT -j MASQUERADE
  iptables -t nat -D POSTROUTING -p udp -d $REMOTE_IP --dport $PORT -j MASQUERADE

  iptables -D FORWARD -p tcp -d $REMOTE_IP --dport $PORT -j ACCEPT
  iptables -D FORWARD -p udp -d $REMOTE_IP --dport $PORT -j ACCEPT

  echo "已删除端口 $PORT 的所有 iptables 规则"
done

4.5 紧急清空(慎用)

调试时需要清空全部 NAT 规则时使用,注意这会影响节点上其他依赖 iptables 的组件(Docker、kube-proxy 的 NAT 规则也会被清掉),K8s 节点上禁止使用

bash
iptables -X          # 删除所有自定义链(filter 表)
iptables -Z          # 计数器清零
iptables -t nat -F   # 清空 nat 表所有规则
iptables -t nat -X
iptables -t nat -Z

4.6 规则持久化

iptables 规则重启即丢失,持久化方式:

bash
# RHEL/Rocky 系
iptables-save > /etc/sysconfig/iptables
systemctl enable iptables

# Debian/Ubuntu 系
apt install iptables-persistent
netfilter-persistent save

五、验证与切换演练

5.1 VIP 漂移验证

bash
# MASTER 上确认 VIP 在线
ip addr show bond0 | grep 192.168.10.100

# 模拟 MASTER 故障:直接停 keepalived
systemctl stop keepalived

# BACKUP 上 1~3 秒内应接管 VIP
ip addr show bond0 | grep 192.168.10.100

# 客户端验证业务不中断
curl -v telnet://192.168.10.100:41200

# 恢复 MASTER,因 priority 更高,VIP 应切回
systemctl start keepalived

5.2 HAProxy 转发验证

bash
# 确认监听
ss -lntp | grep 41200

# 通过 VIP 访问后端服务
nc -zv 192.168.10.100 41200

# 查看 haproxy 日志确认后端选择
tail -f /var/log/haproxy.log

5.3 DNAT 转发验证

bash
# 确认规则存在及命中计数(pkts 列应随访问增长)
iptables -t nat -L PREROUTING -n -v --line-numbers | grep 192.168.10.22

# 从外部客户端访问本机映射端口(以 VIP 上的 8080 为例)
nc -zv 192.168.10.100 8080

# 在本机抓包确认 DNAT 生效
tcpdump -i bond0 -nn host 192.168.10.22

5.4 脑裂检查

两台节点同时持有 VIP 即为脑裂。在两台节点分别执行:

bash
ip addr show bond0 | grep 192.168.10.100

若同时存在,检查两台之间 VRRP 组播/单播是否可达(见故障速查)。

六、故障速查

现象可能原因处理方法
VIP 不漂移两端 virtual_router_id 或 auth_pass 不一致;防火墙拦截 VRRP(协议号 112,非端口)核对两端配置;放行 VRRP 协议:iptables -A INPUT -p vrrp -j ACCEPT
VIP 同时出现在两台(脑裂)两节点间 VRRP 心跳不通(组播被交换机过滤、跨 VLAN);双方都认为自己是 MASTER检查二层连通性;改用 unicast_src_ip/unicast_peer 单播模式;确认 virtual_router_id 未与网段内其他实例冲突
VIP 频繁来回漂移priority 差值太小;MASTER 节点负载高导致 VRRP 通告超时拉大 priority 差值;在 MASTER 上加 nopreempt 关闭抢占;排查节点负载
通过 VIP 访问 haproxy 端口不通VIP 不在本机时内核默认仍可响应绑 0.0.0.0 的服务,但若绑定了具体 IP 需在非持有者节点上放行非本地绑定确保 haproxy 绑 0.0.0.0;如需绑 VIP,设置 net.ipv4.ip_nonlocal_bind=1
DNAT 转发后客户端超时缺少 POSTROUTING MASQUERADE,回包走了直连(三角路由)按 4.3 节补齐 POSTROUTING 规则
DNAT 规则不生效net.ipv4.ip_forward=0;FORWARD 链默认 DROPsysctl net.ipv4.ip_forward=1;补 FORWARD ACCEPT 规则
dmesg 报 nf_conntrack: table full, dropping packetDNAT/MASQUERADE 使所有连接都过 conntrack,表被打满调大 net.netfilter.nf_conntrack_max(如 1000000)并同步调整 hashsize(max/4);对纯转发流量可用 raw 表 NOTRACK 豁免
删除规则报 Bad rule-D 参数与创建时不完全一致iptables -t nat -L --line-numbers 查到行号后按行号删:iptables -t nat -D PREROUTING <行号>
重启后规则丢失未做持久化按 4.6 节 iptables-save / iptables-persistent 持久化

注意事项

事项说明
auth_pass 安全性VRRP 密码明文传输且仅 8 字符有效,作用有限,生产环境应靠网络隔离(专用 VLAN/安全组)保护 VRRP 流量
K8s 节点慎用清空脚本4.5 节的清空操作会连带删除 Docker/kube-proxy 的 NAT 规则,仅限专用转发节点使用
DNAT 与 conntrack每条 DNAT 连接占一个 conntrack 条目,高并发端口映射场景务必评估 conntrack 容量
配置对称两台 LB 节点的 haproxy.cfg、iptables 规则必须保持一致,VIP 漂移后行为才一致;建议用配置管理工具统一下发