Skip to content

会话高可用

这是 nat44-ei 最有分量的独有能力,也是某些场景下必须选 EI 而不能选 ED 的理由。

为什么 NAT 的高可用特别难

普通路由器做主备切换很简单:备机把路由学过来就行,因为路由是无状态的

NAT 不行。NAT 的全部价值都在那张会话表里:

切换本身可能只要几百毫秒,但所有已建立的连接都会断。对用户来说, 这和"网络挂了"没有区别 —— 视频通话掉线、下载中断、SSH 会话消失。

会话高可用要解决的就是这个:让备机提前知道主机的每一条会话。

配置

启动 HA 底座

bash
./labctl up ha

这个底座有两台 VPP:

text
  h1 10.10.0.11 ──── nat1 ──── s1 203.0.113.11
                      │ 10.99.0.1
                      │  同步链路
                      │ 10.99.0.2
                    nat2(备机)

这个底座不开 polling

前两个底座为了低时延给 VPP 开了 polling,代价是吃满一个 CPU 核。 两台 VPP 都开就是两个核,4 核的实验机扛不住。

所以 HA 底座刻意用中断模式。时延会高一些,但 HA 实验关心的是 "会话有没有同步过去",不是微秒级时延。

应用配置

bash
./labctl apply ha 01
./labctl verify ha 01

配置分四步,顺序很讲究

bash
# ─── 1. 备机先就位 ───────────────────────────────
vppctl nat44 ei plugin enable
vppctl nat44 ei add address 203.0.113.1        # ← 关键:和主机相同的地址池
vppctl nat44 ei ha listener 10.99.0.2:8484

# ─── 2. 主机的 HA 配置 ───────────────────────────
vppctl nat44 ei plugin enable
vppctl nat44 ei ha listener 10.99.0.1:8484     # ← 主机也要有本地 listener
vppctl nat44 ei ha failover 10.99.0.2:8484 refresh-interval 2

# ─── 3. 主机的业务配置 ───────────────────────────
vppctl set interface nat44 ei in host-eth1 out host-eth2
vppctl nat44 ei add address 203.0.113.1

# ─── 4. 确认状态 ────────────────────────────────
vppctl show nat44 ei ha

两个必须踩过才知道的坑

配 HA 最折磨人的地方是:配错了不会报错,只是不同步。 下面两个坑我在实验里都撞上了,都花了不少时间才定位。

坑一:主机也需要本地 listener

直觉上 listener 是备机用的(接收),failover 是主机用的(发送)。 所以很自然会只在备机配 listener。

结果主机的 HA 根本起不来:

bash
vppctl nat44 ei ha failover 10.99.0.2:8484
vppctl show nat44 ei ha
text
NAT HA disabled          ← 配了 failover 却显示 disabled

原因是同步用的 UDP socket 由 listener 创建,主机没有 listener 就没有发送端点。 先配上本地 listener,failover 才生效:

bash
vppctl nat44 ei ha listener 10.99.0.1:8484      # 先这个
vppctl nat44 ei ha failover 10.99.0.2:8484      # 再这个
text
LISTENER:
  10.99.0.1:8484 path-mtu 512
FAILOVER:
  10.99.0.2:8484 refresh-interval 2sec
RESYNC:
  completed (0 ACK missed)

坑二:备机必须有相同的地址池

这个坑更隐蔽。两边 HA 状态都正常,同步报文也在链路上跑,但备机会话数就是 0。

在备机上抓包能看到报文确实到了:

bash
./labctl vppctl ha clear trace     # 注意:这条要在 nat2 上执行
text
00:01:29:611765: ip4-input
  UDP: 10.99.0.1 -> 10.99.0.2
  UDP: 8484 -> 8484
00:01:29:611776: nat44-ei-ha
  nat44-ei-ha: 0 events from 10.99.0.2
                ^^^^^^^^ 报文到了,但一个会话事件都没带

心跳在跑,会话事件是空的。

原因是备机没有配地址池。同步过来的会话引用了外部地址 203.0.113.1, 备机不认识这个地址,于是把整条会话丢掉 —— 不报错,不计数,静默丢弃

给备机补上同一个地址池,立刻就通了:

bash
vppctl nat44 ei add address 203.0.113.1     # 在备机上执行

这是 HA 排错的第一检查项

"两边状态都正常但就是不同步" —— 先对比两边的地址池:

bash
./labctl vppctl ha show nat44 ei addresses          # nat1
ssh chenzhong@192.168.49.182 \
  'docker exec clab-natha-nat2 vppctl show nat44 ei addresses'   # nat2

两边必须完全一致。 少一个地址,用到那个地址的会话就全部同步不过去, 而且完全没有告警。

验证:端口映射必须逐条一致

配好之后,让 h1 建 6 条会话:

text
── 3) 建 6 条会话 ──────────────────────────
  nat1(主)会话数:6
  nat2(备)会话数:6
✓ 会话已同步到备机

会话数一致还不够。真正决定切换能不能无感的,是端口映射是否逐条相同:

text
── 4) 关键:端口映射是否逐条一致 ─────────────
  nat1 的 内网端口→外部端口:
      45001	58301
      45002	41464
      45003	52471
      45004	38378
      45005	40001
      45006	39084
  nat2 的 内网端口→外部端口:
      45001	58301
      45002	41464
      45003	52471
      45004	38378
      45005	40001
      45006	39084
✓ 两边的端口映射完全一致

为什么"一致"才是重点

如果备机只知道"有这么一条会话",却自己重新分配了外部端口, 那么切换之后外网发回来的包目的端口对不上,连接照样断

只有映射逐字节相同,切换才是无感的 —— 外网侧根本察觉不到对端换了一台设备。

同步协议

同步走 UDP,端口就是你配的那个(示例里是 8484),由一个专门的 graph node 处理:

text
00:05:49:442756: nat44-ei-ha
  nat44-ei-ha: N events from 10.99.0.1

几个参数:

参数含义建议
refresh-interval <秒>主机多久推一次会话刷新生产建议 5~10 秒。太短占带宽,太长丢的会话多
path-mtu <字节>同步报文的分片上限,默认 512同步链路 MTU 大时可以调大,减少报文数

两个手工命令:

bash
# 强制全量重同步(备机刚上线、怀疑不同步时用)
vppctl nat44 ei ha resync

# 清空 HA 状态
vppctl nat44 ei ha flush

resync 不是万能的

实验里发现:如果会话是在备机配好地址池之前建立的, resync 也推不过去 —— 那些会话在备机侧依然会被丢弃。

所以正确的上线顺序是:先把备机完全配好,再让流量上主机。 反过来会留下一批永远同步不了的"僵尸会话",直到它们自己老化。

这一篇只做了 HA 的一半

必须说清楚:这一篇只验证了会话同步,没有验证故障切换。

一套能真正用的 NAT 高可用,除了会话同步还需要:

text
  ✓ 本篇已验证   会话表实时同步,端口映射逐条一致

  ○ 下一篇补上   虚拟 IP(VIP)—— 主备共用一个网关地址
  ○ 下一篇补上   VRRP —— 检测故障并自动漂移 VIP
  ○ 下一篇补上   切换时已建立连接的实际存活率

  ✗ 仍未覆盖     BFD 联动(做到亚秒级切换)
  ✗ 仍未覆盖     双主(split-brain)防护

会话同步单独存在是没有意义的

备机把所有会话都学到了,但内网主机的默认网关写的还是主机的地址 —— 主机一挂,流量就停在那儿,备机再"知情"也没用。

下一篇用 VRRP 把地址漂移这一半补上, 并用一组对照实验证明:两半都在,切换时连接一次都不断; 少任何一半,切换即断。

ED 有对应功能吗

没有。对比两个插件的命令族:

bash
vppctl nat44 ei ha ?
text
  nat44 ei ha failover      nat44 ei ha failover <ip4-address>:<port> [refresh-interval <sec>]
  nat44 ei ha flush         nat44 ei ha flush
  nat44 ei ha listener      nat44 ei ha listener <ip4-address>:<port> [path-mtu <path-mtu>]
  nat44 ei ha resync        nat44 ei ha resync

nat44-ed 下没有任何 ha 子命令。

所以如果你的需求里有"NAT 切换时已建立的连接不能断",那就只能用 EI。 这不是权衡,是硬约束 —— 哪怕你更想要 ED 的端口利用率和 twice-NAT。

小结

解决什么主备切换时已建立的连接不断
配置顺序备机(含地址池)→ 主机 listener → 主机 failover → 主机业务
坑一主机也需要本地 listener,否则 show ... ha 显示 disabled
坑二备机地址池必须和主机一致,否则会话静默丢弃且无计数
验证要点不只看会话数,要比对端口映射是否逐条一致
同步协议UDP,refresh-interval 控制频率,path-mtu 控制分片
ED 有吗没有。这是选 EI 的硬理由

下一篇:完成 HA:VRRP 与真实切换 —— 把另一半补上。

实验环境基于 netlab + containerlab + VPP 26.06