Bonding 双活模式:Balance-tlb 与 Balance-alb 技术分析
一、概述
1. 背景
在上一篇文章中讨论了 active-backup 模式,能够在一张网卡出现故障时快速切换,配置简单。但这种方式存在明显缺点——虽然有两张网卡,但同一时间只能使用一张,只有当主网卡挂了才会使用备网卡。
2. 核心问题
在高可用设计中,双活优于主备,主备意味着浪费。用户希望实现两张网卡都在工作,当其中一张网卡挂了时,退回到单网卡模式。
3. 解决方案
Linux Bonding 驱动提供了两种双活模式:
- Balance-tlb(Adaptive transmit load balancing,模式 5):发送负载均衡
- Balance-alb(Adaptive load balancing,模式 6):收发双向负载均衡
二、Balance-tlb 模式
1. 工作原理
A. 发送方向
假设两张网卡都要发送数据,需要解决 MAC flapping 问题——如果同一个 MAC 地址一会从端口 1 发出,一会从端口 2 发出,交换机通过收到的包学习 MAC 地址会感到困惑。
因此,两个 interface 必须使用不同的 MAC 地址。对于发送流量,可以使用两个 interface 来发送,交换机看起来就像是两个不同的主机。
B. 接收方向
对于接收流量,只能用一个 interface 来接收。实现方式是:在回应 ARP 请求时,总是使用其中一个 interface 的 MAC 地址来回复。
graph LR
subgraph 服务器
B0[bond0<br/>IP: 192.168.1.10<br/>主MAC: 1a:50:9c:11:58:a3]
E0[eth0<br/>MAC: 1a:50:9c:11:58:a3<br/>主接口: 收+发]
E1[eth1<br/>MAC: 9e:fa:f1:b5:0c:28<br/>备接口: 仅发]
B0 --> E0
B0 --> E1
end
C1[客户端1] -->|目标MAC: 1a:50| SW[交换机]
C2[客户端2] -->|目标MAC: 1a:50| SW
SW --> E0
SW --> E1
E0 -->|源MAC: 1a:50| SW
E1 -->|源MAC: 9e:fa| SW
SW --> C1
SW --> C22. 流量特征
对于发送流量:
- 使用两个 interface 发送数据
- 通过 hash 机制保证同一个 TCP 流总是走同一个 interface,避免 reorder 问题
- 实际上会根据两张卡的负载动态调整使用哪张卡
对于接收流量:
- 由于其他客户端只知道 IP 对应一个网卡的 MAC 地址,入流量总是走到其中一个 interface 上
- 从交换机视角看,有一个主机的 MAC 地址只会发出去包,但从来没有其他人发给这个 MAC
3. 适用场景
虽然只是发送流量可以使用两张卡,但对于面向客户的服务器来说:
- 进入服务器的流量较少(请求一般比较小)
- 从服务器出去的流量较多(响应一般很大)
对于短视频服务器、流媒体、HTTP 服务器等,这种方案可以有效解决性能问题。
三、Balance-tlb 故障切换流程
1. 角色定义
- 主接口:接收流量的 interface
- 备接口:不接受流量、只发送的 interface
2. 故障场景
A. 备接口故障
- 接收端不受影响
- 发送端切换到只用主接口发送
B. 主接口故障
- 发送端切换到只使用备接口发送
- 接收端流量也要切换到备接口
3. 快速切换机制
接收端流量的切换需要让发送流量的地方来切换。直观思路是切换 ARP,但 ARP 太慢,在 ARP 更新到发送者之前,流量会因发送到挂掉的 MAC 地址而被丢弃。
更好的解决办法是让交换机来切换:
- 发送者的流量发送给交换机,交换机再发送给服务器
- 客户端的目标 MAC 地址不能变,但交换机转发的端口改变
- 这与 active-backup 中讨论的 Gratuitous ARP 类似
4. MAC 地址交换机制
Balance-tlb 模式需要硬件支持改写 MAC 地址。切换过程需要保持主 MAC 地址总是可用,以不影响 ARP 缓存,实现快速切换:
sequenceDiagram
participant C as 客户端
participant SW as 交换机
participant E0 as eth0<br/>(主: 1a:50)
participant E1 as eth1<br/>(备: 9e:fa)
Note over C,E1: 正常状态
C->>SW: 发送至 MAC: 1a:50
SW->>E0: 从端口1转发
E0->>SW: 发送 (源MAC: 1a:50)
E1->>SW: 发送 (源MAC: 9e:fa)
Note over C,E1: 主接口故障
E0--xE0: 故障
E1->>E1: MAC交换<br/>eth1: 1a:50
E1->>SW: GARP (1a:50在端口2)
SW->>SW: 更新MAC表<br/>1a:50 → 端口2
Note over C,E1: 切换完成
C->>SW: 发送至 MAC: 1a:50
SW->>E1: 从端口2转发
E1->>SW: 发送 (源MAC: 1a:50)切换步骤:
- 交换主和备的 MAC 地址,主 MAC 地址迁移到备上,继续存活
- 通过备 interface(现在的主)发送一个 GARP,让交换机更新 MAC address table
4. 配置示例
ip link add bond0 type bond mode balance-tlb
ip link set eth0 down
ip link set eth1 down
ip link set eth0 master bond0
ip link set eth1 master bond0
ip link set bond0 up
ip addr add 192.168.1.10/24 dev bond05. 验证配置
查看网卡信息,bond0 interface 的 MAC 地址和其中一个 slave eth0 一样:
root@ubuntu-5:~$ ip link show
2: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT
link/ether 1a:50:9c:11:58:a3 brd ff:ff:ff:ff:ff:ff
35: eth0: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc fq_codel master bond0 state UNKNOWN
link/ether 1a:50:9c:11:58:a3 brd ff:ff:ff:ff:ff:ff
36: eth1: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc fq_codel master bond0 state UNKNOWN
link/ether 9e:fa:f1:b5:0c:28 brd ff:ff:ff:ff:ff:ff其他客户端查询 192.168.1.10 对应的 MAC 地址,获取到的是主 MAC 地址:
root@ubuntu-4:~$ arp -a | grep 192.168.1.10
? (192.168.1.10) at 1a:50:9c:11:58:a3 [ether] on eth06. 故障切换验证
在 ping 的同时手动 down 掉 eth0,可以看到 1a:50:9c:11:58:a3 这个地址迁移到了 eth1 上:
root@ubuntu-5:~$ ip link set eth0 down
root@ubuntu-5:~$ ip link show
35: eth0: <BROADCAST,MULTICAST,SLAVE> mtu 1500 qdisc fq_codel master bond0 state DOWN
link/ether 9e:fa:f1:b5:0c:28 brd ff:ff:ff:ff:ff:ff
36: eth1: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc fq_codel master bond0 state UNKNOWN
link/ether 1a:50:9c:11:58:a3 brd ff:ff:ff:ff:ff:ff与此同时,ping 也没有失败。
四、Balance-alb 模式
1. 工作原理
Balance-tlb 只在发送方向使用双卡,能否在接收方向也用上两张卡呢?这涉及到发送者的行为,因为作为流量接收方,有两个 MAC 地址,对于让流量发送到哪一个 interface 来说无能为力。
发送者通过 ARP 决定发给哪一个 MAC。解决方案是 ARP negotiation:
- 有两个 interface,分别有两个 MAC 地址
- 希望这两个 interface 各收到 50% 的流量
- 在响应 ARP 时,有时告诉客户端这个 IP 对应 MAC AA,有时说是 MAC BB
graph LR
subgraph 服务器
B0[bond0<br/>IP: 192.168.1.10]
E0[eth0<br/>MAC: 1a:50:9c:11:58:a3]
E1[eth1<br/>MAC: 9e:fa:f1:b5:0c:28]
B0 --> E0
B0 --> E1
end
C1[客户端1] -->|ARP Query| B0
B0 -->|ARP Reply<br/>MAC: 1a:50| C1
C1 -->|Data to 1a:50| SW[交换机]
SW --> E0
C2[客户端2] -->|ARP Query| B0
B0 -->|ARP Reply<br/>MAC: 9e:fa| C2
C2 -->|Data to 9e:fa| SW
SW --> E12. 技术难点
A. 客户端一致性
对于相同的客户端,要给出相同的 MAC 地址答案。不能让客户端一会发送到 MAC AA,一会又发送到 MAC BB,这样会造成 TCP 乱序影响性能。
解决方法:追踪回复给不同客户端的 MAC 地址,后续对于此客户端都回复一样的 MAC 地址。
B. ARP 学习问题
主机不仅通过发送 ARP 请求获取 IP-MAC 对应关系,还会从收到的 ARP 请求中学习:
- 当主机 Z 收到 ARP 请求时,不管有没有这个地址,都会从请求中学习到 IP 对应的 MAC 地址
- 并缓存在自己的 ARP 缓存中
1a:50:9c:11:58:a3 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 58:
Request who-has 192.168.1.14 tell 192.168.1.10这样做的目的是减少 ARP 查询,但这对 balance-alb 来说很糟糕:好不容易骗一半客户端认为 MAC 是 AA,一半认为是 BB,一旦发送 ARP 请求,所有人都知道了同一个 MAC 地址。
C. ARP 广播问题
ARP 请求是发送一个广播包给交换机,交换机帮助广播给所有客户端。对于一个 ARP 请求,发出去的只有一个包,无法对不同客户端区别对待。
3. GARP 解决方案
ARP 请求刷新所有人缓存无法避免,但可以在刷新之后,重新修正部分客户端的 ARP 缓存:
sequenceDiagram
participant S as 服务器
participant SW as 交换机
participant C1 as 客户端1<br/>(缓存: 1a:50)
participant C2 as 客户端2<br/>(缓存: 9e:fa)
Note over S,C2: 正常状态
C1->>S: 发送到 1a:50
C2->>S: 发送到 9e:fa
Note over S,C2: 服务器发送ARP请求
S->>SW: ARP Request<br/>(源MAC: 1a:50)
SW->>C1: 广播ARP
SW->>C2: 广播ARP
C1->>C1: 学习: 192.168.1.10 = 1a:50
C2->>C2: 学习: 192.168.1.10 = 1a:50<br/>缓存被覆盖!
Note over S,C2: 服务器发送GARP修复
S->>C1: GARP (Unicast)<br/>192.168.1.10 = 1a:50
S->>C2: GARP (Unicast)<br/>192.168.1.10 = 9e:fa
C2->>C2: 更新缓存: 192.168.1.10 = 9e:fa解决步骤:
- 在发送 ARP 请求收到回复之后,对所有人发送一个 Gratuitous ARP
- 使用不同的 MAC 地址告知这些客户端,重新对客户端分流
Gratuitous ARP 不仅可以是问题,也可以是 reply。当其他主机收到这个 ARP reply 时,会更新自己的 ARP 缓存。
4. 配置示例
配置命令与 balance-tlb 几乎一样,只有 mode 改成 balance-alb:
ip link add bond0 type bond mode balance-alb
ip link set eth0 down
ip link set eth1 down
ip link set eth0 master bond0
ip link set eth1 master bond0
ip link set bond0 up
ip addr add 192.168.1.10/24 dev bond05. 验证 ARP 分流
配置后,在不同的机器上查看 ARP 记录,可以看到相同的 IP 有不同的 MAC 地址:
# ubuntu-3
root@ubuntu-3:~$ arp -a
? (192.168.1.10) at 9e:fa:f1:b5:0c:28 [ether] on eth0
# ubuntu-4
root@ubuntu-4:~$ arp -a
? (192.168.1.10) at 1a:50:9c:11:58:a3 [ether] on eth0在服务器上发送 ping 包,并在客户端抓包(只抓 in 方向的包):
# ubuntu-3 抓包结果
09:40:08.639278 ARP, Request who-has 192.168.1.14 tell 192.168.1.10, length 44
09:40:10.746098 ARP, Reply 192.168.1.10 is-at 9e:fa:f1:b5:0c:28, length 28
09:40:10.746125 ARP, Reply 192.168.1.10 is-at 9e:fa:f1:b5:0c:28, length 28
# ubuntu-4 抓包结果
09:40:08.639326 ARP, Request who-has 192.168.1.14 tell 192.168.1.10, length 44
09:40:10.746289 ARP, Reply 192.168.1.10 is-at 1a:50:9c:11:58:a3, length 28
09:40:10.746300 ARP, Reply 192.168.1.10 is-at 1a:50:9c:11:58:a3, length 28所有人都收到了 ARP 广播请求,随后又收到了一个 ARP reply,但 reply 中 IP 对应的 MAC 地址是不同的。
6. 故障切换流程
Balance-alb 的 failover 过程和 balance-tlb 基本一样,但现在无论哪一个网卡挂了,都必须要更新所有人的 ARP 缓存。
Failover 流程:
- 如果挂的是主,交换两张卡的 MAC 地址;如果是备,不需要
- 发送 GARP 给所有人(Unicast),告诉所有人自己的 IP 对应的 MAC(当然是没挂的那一个)
将 link set down 并且在其他机器上抓包,会看到有多个 GARP 包发过来:
root@ubuntu-4:~$ tcpdump -i eth0 -n arp -Q in
10:04:16.689841 ARP, Reply 192.168.1.10 is-at 1a:50:9c:11:58:a3, length 28
10:04:16.690286 ARP, Reply 192.168.1.10 is-at 1a:50:9c:11:58:a3, length 28
10:04:16.690720 ARP, Reply 192.168.1.10 is-at 1a:50:9c:11:58:a3, length 28五、三种 Bonding 模式对比
1. 模式总结
| 模式 | 实现复杂度 | 切换速度 | 网卡利用率 | 硬件要求 |
|---|---|---|---|---|
| active-backup | 简单 | 快速 | 单卡 | 无特殊要求 |
| balance-tlb | 简单 | 快速 | 发送双卡 | 需支持 MAC 地址修改 |
| balance-alb | 简单 | 较慢 | 收发双卡 | 需支持 MAC 地址修改 |
2. 各自特点
A. Active-backup(模式 1)
- 实现简单,快速切换
- 只能用单卡,资源浪费
B. Balance-tlb(模式 5)
- 实现简单,快速切换
- 发送能用双卡,接收单卡
- 需要硬件支持 MAC 地址修改
- 适合出流量大的场景(如流媒体、HTTP 服务)
C. Balance-alb(模式 6)
- 实现简单,切换较慢
- 收发双卡,充分利用带宽
- 涉及 GARP 刷新所有客户端缓存
- 需要硬件支持 MAC 地址修改
3. 扩展性
这三种方案都是用两张卡讨论的,实际上都可以加更多的卡,工作原理完全一样。
4. 局限性
这些方案都有缺点,都不是数据中心在用的方案,但离"完美"的方案越来越近了。数据中心的实际方案会在后续文章中讨论。