Skip to content

完成 HA:VRRP 与真实切换

上一篇做完了 NAT 高可用的一半 —— 会话表同步。 但那一篇结尾有一张诚实的清单:

text
  ✓ 已验证    会话表实时同步,端口映射一致
  ✗ 未验证    虚拟 IP(VIP)
  ✗ 未验证    VRRP / BFD 自动切换
  ✗ 未验证    切换时的实际连接存活率

这一篇把这三项补上。结论先说:做完之后,切换时已建立的连接一次都不断。

为什么会话同步只是一半

会话同步解决的是"备机知道有哪些连接"。但它没解决另一个问题: 流量凭什么会流到备机去?

内网主机的默认网关写的是 nat1 的地址。nat1 挂了,流量就停在那儿 —— 备机会话表再完整也没用,没人把包送给它。

需要一个会漂移的地址

VRRP:让地址在两台设备之间漂移

VRRP(Virtual Router Redundancy Protocol,RFC 5798)的做法:

text
  两台设备加入同一个 VR(虚拟路由器)组

  组内选出一个 Master(按优先级)

  Master 持有 VIP 和一个虚拟 MAC(00:00:5e:00:01:<vr_id>)

  Master 停止发通告 → Backup 的 master-down 定时器超时 → 接管

关键细节:虚拟 MAC 在两台上是同一个。所以切换时内网主机 连 ARP 都不用重学 —— 它继续发往 00:00:5e:00:01:01, 只是接手的换了一台设备。

拓扑要求

主备必须在同一个二层网段上

这是 VRRP 的前提,也是本教程 HA 底座改过一次拓扑的原因。

最初的底座只有一条同步链路连着 nat2,做会话同步够用, 但 nat2 不在内外网段上,VRRP 无从谈起。现在的底座是:

text
  h1 10.10.0.11 ──┬── nat1 .1 ──┬── s1 203.0.113.11
                  └── nat2 .2 ──┘
                       │ 同步链路 10.99.0.0/24

  内网 VIP  10.10.0.254
  外网 VIP  203.0.113.254

两台 VPP 同时在内网段、外网段和同步链路上,一共三个接口。

配置

应用配置

bash
./labctl up ha
./labctl reset ha
./labctl apply ha 02

分四步。

一、两台的 NAT 配置必须一致,地址池就是外网 VIP

bash
# 在 nat1 和 nat2 上都执行
vppctl nat44 ei plugin enable
vppctl set interface nat44 ei in host-eth1 out host-eth2
vppctl nat44 ei add address 203.0.113.254      # ← 就是那个会漂移的 VIP

地址池用 VIP 是整个方案的枢纽

如果 nat1 的池是 203.0.113.1、nat2 的池是 203.0.113.2, 那么切换之后所有会话的外部地址都变了 —— 外网侧看到源地址突变, 连接必然中断。

池地址必须跟着 Master 一起漂移,外网侧才会毫无感知。

顺带一个前面章节的知识点在这里派上用场: nat44 会把池地址装进 FIB 当作本地地址, 所以持有 VIP 的那台自然会代答 ARP。

二、打开会话同步

bash
# nat2(备)
vppctl nat44 ei ha listener 10.99.0.2:8484
# nat1(主)
vppctl nat44 ei ha listener 10.99.0.1:8484
vppctl nat44 ei ha failover 10.99.0.2:8484 refresh-interval 2

细节和坑见上一篇 —— 主机也需要本地 listener, 两边地址池必须一致,否则同步会被静默丢弃。

三、建两个 VRRP 组

内外网段各一个,因为两侧的地址都要漂移:

bash
# 内网 VR 1
vppctl vrrp vr add host-eth1 vr_id 1 priority 200 accept_mode 10.10.0.254   # nat1
vppctl vrrp vr add host-eth1 vr_id 1 priority 100 accept_mode 10.10.0.254   # nat2

# 外网 VR 2
vppctl vrrp vr add host-eth2 vr_id 2 priority 200 accept_mode 203.0.113.254 # nat1
vppctl vrrp vr add host-eth2 vr_id 2 priority 100 accept_mode 203.0.113.254 # nat2

# 两台都要启动协议
vppctl vrrp proto start host-eth1 vr_id 1
vppctl vrrp proto start host-eth2 vr_id 2
参数含义
priority越大越优先,nat1 给 200、nat2 给 100
accept_mode必须开 —— 不开的话 Master 只转发,自己不接收发往 VIP 的包,VIP 连 ping 都不通
interval通告间隔,默认 100(单位 10ms,即 1 秒)
no_preempt高优先级节点恢复后抢回 Master

四、内网主机改用 VIP 做网关

bash
ip route replace default via 10.10.0.254 dev eth1

确认状态

bash
./labctl vppctl ha show vrrp vr
text
[0] sw_if_index 1 VR ID 1 IPv4
   state Master flags: preempt yes accept yes unicast no
   priority: configured 200 adjusted 200
   timers: adv interval 100 master adv 100 skew 21 master down 321
   virtual MAC 00:00:5e:00:01:01
   addresses 10.10.0.254

选举要等几秒

master down 321 是 3.21 秒。刚 vrrp proto start 之后马上去看, 可能两边都显示 Backup —— 定时器还没到。等 3~5 秒再看。

我第一次测就是查得太早,以为外网那个 VR 没选出 Master。

验证:连接到底断不断

跑自检

bash
./labctl verify ha 02

自检做两组对照实验,各建一条长连接(每秒一次心跳), 在中途触发切换,看心跳有没有断。

实验 A:VRRP + 会话同步

text
  节点   切换前包数 切换后包数 增量
  ────── ────────── ────────── ──────
  nat1     35           35           +0
  nat2     17           52           +35

  RESULT 成功 18 次,失败 0 次
✓ 已建立的连接全程存活,一次心跳都没丢
✓ 切换后流量确实转到了 nat2(增量更大)

两件事同时被证明了:

  • 流量真的转移了 —— 切换后 nat2 的转发包数 +35,nat1 +0
  • 连接毫发无损 —— 18 次心跳全部成功

为什么要看包计数

只看"连接没断"是不够的。VRRP 用同一个虚拟 MAC,如果切换其实没成功, 流量可能仍然走着原来那台,连接自然也不会断 —— 那就测了个假阳性。

必须用两台的转发计数证明流量确实换了设备。 这是设计这个实验时最容易偷懒的地方。

实验 B:只有 VRRP,没有会话同步

同样的拓扑、同样的切换,只是不配 ha failover

text
  RESULT 成功 6 次,失败 9 次
      [ 12] FAIL BrokenPipeError
      [ 13] FAIL BrokenPipeError
      [ 14] FAIL BrokenPipeError
✓ 连接在切换后断了 —— 证明光有 VRRP 不够

VIP 漂过去了,包也到了备机。但备机的会话表里没有这条流, 回程包查不到转换关系,直接被丢。

text
  ┌────────────────────┬────────────────────────────┐
  │ 配置                │ 切换后已建立的连接            │
  ├────────────────────┼────────────────────────────┤
  │ VRRP + 会话同步      │ 成功 18 次,失败 0 次        │
  │ 只有 VRRP           │ 成功 6 次,失败 9 次         │
  └────────────────────┴────────────────────────────┘

NAT 的高可用是两件事,缺一不可

text
  地址漂移(VRRP)   →  让流量能到备机
  状态同步(HA sync)→  让备机认得这些流量

只做前者:流量到了但被丢,症状是切换后连接全断、 备机 no translation 计数暴涨。

只做后者:备机什么都知道,但永远等不到流量。 这就是上一篇结束时的状态。

接口跟踪:更实用的切换触发条件

上面的实验用 vrrp proto stop 手工触发切换。真实故障通常是 某个接口挂了,而 VRRP 默认只看自己所在网段的通告。

设想:nat1 的外网口断了,但内网口还好。这时 nat1 仍会在内网段上 正常发通告,继续当 Master —— 而它已经出不去了,流量全黑洞。

track-if 解决这个问题:

bash
vppctl vrrp vr track-if add host-eth1 vr_id 1 \
    track-index <外网口的 sw_if_index> priority 50

意思是:如果被跟踪的接口 down,本 VR 的优先级降低 50。 降到低于对端后,对端抢占成为 Master。

优先级差值要算好

text
  nat1 配置优先级 200,跟踪扣减 50 → 降到 150
  nat2 配置优先级 100

  150 > 100,仍然是 nat1 当 Master —— 跟踪等于没起作用

扣减值必须大于两台的优先级差。上例应该扣 ≥ 101, 或者把 nat2 的优先级提到 160 以上。

这是配 VRRP 跟踪时最常见的错误。

还剩什么没做

诚实地说,这一篇之后仍有两块没覆盖:

text
  ✗ BFD 联动
    VRRP 的默认通告间隔是 1 秒,master-down 约 3.2 秒 ——
    也就是说切换要 3 秒左右。要做到亚秒级,需要 BFD
    快速检测链路故障并联动 VRRP。VPP 有 bfd 插件,
    但和 VRRP 的联动没在本教程里验证。

  ✗ 双主(split-brain)防护
    同步链路断了,两台都会认为对方挂了,都当 Master。
    两台同时用同一个 VIP 和同一个 NAT 池 → 端口分配冲突、
    会话错乱。生产环境需要额外的仲裁机制
    (多条心跳链路、或者第三方仲裁点)。

第二条是真实部署里最需要小心的。本实验用一条专用同步链路, 它本身就是单点。

小结

解决什么让流量能切到备机,配合会话同步实现无感切换
前提主备必须在同一个二层网段
配置两侧各建一个 VR,priority 定主备,必须开 accept_mode
枢纽NAT 地址池 = 外网 VIP,池地址跟着 Master 漂移
实测结果VRRP + 同步 → 18/18 心跳存活;只有 VRRP → 切换即断
验证要点不能只看连接没断,要用两台的转发计数证明流量真的转移了
切换耗时约 3 秒(默认定时器);要更快需要 BFD 联动
未覆盖BFD 联动、双主防护

第 3 章到此真正完整了:

text
  行为画像   会话粒度、过滤、端口算法、每用户配额
  会话同步   备机知道所有连接
  VRRP 切换  流量能到备机,连接不断
  选型       什么时候必须用 EI

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