Skip to content

实验 11:链路聚合 ​

两台交换机之间只有一条线,带宽受限,断了就全断。多拉几条线呢?直接接会形成二层环路,广播风暴瞬间打满。

链路聚合(bond)解决这个问题:把多条物理链路绑成一个逻辑口,对上层来说只有一个端口,不成环;对下层来说流量分摊在多条线上,断一条自动切换。

起环境 ​

bash
cd labs/learn-ovs/08-bond
./start.sh
text
              两条链路
   h1 ── ovs1 ═════════ ovs2 ── h2
         eth2,eth3   eth1,eth2

   ovs1: eth1 接 h1,eth2/eth3 去 ovs2
   ovs2: eth1/eth2 去 ovs1,eth3 接 h2

start.sh 只把主机接进了网桥,两条互联线都还没接,所以现在不通:

bash
docker exec clab-bond-h1 ping -c 2 -W 1 192.168.10.2
text
2 packets transmitted, 0 received, 100% packet loss

不要直接把两条线都 add-port

bash
# 千万别这么干
ovs-vsctl add-port br0 eth2
ovs-vsctl add-port br0 eth3

两条线在同一个二层里就是一个环。一个广播包会在两条线之间无限循环并不断复制,几秒内打满 CPU 和链路。

避免环路只有两条路:开 STP(阻塞其中一条,浪费带宽),或者做 bond(两条都用上)。

建 bond ​

bash
N1="docker exec clab-bond-ovs1"
N2="docker exec clab-bond-ovs2"

$N1 ovs-vsctl add-bond br0 bond0 eth2 eth3 \
  -- set Port bond0 bond_mode=balance-slb

$N2 ovs-vsctl add-bond br0 bond0 eth1 eth2 \
  -- set Port bond0 bond_mode=balance-slb

语法是 add-bond <网桥> <bond名> <成员口...>。两端都要配 —— 只配一边,另一边会把两条线当独立端口,环路照样成立。

bash
$N1 ovs-vsctl show
text
Bridge br0
    Port bond0
        Interface eth2
        Interface eth3
    Port eth1
        Interface eth1
    Port br0
        Interface br0
            type: internal

这就是 Port 和 Interface 分两张表的原因

实验 2 里我们说 Port.interfaces 是个列表,单口时只有一个元素。

这里终于用上了:Port bond0 下面挂着两个 Interface。VLAN、QoS 这些配在 bond0 这个 Port 上,对两个成员统一生效。

现在通了:

bash
docker exec clab-bond-h1 ping -c 3 192.168.10.2
text
3 packets transmitted, 3 received, 0% packet loss

看 bond 状态 ​

bash
$N1 ovs-appctl bond/show bond0
text
---- bond0 ----
bond_mode: balance-slb
bond may use recirculation: no, Recirc-ID : -1
bond-hash-basis: 0
lb_output action: disabled, bond-id: -1
all members active: false
updelay: 0 ms
downdelay: 0 ms
next rebalance: 3913 ms
lacp_status: off
lacp_fallback_ab: false
active-backup primary: <none>
active member mac: aa:c1:ab:b7:a6:cf(eth2)

member eth2: enabled
  active member
  may_enable: true

member eth3: enabled
  may_enable: true

关键字段:

字段含义
bond_mode分流算法
lacp_statusLACP 协商状态,off = 没开
updelay / downdelay链路状态变化后延迟多久才切换,防抖动
next rebalance距离下次重新平衡还有多久
active member当前承载广播和未知单播的成员
may_enable这个成员是否可用

active member 是什么

即使在负载分担模式下,广播和未知单播也只从一个成员发出 —— 否则对端会收到重复的包。这个被选中的成员就是 active member。

单播流量则按哈希分摊到所有成员。

断一条线试试 ​

bash
$N1 ip link set eth2 down
sleep 3
docker exec clab-bond-h1 ping -c 3 192.168.10.2
text
3 packets transmitted, 3 received, 0% packet loss

流量自动走了 eth3,业务无感。这是 bond 最直接的价值。

恢复:

bash
$N1 ip link set eth2 up

恢复后要等它收敛

链路 up 之后,OVS 需要重新把成员纳入哈希分配。bond/show 里的 next rebalance 就是这个周期。

恢复后立刻打流量可能丢一两个包。 这也是 verify.sh 在恢复后要 sleep 8 的原因 —— 否则测的是收敛过程而不是稳定状态。生产环境用 updelay 来避免链路抖动导致的反复切换:

bash
ovs-vsctl set Port bond0 other_config:bond-rebalance-interval=10000
ovs-vsctl set Port bond0 other_config:bond-detect-mode=carrier

三种 bond 模式 ​

模式分流依据需要对端支持说明
active-backup不分流否只用一条,其余待命。最保险,浪费带宽
balance-slb源 MAC(加 VLAN)哈希否默认选择。同一个源 MAC 始终走同一条线
balance-tcp五元组哈希是(LACP)分流最均匀,必须配 LACP

怎么选 ​

对端也做聚合(另一台 OVS、或配了 LACP 的物理交换机) → balance-tcp + LACP。这是默认选择。同一台主机的不同 TCP 连接能走不同的线,分流最均匀,而且协商保证两端一致。

对端是普通交换机、完全不能配聚合 → balance-slb。它靠源 MAC 哈希,对端无需任何配置。但两端都是 OVS 时别用它(见上面的警告)。

只要冗余不要带宽叠加 → active-backup。行为最容易预测,也不需要对端配合。

balance-slb 的局限

按源 MAC 哈希意味着单台主机的流量永远只走一条线。两台服务器之间跑一个大文件传输,无论绑几条线,带宽都是一条线的。

要让单台主机的流量分摊,必须用 balance-tcp(按五元组哈希,不同连接走不同线)。

看 LACP 协商结果 ​

LACP(802.3ad)是标准协商协议:两端交换协议报文,确认对方也把这些口绑在一起了,才启用聚合。

bash
$N1 ovs-appctl lacp/show bond0
text
member: eth2: current attached
  actor state: activity aggregation synchronized collecting distributing
  partner sys_id: 6e:57:8e:b1:07:4b
  partner state: activity aggregation synchronized collecting distributing

member: eth3: current attached
  actor state: activity aggregation synchronized collecting distributing
  partner state: activity aggregation synchronized collecting distributing

actor 是自己,partner 是对端。两边都出现 synchronized collecting distributing 就是协商成功。

LACP 设置行为
lacp=active主动发 LACP 报文
lacp=passive只响应,不主动发
lacp=off关闭

至少有一端是 active,两端都 passive 会一直等下去。

LACP 的真正价值是防呆

它最大的作用不是分流,而是确认对端配置正确。线插错口、对端忘了配 bond,LACP 协商不通过,那条线就不会被启用 —— 避免了静默的环路或黑洞。

生产环境能用 LACP 就用。

和 VLAN 一起用 ​

bond 口就是个普通 Port,VLAN 配置照常:

bash
ovs-vsctl set Port bond0 trunks=100,200      # trunk
ovs-vsctl set Port bond0 tag=100             # access

配在 bond0 上,对两个成员统一生效 —— 又一次体现了 Port/Interface 分层的价值。

自动检查 ​

bash
./verify.sh
text
  [通过] ovs1 上存在 bond0
  [通过] bond0 有两个成员口
  [通过] bond 模式是 balance-slb
  [通过] h1 能 ping 通 h2

== 断掉一条链路,验证流量自动切到另一条 ==
  [通过] 断一条线后仍然连通

自测清单 ​

  1. 为什么不能直接把两条互联线都 add-port 进网桥?
  2. bond 在 OVSDB 里是怎么表示的?体现了哪张表的什么设计?
  3. active member 承载什么流量?为什么广播不能从所有成员发?
  4. balance-slb 和 balance-tcp 的分流依据分别是什么?
  5. 两台服务器之间传大文件,用 balance-slb 绑两条线能翻倍吗?
  6. 为什么两端都是 OVS 时不能用 balance-slb?会出现什么症状?
  7. LACP 最大的价值是分流还是别的?
  8. 两端都配 lacp=passive 会怎样?

常见问题 ​

建了 bond 但不通 ​

按顺序查:

  1. 两端都配了吗 —— 只配一边必然出问题
  2. 成员口是 up 的吗 —— ip -br link
  3. 成员口配对了吗 —— ovs1 的 eth2/eth3 要真的连到 ovs2 的 eth1/eth2
bash
ovs-appctl bond/show bond0     # 看 may_enable 是不是 true

广播风暴(CPU 打满、链路跑满) ​

多半是环路。立刻断掉一条线:

bash
ip link set eth3 down

然后检查两端是不是都建了 bond。只有一端建了 bond,另一端把两条线当独立端口,环路依然存在。

LACP 一直协商不上 ​

bash
ovs-appctl lacp/show bond0
  • partner 全是 0 或 defaulted → 对端根本没发 LACP 报文,检查对端配置
  • 两端都 passive → 至少改一端为 active
  • 状态里没有 synchronized → 检查两端的 aggregation key 是否一致

时通时不通、MAC 表看着都对 ​

典型的 balance-slb 两端都跑的症状。改成 LACP:

bash
ovs-vsctl set Port bond0 bond_mode=balance-tcp lacp=active   # 两端都要
ovs-appctl lacp/show bond0 | grep synchronized               # 确认协商成功

流量都压在一条线上 ​

用 balance-slb 且只有一台主机在发流量,这是预期行为(按源 MAC 哈希)。想分摊要用 balance-tcp + LACP。

看实际分布:

bash
ovs-appctl bond/show bond0        # 看每个成员的哈希桶
ovs-ofctl dump-ports br0          # 看各口的收发计数

小结 ​

  • 多条线直接接进同一个网桥会成环,必须做 bond(或开 STP)
  • add-bond <网桥> <bond名> <成员...>,两端都要配
  • bond 在 OVSDB 里是一个 Port 挂多个 Interface,这是 Port/Interface 分表的意义所在
  • active member 承载广播和未知单播;单播按哈希分摊
  • active-backup 只求冗余,balance-slb 按源 MAC(对端无需配置),balance-tcp 按五元组(需要 LACP)
  • balance-slb 下单台主机的流量不会分摊到多条线
  • 两端都是 OVS 时必须开 LACP:balance-slb 不和对端协商,两边各自挑 active member 可能挑不到一对,导致广播被丢、时通时不通
  • LACP 的核心价值是确认对端配置正确、两端状态一致,防止静默环路和上面那种竞态
  • 链路恢复后要等重新平衡,立刻打流量可能丢包
  • VLAN、QoS 配在 bond 这个 Port 上,对所有成员统一生效

下一章:实验 12:端口镜像与流量采样,把流量复制一份出来看。