Linux 网络高级配置实战:Bonding、RAID 卡、故障诊断与虚拟交换机
Bonding、RAID、网桥其实是同一个抽象:用冗余换可用性,同时把"故障检测"挪进了软件。miimon 这类参数决定的不是会不会断,而是断多久。
一台服务器插着两根网线,你说"断一根没事"——这句话只有在你真的拔过一根并计时之后才成立。因为冗余的关键从来不在"有没有备用路径",而在主路径失效到备用路径接管之间的那段时间;而这段时间不是硬件给的,是软件参数给的。
这篇文章要解决的问题是:如何在 Linux 上把多块物理网卡聚合成一个逻辑接口(bond),再在这个聚合接口之上承载多个 VLAN 的 trunk 流量,并且在出故障时知道它断了多久、断在哪一层。背后最基础的事实是:Bonding 的聚合发生在内核二层(MAC 层)——它把多块物理网卡"接管"成一张虚拟网卡 net_device,链路层的故障检测、负载分担,以及 VLAN 标签的插入与剥离,全部在这一层完成;IP 层只看到这张虚拟网卡,对它而言"主备切换"和"两条物理链路"是不存在的。先把这个层次关系看清楚,后面所有配置、验证和排障才有坐标。
#原理层:bond 在内核里到底做了什么
#从一块网卡出发:数据包怎么走到 IP 层
先建立基准。一块普通物理网卡在内核里对应一个 struct net_device,它既代表硬件,也代表一层收发的边界。数据到达时,典型的路径是:
- 网卡 DMA 把帧写入 ring buffer,触发中断(或 NAPI 轮询);
- 驱动把缓冲区包成
sk_buff(skb),调用收包入口交给协议栈; - 协议栈先做二层处理(目的 MAC 是否是本机、是否是广播/组播),再按 EtherType 分发——IPv4 交给 IP 层、ARP 交给 ARP 层、802.1Q 交给 VLAN 处理;
- IP 层按路由表决定这些包属于哪台主机、哪个会话,最终交给 TCP/UDP 或上层应用。
到这里会注意到:IP 层面对的单位是"一个 net_device",而不是"一根网线"。发送方向也是一样,IP 层选择出口设备,然后把这个设备交给链路层去完成封装。Bonding 利用的正是这个抽象边界:IP 层不关心出口设备背后是几块网卡,只要交给它的这个 net_device 能收发即可。
#bond 是一张"虚拟网卡",成员网卡被接管
Bonding 驱动做的事,可以概括为三步:
- 注册一个新的 net_device(如
bond0),它没有自己的硬件,只有一组参数和状态; - 把物理网卡(slave,成员)"接管"进来:挂上
IFF_SLAVE标志,把成员网卡的 MAC 统一成 bond 的 MAC(由第一个活跃成员继承或显式指定),此后成员网卡不再作为独立可寻址设备参与网络栈; - 在收、发两条路径上接管流量:接收时,成员网卡收到的帧统一走 bond 的接收处理(收包看到的是 bond0);发送时,bond 按所选模式调度的
bond_xmit选择某一块成员网卡真正把帧发出去。
用一句话记:bond0 是 IP 层看到的"接口",成员网卡只是它背后的"出口集合"。 物理网卡 ⇄ bond0 ⇄ IP 层,这个层次是最重要的心智模型。
顺带一提,Linux 的 team(libteam/teamd)解决的是同一类问题,只是把调度策略搬到了用户态守护进程 teamd 里实现,内核只保留一个通用的 team 设备。概念上两者等价:都是"多个物理口聚合成一个逻辑接口"。
#为什么 bond 的成员网卡不能配 IP
这是一个常被误解的点,用上面的层次模型可以直接推导出来。
第一,成员网卡被接管后不再是独立设备。IFF_SLAVE 标志意味着内核把它从常规的 IP 收发路径中摘除,收包被移交 bond 处理,发包由 bond 统一调度。你在成员口上配 IP,等于对着一张"已经失去独立身份"的网卡做 IP 层配置——它本应是 bond 的端口,而不是主机的一个地址入口。
第二,MAC 地址已经统一。成员网卡 enslave 后,硬件地址被设成 bond 的 MAC(cat /proc/net/bonding/bond0 里能看到 Permanent HW addr 是出厂地址,而 bond 使用的是统一 MAC)。如果成员口再配一个独立 IP,就会出现"同一块物理口、同一个 MAC、却挂着两个地址语义"的混乱。二层交换机的 MAC 表、ARP 邻居表都将无所适从:同一个 MAC 应该从哪个口出去、应该应答哪个 IP,结果变成"时通时不通"。
第三,IP 层的路由与 ARP 语义要求"地址 ⇄ 出口设备"一一对应。Linux 在做源地址选择和 ARP 应答时都绑定到具体出口设备;同一个子网里出现 bond0 和 eno1 两个接口(且 eno1 是 bond 的成员),就触发了内核的多网卡同网段保护逻辑(arp_filter、arp_announce 等),最典型的现象是:ping 问 eno1 上的 IP,应答却从 bond0 发出去,或者干脆不应答。
所以实践结论是硬的:成员网卡不配 IP,IP 只配在 bond0 或 bond0 之上的 VLAN 子接口上。 发行版工具也按这个约定工作——NetworkManager 创建 slave 连接时默认 ipv4.method=disabled,旧式 ifcfg 里 SLAVE=yes 的接口即使写了 IPADDR 也会被忽略。不要手工往 slave 上 ip addr add,就算命令能执行,得到的也是不可预期的行为。
#三种常用模式:mode 0 / mode 1 / mode 4 各自的机制与依赖
Bonding 共 7 种模式(0–6),但本质上它们只在回答两个不同的问题:"我要断一根线不断网",还是"我要把多根线的带宽合并使用"。 这两种目标的机制和依赖完全不同,混在一起写需求,通常只会配出一个两边都不理想的东西。
| 模式 | 名称 | 机制 | 需要交换机配合吗 |
|---|---|---|---|
| 0 | balance-rr | 逐包轮询在成员间轮流发送 | 建议静态聚合,否则回程可能集中到单口并乱序 |
| 1 | active-backup | 主备,同一时刻只有一块成员转发 | 不需要,两个口放同一 VLAN 即可 |
| 2 | balance-xor | 按 XOR 哈希选成员发送 | 建议静态聚合或 LACP |
| 3 | broadcast | 所有成员同时发送同一帧 | 通常不需要 |
| 4 | 802.3ad | 与交换机 LACP 动态协商聚合 | 必须,两端都要配 LACP |
| 5 | balance-tlb | 发送侧自适应负载均衡(按成员负载调整) | 不需要 |
| 6 | balance-alb | tlb + 接收侧 ARP 协商负载均衡 | 不需要 |
下面把最常用的 0、1、4 讲透。
mode 1(active-backup)——只要冗余。 机制是:同一时刻只有一块成员网卡(active slave)承担收发,其余成员 standby;当 active 链路被判定故障,bond 立即把收发切换到下一块。它不需要交换机做任何聚合配置(交换机只看到一块"网卡"在活动),所以是所有模式里最容易验证、最不容易被交换机坑到的。它可以配 primary 指定首选成员,例如让 eno1 优先,eno1 恢复后自动切回。故障检测有两套手段:miimon(监测链路载波/状态,快,简单)和 arp_interval + arp_ip_target(定期向网关等目标发 ARP 探测,用于"链路 up 但网络不通"的场景,例如中间接了交换机且对端故障),两者互斥,不能同时开。
mode 4(802.3ad,LACP)——要带宽叠加,但要付出配置代价。 机制是:bond 与交换机通过 LACP(IEEE 802.3ad/802.1AX)协议协商,动态确认两端都认可同一个聚合组后,成员口才真正进入转发。两端各有一个"actor"和一个"partner":Linux 是 actor,交换机是 partner,协商成功会形成聚合组(Aggregator),交换机会按组的哈希把回程帧分散到各成员口。代价有两条,必须提前接受:
- 交换机侧必须预先配置 LACP。 一边配了、一边没配,会出现很难查的连通性异常——因为两端对"这是个聚合组"的理解不一致(详见下文"半边通")。
- 带宽叠加不等于单连接变快。 802.3ad 和 balance-xor 一样,按哈希算法逐流分配,单个 TCP 连接只会走一条成员链路(
xmit_hash_policy决定哈希因子:layer2用 MAC、layer2+3加 IP、layer3+4再加端口)。想靠 Bonding 让一个 SCP 下载变快,基本不会成功;能变快的是"很多个并发连接加起来的总吞吐"。
mode 0(balance-rr)——特例。 它逐包轮流发送,同一 TCP 流的包也会分散到不同链路,属于"发送侧真·叠加";但接收侧由交换机决定回程走哪个口,而交换机又把同一目的 MAC 的帧大都发到同一个口上,于是极易乱序,TCP 会因乱序触发大量重传,实测经常比单口还慢。它适用于极少数对吞吐要求苛刻、且流量本身可容忍乱序的场景(或配合对端做严格静态聚合时谨慎使用)。绝大多数生产环境,可靠性选 mode 1,带宽选 mode 4,mode 0 放在"知道它为什么慢"的前提下再考虑。
#切换速度由参数决定:miimon / updelay / downdelay / arp_interval
这是全篇最实用的一节。miimon、updelay、downdelay 这三个参数决定的不是"会不会切换",而是切换的速度与稳定性:
miimon=100 每 100ms 检查一次链路(默认量级)
updelay=... 成员恢复后,等多久才启用它
downdelay=... 链路异常后,等多久才判它坏了
arp_interval=1000 每 1000ms 发一次 ARP 探测(与 miimon 互斥)
arp_ip_target=192.0.2.1 探测的目标地址,可多个它们之间存在一个必须理解的取舍:
miimon太小 → 链路抖动(比如交换机做 STP 收敛、光模块瞬断)会被判成故障,切换过于频繁,反而制造中断;miimon太大 → 真故障时切换慢,中断时间变长;updelay太短 → 成员刚"看起来好了"就被启用,可能马上又坏,形成来回抖动(flapping)。
所以判断标准不是"哪个值更安全",而是:这台机器能容忍多长的中断,和它的链路有多爱抖。 对波动大的链路,宁可切换慢一点也别来回跳。miimon 是个参数,但选它是选择"更怕中断还是更怕抖动"——这正是工程选择与价值判断混在一起的典型例子。
还有一个最容易被忽略的观测点,来自 /proc 状态文件:
# 看当前 bond 状态(哪块是 active、切换统计)
cat /proc/net/bonding/bond0关键几行:
Currently Active Slave: eno1
MII Status: up
Link Failure Count: 2 # 切换过几次,静默发生的故障在这里Link Failure Count 是一本"沉默的故障账本"。如果你的告警从来没有触发过,但这个计数却在涨,说明发生过短暂中断且没人知道。巡检查看它是很有价值的习惯。
#VLAN trunk:tag 是怎么打到 bond 之上的
VLAN 的机制是 802.1Q:在以太网帧的源 MAC 之后插入 4 字节标签,其中 12 bit 是 VLAN ID(VID),交换机用这个标签区分同一物理链路上的多个虚拟网络。所谓 trunk 口,就是允许带标签帧通过、并在出入口按需打标/去标的端口。
关键问题:在 bond 上做 trunk,标签该加在哪一层?
答案是加在 bond0 之上,而不是成员网卡上。层次关系是:
物理口 eno1 ─┐
├─ bond0(二层聚合,无标签逻辑)── bond0.10(VID 10,IP 入口)
物理口 eno2 ─┘ ├─ bond0.20(VID 20,IP 入口)
└─ bond0(本体,可理解为 untagged/native)802.1Q 的 VLAN 子接口(bond0.10 这种)是 bond0 的上层虚拟设备:内核加载 8021q 模块后,ip link add link bond0 name bond0.10 type vlan id 10 会创建一个 net_device,它的收包路径负责剥离 VID 10 的标签,发包路径负责给帧打上 VID 10 的标签,其余逻辑(成员选择、主备切换)全部由下层 bond0 完成。所以:
- 先有 bond,再有 VLAN:聚合是底层的,tag 是上层的;
- IP 只配在 bond0.10 / bond0.20 上(或配在 bond0 本体作为 native VLAN 的管理地址),成员网卡永远只承载"聚合后的裸二层帧";
- 一条物理聚合链路(两根线)因此可以同时承载多个 VLAN 的流量,这就是"bond 之上的 trunk"。
理解了这条因果链,配置就只是把上面的层次关系逐层落下来。
#操作层:完整可复现的配置
#前置条件与目标拓扑
下面以最常见的两种发行版家族为例:
- RHEL 系(RHEL 8/9、Rocky Linux、AlmaLinux、Fedora):以 NetworkManager + nmcli 为主要方案,先确认工具存在(发行版未装时
dnf install NetworkManager并systemctl enable --now NetworkManager)。 - Debian 系(Debian 11/12、Ubuntu 20.04/22.04/24.04):以 ifupdown(/etc/network/interfaces)为例;使用 bond 需要
ifenslave包,使用 VLAN 需要vlan包(或直接使用 ifupdown2)。 - 若用传统 ifcfg 方式(RHEL 8 及更早),单独给出一节;RHEL 9 已移除 network-scripts(ifcfg),只支持 NetworkManager,这一点请先确认再选方案。
目标拓扑(示例,全部使用 RFC 5737 文档地址,请替换为你的实际网段):
- 两块物理网卡
eno1、eno2(名字以ip link实际输出为准); - 聚合为
bond0,模式按需求选 active-backup(1)或 802.3ad(4); bond0本体配管理地址192.0.2.10/24(native,交换机放行 untagged);bond0.10配198.51.100.5/24、bond0.20配203.0.113.5/24(tagged,交换机 trunk 放行 VID 10、20);- 交换机把接
eno1/eno2的两个口做成聚合口,mode 4 时启用 LACP,并把聚合口配为 trunk。
#方案 A:RHEL 系 + NetworkManager(nmcli)
在 NetworkManager 里,bond 与 slave 都是独立的连接(connection),用 master 关联。一步步来。
1) 确认环境与网卡名
nmcli -v
ip link show
ip -br link2) 创建 bond(以 802.3ad / mode 4 + layer3+4 哈希为例)
nmcli con add type bond con-name bond0 ifname bond0 mode 802.3ad miimon 100
nmcli con mod bond0 +bond.options "xmit_hash_policy=layer3+4,updelay=0,downdelay=0"
nmcli con add type ethernet con-name bond0-port1 ifname eno1 master bond0
nmcli con add type ethernet con-name bond0-port2 ifname eno2 master bond0
nmcli con up bond0说明:con-name 是 NetworkManager 连接名,ifname 是内核设备名;slave 连接用 master bond0 挂接。此时 bond0 还没有 IP。
3) 给 bond0 配 IP(native/管理地址)
nmcli con mod bond0 ipv4.addresses 192.0.2.10/24
nmcli con mod bond0 ipv4.gateway 192.0.2.1
nmcli con mod bond0 ipv4.method manual
nmcli con up bond04) 在 bond 上创建 VLAN 子接口并配 IP
nmcli con add type vlan con-name bond0.10 ifname bond0.10 dev bond0 id 10
nmcli con mod bond0.10 ipv4.addresses 198.51.100.5/24
nmcli con mod bond0.10 ipv4.method manual
nmcli con up bond0.10
nmcli con add type vlan con-name bond0.20 ifname bond0.20 dev bond0 id 20
nmcli con mod bond0.20 ipv4.addresses 203.0.113.5/24
nmcli con mod bond0.20 ipv4.method manual
nmcli con up bond0.205) 只要冗余、不要带宽:把模式改成 active-backup(mode 1)
上面的流程只需要把第一步的 mode 802.3ad 换成 mode active-backup,其余不变:
nmcli con delete bond0 bond0-port1 bond0-port2 # 若已按 mode 4 创建过,先清掉重来
nmcli con add type bond con-name bond0 ifname bond0 mode active-backup miimon 100
nmcli con add type ethernet con-name bond0-port1 ifname eno1 master bond0
nmcli con add type ethernet con-name bond0-port2 ifname eno2 master bond0
nmcli con mod bond0 ipv4.addresses 192.0.2.10/24
nmcli con mod bond0 ipv4.method manual
nmcli con up bond0 bond0-port1 bond0-port2mode 1 也可以指定首选成员和 ARP 探测(注意与 miimon 互斥):
nmcli con mod bond0 +bond.options "primary=eno1"
# 若改用 ARP 探测(同时去掉 miimon):
nmcli con mod bond0 bond.options "mode=active-backup,primary=eno1,arp_interval=1000,arp_ip_target=192.0.2.1"
nmcli con down bond0 && nmcli con up bond06) 清理
删除连接即删除设备:
nmcli con delete bond0.20 bond0.10 bond0-port2 bond0-port1 bond0#方案 B:Debian / Ubuntu(/etc/network/interfaces)
先装依赖包:
apt-get update && apt-get install -y ifenslave vlan
modprobe bonding
modprobe 8021q
grep -q bonding /etc/modules || echo bonding >> /etc/modules
grep -q 8021q /etc/modules || echo 8021q >> /etc/modules编辑 /etc/network/interfaces(以 mode 4 + 一个 VLAN 子接口为例):
# 成员网卡:只声明属于 bond0,不配 IP
auto eno1
iface eno1 inet manual
bond-master bond0
auto eno2
iface eno2 inet manual
bond-master bond0
# bond 本体:native(untagged)管理地址
auto bond0
iface bond0 inet static
address 192.0.2.10
netmask 255.255.255.0
gateway 192.0.2.1
bond-slaves eno1 eno2
bond-mode 802.3ad
bond-miimon 100
bond-xmit-hash-policy layer3+4
bond-updelay 0
bond-downdelay 0
# VLAN 子接口:VID 10,tagged
auto bond0.10
iface bond0.10 inet static
address 198.51.100.5
netmask 255.255.255.0
vlan-raw-device bond0说明:bond-mode 也可写数字(bond-mode 4);mode 1 时写 bond-mode active-backup 并去掉 bond-xmit-hash-policy。ifupdown 会自动按依赖顺序先起 bond0 再起 bond0.10。如果使用 netplan(Ubuntu 22.04+ 部分环境),等价的声明是给两块物理网卡配 bond 引用、给 bond0 配 addresses,再在其上声明 vlan-id——这里以 ifupdown 为准,netplan 请按你安装版本的参考文档核对。
应用配置:
systemctl restart networking # 或 ifdown -a && ifup -a#方案 C:RHEL 系传统 ifcfg(RHEL 8 及更早)
RHEL 8 及更早年份的网络脚本(network-scripts)用 ifcfg 文件描述接口。先确认在跑传统网络服务而非 NetworkManager,然后写四个文件:
/etc/sysconfig/network-scripts/ifcfg-bond0:
DEVICE=bond0
NAME=bond0
TYPE=Bond
BONDING_MASTER=yes
ONBOOT=yes
BOOTPROTO=none
IPADDR=192.0.2.10
PREFIX=24
GATEWAY=192.0.2.1
BONDING_OPTS="mode=802.3ad miimon=100 xmit_hash_policy=layer3+4"/etc/sysconfig/network-scripts/ifcfg-eno1:
DEVICE=eno1
NAME=eno1
TYPE=Ethernet
ONBOOT=yes
BOOTPROTO=none
MASTER=bond0
SLAVE=yes/etc/sysconfig/network-scripts/ifcfg-eno2:同上,仅把 DEVICE/NAME 换成 eno2。
/etc/sysconfig/network-scripts/ifcfg-bond0.10:
DEVICE=bond0.10
NAME=bond0.10
VLAN=yes
ONBOOT=yes
BOOTPROTO=none
IPADDR=198.51.100.5
PREFIX=24应用:
systemctl restart network重申:RHEL 9 已经移除 network-scripts,此方案只适用于 RHEL 8 及更早;在这些系统上,也需要先 modprobe bonding 确认模块可用。
#交换机侧的配合
Linux 侧配完只是半边,交换机侧必须与 bond 模式对齐。分三种情况:
mode 1(active-backup): 交换机不需要任何聚合配置,只要让两个口都 access 到同一个 VLAN 即可。这是因为任一时刻只有一个成员在转发,交换机只会从其中一个口学到 MAC,不存在"两个口同时转发同一 MAC"的问题。
mode 4(802.3ad): 必须预先在两个口上建立 LACP 聚合口,且 trunk 配置(放行哪些 VLAN)要配在聚合口上,而不是分别配在每个物理口上。以 Cisco IOS 为例:
interface Port-channel1
switchport mode trunk
switchport trunk allowed vlan add 10,20
!
interface GigabitEthernet0/1
channel-group 1 mode active
!
interface GigabitEthernet0/2
channel-group 1 mode active以华为 VRP 系交换机(S 系列/CE 系列命令行风格)为例:
interface Eth-Trunk1
mode lacp-static
port link-type trunk
port trunk allow-pass vlan 10 20
#
interface GigabitEthernet0/0/1
eth-trunk 1
#
interface GigabitEthernet0/0/2
eth-trunk 1(具体语法以你设备版本的官方文档为准;关键语义是:LACP 模式对应 Linux 的 802.3ad,聚合口上放行带 tag 的 VLAN。)
mode 0/2(balance-rr / balance-xor,不协商 LACP): 为避免回程集中在单口导致性能与乱序问题,可以把交换机两个口做成静态聚合(Cisco channel-group 1 mode on;华为 Eth-Trunk 默认手工模式),此时两端不做 LACP 协商也能聚合转发,但链路状态变化时交换机的收敛依赖静态配置,不会自动重协商。
#team 作为另一种实现
如果并非必须用 bonding,RHEL 7/8 时代曾推荐 team(libteam/teamd)替代 bonding,原因是其 JSON 配置更易理解、含 lacp 等 runner。一张对照表:
| 需求 | bonding 模式 | team runner |
|---|---|---|
| 主备 | 1(active-backup) | activebackup |
| 轮询 | 0(balance-rr) | roundrobin |
| 负载均衡(哈希) | 2(balance-xor) | loadbalance |
| LACP | 4(802.3ad) | lacp |
NetworkManager 下创建 team 的等价命令(以 activebackup 为例):
nmcli con add type team con-name team0 ifname team0 config '{"runner":{"name":"activebackup"}}'
nmcli con add type team-slave con-name team0-port1 ifname eno1 master team0
nmcli con add type team-slave con-name team0-port2 ifname eno2 master team0
nmcli con up team0查看 team 状态用 teamdctl team0 state view。但要注意:RHEL 9 已弃用 team,官方推荐回归 bonding;Debian 系仍可用 libteam。如果不是存量环境,新部署选择 bonding 更稳妥。
#验证层:怎么确认我做对了
配置完成后,必须用证据确认"聚合真的生效、tag 真的打对、切换真的够快",而不是看几行配置就宣布成功。
#cat /proc/net/bonding/bond0:读懂状态与切换账本
这是 bond 状态的第一手来源。mode 1 的典型输出节选:
Ethernet Channel Bonding Driver: v6.x
Bonding Mode: fault-tolerance (active-backup)
Primary Slave: None
Currently Active Slave: eno1
MII Status: up
MII Polling Interval (ms): 100
Up Delay (ms): 0
Down Delay (ms): 0
Slave Interface: eno1
MII Status: up
Speed: 1000 Mbps
Duplex: full
Link Failure Count: 0
Permanent HW addr: 52:54:00:aa:bb:01
Slave Interface: eno2
MII Status: up
Speed: 1000 Mbps
Duplex: full
Link Failure Count: 1
Permanent HW addr: 52:54:00:aa:bb:02mode 4 的典型输出节选(802.3ad 段):
Bonding Mode: IEEE 802.3ad Dynamic link aggregation
Transmit Hash Policy: layer3+4 (1)
MII Status: up
MII Polling Interval (ms): 100
802.3ad info
LACP rate: slow
Aggregator selection policy (ad_select): stable
System priority: 65535
System MAC address: 52:54:00:aa:bb:01
Active Aggregator Info:
Aggregator ID: 1
Number of ports: 2
Actor Key: 9
Partner Key: 1
Partner Mac Address: 00:1c:73:xx:xx:xx
Slave Interface: eno1
...
Aggregator ID: 1判定标准:
Bonding Mode必须是你配置的模式;- mode 1:
Currently Active Slave只有一块,切换后它会变; - mode 4:
Aggregator ID两块成员一致且非 0、Number of ports: 2、Partner Mac Address是交换机端口的 MAC(不是空/全 0)——这三条同时成立,才说明 LACP 协商成功; Link Failure Count是故障账本,见上文。
#ip -d link 与 /proc/net/vlan:确认层次和设备关系
用 ip -d(detail)可以直接看到设备的类型细节:
ip -d link show bond0
ip -d link show bond0.10典型输出:
6: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 52:54:00:aa:bb:01 brd ff:ff:ff:ff:ff:ff
bond mode 802.3ad miimon 100ms xmit_hash_policy layer3+4
...
7: bond0.10@bond0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 52:54:00:aa:bb:01 brd ff:ff:ff:ff:ff:ff
vlan protocol 802.1Q id 10 <REORDER_HDR>
...注意三点:bond0 显示 MASTER、成员网卡显示 SLAVE;bond0.10@bond0 的 @bond0 表示它的下层设备是 bond;vlan protocol 802.1Q id 10 确认标签类型和 VID。
VLAN 收发计数在 /proc/net/vlan/ 下:
cat /proc/net/vlan/bond0.10它会显示 VID、以及收到的带标签帧总数与发送总数——发数在增长、收数在增长,说明 tag 在这条链路上真的来回走。
#ethtool 与计数器:诊断顺序从证据开始
网络诊断最常见的错误是从"猜"开始(换网线、换交换机端口),而不是从可观测证据开始。推荐的顺序是:
# 1) 物理层与协商(看起来 up 不等于协商正常)
ethtool eno1 | grep -E "Speed|Duplex|Link detected"
# 2) 错误计数器 —— 最直接的证据
ethtool -S eno1 | grep -Ei "err|drop|discard|crc|fifo"
# 3) 网卡与驱动
ethtool -i eno1 # 驱动与固件版本
dmesg | grep -i eno1 # 有没有 link down / reset 记录
# 4) 上层:路由、连接、抓包
ip -s link show eno1 # 内核视角的收发统计与错误
ss -s # 连接状态汇总计数器的解读是这块最值钱的东西:crc_errors 涨说明物理层有问题(线、模块、端口);rx_missed / rx_dropped 涨更像主机侧处理不过来(ring buffer、中断、CPU);tx_dropped 可能指向队列或虚拟化层。计数器把"网络慢"分成了几个完全不同的方向,而猜是分不出来的。 配合 dmesg 里的 link down / link up 记录与 bond 的 Link Failure Count,往往能还原出"什么时候抖过"——这个时间线对判断故障是偶发还是趋势非常关键。
#连通性与故障演练:拔掉一根线,计时
配置验证的最终一步是主动制造故障,而不是等故障发生。
# 后台持续 ping 网关,统计丢包
ping -f 192.0.2.1 &
# 拔掉 eno1 的网线(或 ip link set eno1 down),观察:
# ping 丢了几个包?中断了多少毫秒?
cat /proc/net/bonding/bond0 | grep -A2 "Slave Interface: eno1"
cat /proc/net/bonding/bond0 | grep "Currently Active Slave"
# 插回网线,观察切换恢复与 updelay 的作用预期:mode 1 下,ip link set eno1 down 后 100ms 量级(miimon 决定)内 Currently Active Slave 切到 eno2,ping 丢包通常只有 0–2 个;Link Failure Count 对 eno1 加 1。如果丢包远超预期,去查 miimon 是否过大、交换机端口是否也做了聚合或 STP 收敛期过长。mode 4 下还要在交换机侧确认聚合口没有 down/重协商。
如果需要确认帧真的带了 tag,抓包看二层头:
tcpdump -i bond0.10 -nn -e icmp
# 报文里应看到 vlan 10(802.1Q, QOS 0x0, vlan 10)才能进入该子接口注意:抓 bond0.10 时看到的是剥离后的帧;要看原始带 tag 帧,抓 bond0 本体:tcpdump -i bond0 -nn -e 'vlan 10'。
#边界与坑
#半边通:两端口都 up 但流量不正常
Bonding 出问题最麻烦的形态是"半边通":两张网卡都 up,但流量只走其中一张,或者某些目标通、某些不通。 典型原因有三个:
- LACP 静默模式不一致:一边期望协商、一边没配,最终可能只有一个成员在转发(
/proc/net/bonding/bond0里能看到成员没被聚合)。 - 哈希不一致:两端对流的哈希结果不同,导致回程走了另一条链路;中间再有有状态设备(如防火墙),直接不通。
- ARP/邻居表问题:切换后对端交换机仍缓存着旧端口的 MAC 表项(MAC 表项老化需要时间),表现为"切了但几分钟内不通"。
第三条常被忽略:网络设备的转发表是有记忆的。 二层的收敛时间不只看服务器侧,也要看交换机侧的老化时间。这也是为什么"同一网段切换很快、跨网段切换很慢"有时是真的——中间的设备越多,需要重新学习的表就越多。排障时把交换机侧的状态也纳入检查,不要只盯 Linux。
#负载均衡的边界:单流带宽不叠加
再次强调:802.3ad / balance-xor 按流哈希,单个 TCP 连接只走一条成员链路;想靠 Bonding 让一个 SCP 变快,基本不会成功。balance-rr 是例外(包级轮询),但乱序代价往往更糟。真要让大文件传输变快,思路应是"把传输拆成多个并发连接",或换用支持多路径的上层技术——bond 解决的是链路级可用性与多连接总吞吐,不解决单流带宽。
#链路抖动与参数取舍
miimon 太小会把交换机 STP 收敛期的短暂丢帧误判成链路故障,触发无谓切换;updelay 太短会让刚恢复的成员立刻被启用,形成 flapping。这类问题的典型语境是机房链路本身波动大:宁可把 miimon 放大(如 200–500ms)也别让它来回跳。这是个工程判断:这台机器更怕中断,还是更怕抖动。
#RAID 的同一个道理:沉默的降级与写缓存
RAID 和 Bonding 的相似性比看起来高,值得放在一起理解:
| 相似点 | Bonding | RAID |
|---|---|---|
| 冗余对象 | 链路 | 磁盘 |
| 降级状态 | 单链路运行 | 降级(degraded)运行 |
| 重建是瓶颈 | 不太涉及 | 重建期是性能与风险双高期 |
| 缓存策略改变语义 | 少见 | 写缓存(Write Back)与丢电 |
最后一行是 RAID 独有的要害:开启写缓存能大幅提升写性能,但如果没有电池/超级电容保护(BBU/超级电容),掉电时缓存中的数据会丢。 这不是"风险有多大"的问题,而是"你在性能和一致性之间选了哪一边"的问题——而且很多 RAID 卡的默认策略并不保守,需要显式检查。与 Bonding 呼应的一点是:RAID 卡也会"沉默地降级",阵列从 RAID 10 掉一块盘之后通常还能跑,性能与冗余度却都下降了。状态要主动巡检,不要等它坏了再发现——那时已经晚了。Link Failure Count 与 RAID 告警日志,是同一类"沉默账本"。
#虚拟交换机的两套思维与 offload
Linux Bridge 和 Open vSwitch 都是"交换机",但思维不同:
| Linux Bridge | Open vSwitch | |
|---|---|---|
| 定位 | 内核自带的二层转发 | 可编程的虚拟交换 |
| 配置方式 | ip / bridge 命令,持久化靠发行版 | 数据库(ovsdb)+ 命令行 |
| VLAN 处理 | 支持,但表达力有限 | 显式 tag/trunk 概念,更接近物理交换机 |
| 流表与 ACL | 基本靠 iptables/ebtables 配合 | 原生流表,可做隧道与精细策略 |
| 适合 | 简单网桥、KVM 默认 | 需要多租户、隧道、策略的场景 |
与本文主题相关的坑是:虚拟交换机的性能和"卸载"能力与物理设备不同。 某些 offload 特性(如大包分段、校验和卸载)在经过虚拟交换机后行为会变化,抓包看到的和实际发出的可能不一致(经典的"抓到的包看着不对但通信正常")。抓包不一致时先怀疑卸载,而不是先怀疑网卡坏了。
#发行版差异与工具风险
几点需要明确限定的边界:
- 发行版差异极大:RHEL 系、Debian 系、NetworkManager 与传统网络脚本的配置表达不同;RHEL 9 移除 network-scripts、弃用 team。抄一份模板到处用的误导性大于帮助,先确认发行版版本再选方案。
- RAID 卡厂商工具的风险:
storcli/perccli/MegaCli版本语法差异明显,且用它们改配置是有风险的操作,请以对应工具文档为准,本文不展开。 - 性能调优不脱离场景:中断绑定、RPS/RFS、ring buffer 大小都有效,但全部依赖具体负载与硬件,脱离场景给数值等于让读者替作者试错。
- 再往上一层是 Kubernetes 网络:CNI 有自己的抽象(比如 Bonding 之上的多网卡、IPVLAN 等),但底层仍在用本文的概念——理解二层聚合与 VLAN 的层次,是理解容器网络的前提。
#收尾
冗余的价值不取决于"有备用",而取决于"多快发现主用坏了"。那个"多快"是可配的(miimon、updelay、downdelay、LACP 协商),也是该被计时验证的——配完 bond 和 trunk 之后,顺手拔一根网线,测一次。如果 Link Failure Count 从此以后安静地躺着不动,那说明过去的一段日子网络一直很健康;如果它在某天悄悄涨了一格,那段没人知道的短暂中断,正是这篇配置留下并被记录下来的意义。
相关阅读
Related留言讨论
0 条还没有留言,来说点什么吧。