EVPN 全活跃网段上的 Linux 主备 bond:哪里会断
全活跃 EVPN 网段会把单播发往任一叶交换机,而 Linux 主备 bond 只在一个端口上收包。测试前先把两种模型对齐。
在服务器上放一个主备 bond,在 fabric 上放一个全活跃以太网网段,一半的回程流量可能凭空消失,而任何一个错误计数器都不动。这两种配置单独看都没有错。问题出在两者之间的交接:一侧只挑一个端口,另一侧却假定每台接入的叶交换机都能送达。沿每个方向跟踪一个帧,不匹配就一目了然。
主备模式如何处理进来的帧
bonding 模式 1 在发送路径上只保留一个 slave,只有当前 slave 故障时才切换。这一点众所周知。这里要紧的是接收侧:只有当帧从活动 slave 到达时,内核才会把它交给协议栈,出现在其他 slave 上的帧一律丢弃。这个 all_slaves_active 设置正是为了放宽这条规则而存在,默认是关闭的。 ip -d link show bond0 会在模式旁边打印当前值。
/proc/net/bonding/bond0 会列出活动 slave,并报告每个 slave 的链路状态。要问当前哪个端口在承载流量,那个文件是唯一可靠的来源。
处于这种模式的主机一次只有一条可用的接收路径,而且是本地选出的,完全不知道 fabric 的情况。如果发往该主机的帧从另一根线缆进来,交换机的端口会把它计为已接收,主机的协议栈永远看不到它,除了主机自己的丢弃计数器以外,所有计数器看起来都完全正常。
全活跃多归属对远端下一跳意味着什么
EVPN 多归属描述一组共享同一个以太网网段的叶交换机,用 ESI 标识,并向 fabric 的其余部分通告该网段。在全活跃模型下,远端对等体会为该网段的地址安装不止一个下一跳。于是发往服务器的单播可能落在任一叶交换机上,fabric 可以按流自由选择。这正是设计的意义所在,也是服务器 bond 必须满足的前提。
泛洪流量遵循另一条规则。叶交换机为每个网段选举一台指定转发者,因此广播、未知单播和组播通常从其中一台进入。所以单播和泛洪做出两个彼此独立的决定,而主机 bond 状态里只看得到其中一个。
两种选择在哪里分歧
内核依据链路状态和自己的策略挑选活动 slave。fabric 依据 EVPN 路由挑选下一跳,并依据叶交换机之间的选举挑选指定转发者。两个过程互不通气。当它们选中不同的叶交换机时,故障只出现在一个方向上:服务器发出去的报文正常离开,回程的应答却消失了。
有两种受支持的出路,而且不能互相替代。给主机一个 fabric 能当作单一逻辑接入来处理的 bond,实践中就是用 LACP 与叶交换机协商的 bond,两条链路真正同时活跃。或者反过来,也接受 fabric 侧的单活跃投递,让网段遵循和 bond 一样的“一次一个端口”模型。行不通的是一台只在一个端口上收包的 bond 前面放一个全活跃网段,除非你有意打开非活动 slave 上的接收,并接受随之而来的重复投递。
为什么备用线缆一直看起来正常
不承载流量的线缆照样汇报链路 up,bond 里的备用 slave 本来就被设计成看起来随时可用。运维人员把这当成冗余,于是不再追问。
换个问法:回程帧走的是哪个物理端口。在主机上按 slave 而不是按 bond 统计收包数,因为 bond 的计数器把不对称掩盖了。在叶交换机上,看 服务器 MAC 对应的网桥转发表项 以及 EVPN 路由携带的下一跳集合。如果表项指向的是主机链路为备用状态的那台叶交换机,设计就是在往一个主机根本不看的端口投递。这是配置不匹配,不是硬件故障,任何链路状态监控都发现不了。
要测两个方向的送达,而不是链路状态
搭一张矩阵,每个故障一行,每个方向一列,然后填实际送达的流量,而不是接口状态。第一行是正常运行,而不匹配恰恰藏在这里,因为症状看起来像应用问题。
接着依次移除活动主机链路、移除一台叶交换机、移除那台叶交换机的上联,再逐个恢复。每一步之后,记录活动 slave、EVPN 网段状态、远端转发表项,以及两个方向上实测的吞吐。主机链路仍然 up 却丢掉 fabric 路径的叶交换机才是关键情形,因为接入仍然被通告,可能悄悄变成一个死胡同。要用全新的单播流和业务依赖的泛洪流量分别测试,因为两者的转发决定并不相同。
只有当矩阵显示出流量沿你预期的那条路径双向送达时,才可以接受这个设计。两根线缆都显示 up,并不等于那个结果。