主题
实验 11:链路聚合
两台交换机之间只有一条线,带宽受限,断了就全断。多拉几条线呢?直接接会形成二层环路,广播风暴瞬间打满。
链路聚合(bond)解决这个问题:把多条物理链路绑成一个逻辑口,对上层来说只有一个端口,不成环;对下层来说流量分摊在多条线上,断一条自动切换。
起环境
bash
cd labs/learn-ovs/08-bond
./start.shtext
两条链路
h1 ── ovs1 ═════════ ovs2 ── h2
eth2,eth3 eth1,eth2
ovs1: eth1 接 h1,eth2/eth3 去 ovs2
ovs2: eth1/eth2 去 ovs1,eth3 接 h2start.sh 只把主机接进了网桥,两条互联线都还没接,所以现在不通:
bash
docker exec clab-bond-h1 ping -c 2 -W 1 192.168.10.2text
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 showtext
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.2text
3 packets transmitted, 3 received, 0% packet loss看 bond 状态
bash
$N1 ovs-appctl bond/show bond0text
---- 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_status | LACP 协商状态,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.2text
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 bond0text
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 distributingactor 是自己,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.shtext
[通过] ovs1 上存在 bond0
[通过] bond0 有两个成员口
[通过] bond 模式是 balance-slb
[通过] h1 能 ping 通 h2
== 断掉一条链路,验证流量自动切到另一条 ==
[通过] 断一条线后仍然连通自测清单
- 为什么不能直接把两条互联线都
add-port进网桥? - bond 在 OVSDB 里是怎么表示的?体现了哪张表的什么设计?
active member承载什么流量?为什么广播不能从所有成员发?balance-slb和balance-tcp的分流依据分别是什么?- 两台服务器之间传大文件,用
balance-slb绑两条线能翻倍吗? - 为什么两端都是 OVS 时不能用
balance-slb?会出现什么症状? - LACP 最大的价值是分流还是别的?
- 两端都配
lacp=passive会怎样?
常见问题
建了 bond 但不通
按顺序查:
- 两端都配了吗 —— 只配一边必然出问题
- 成员口是 up 的吗 ——
ip -br link - 成员口配对了吗 —— 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 bond0partner全是 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:端口镜像与流量采样,把流量复制一份出来看。