CentOS7上做虚拟化,网络规划往往是最容易被低估的一环。很多人觉得只要把物理网卡桥接出来,虚拟机就能上网;等到需要多业务隔离、链路冗余的时候才发现,直接桥接的方式既浪费端口,又扛不住单点故障。这篇内容就是围绕CentOS7虚拟化环境里的两个基础动作——绑定网卡和划分不同VLAN——展开的实操记录,主要面向KVM/libvirt这类平台,也适合准备把现有网络改造成聚合链路加VLAN隔离的读者。
我之前在改造某内部虚拟化项目时,四台宿主机、每台双万兆口,物理交换机只给了一个trunk口,业务却要拆成三套互相隔离的环境。当时落地的方案就是:两个物理口先做成bond,bond上面划出若干VLAN子接口,每个子接口再桥接给对应的虚拟机。整个链路从物理口到虚拟网卡共四层,任何一层配置错位,现象都会非常诡异。下面把这套方案的原理、配置、验证和排错完整拆开讲,内容都能直接照着操作。
1. 虚拟化主机的网络底座:bond与VLAN各自解决什么问题
1.1 一块网卡不够用时的选择:从直接桥接到先做绑定
物理服务器的网卡数量是有限的,尤其刀片机或高密度服务器,常常只有两个万兆口。一开始很多人图省事,把物理网卡直接桥接给虚拟机用,也就是br0桥接eth0,虚拟机网卡挂到br0上。这种做法的优点是配置简单,但代价也很清楚:一旦这块物理网卡、网线、或者交换机端口出问题,依赖这个桥的虚拟机全部失联;带宽方面也只有一个物理口的硬上限,多块网卡之间不会自动分担流量。
绑定网卡(bonding)就是在这个场景下出现的。它的核心价值是两个词,一个是冗余,一个是带宽池。把两块甚至四块物理网卡聚合成一个逻辑口,外部看到的只有一个IP、一个MAC,但内部流量可以按策略在主备或负载均衡模式下调度。虚拟机层面完全感知不到底层是几张网卡,它只知道自己的虚拟网卡连在某个桥接设备上,数据报文到达bond口之后,由内核的bonding驱动决定从哪个真实物理口出去。
举个例子,两块千兆网卡做mode 4的bond,理论上可用带宽接近两倍,而且一块口断开时另一块继续兜底,切换时间通常在秒级以内。对于虚拟化宿主机来说,这个特性简直是为“一晚上不能断”这种需求量身定做的。前端业务、后端存储、管理通道混在同一台机器上,任何一次非计划断网都会牵连一排虚拟机,所以效率再高,也不如先把“不会因为一块网卡掉线就全盘崩掉”这件事做扎实。
1.2 为什么要在宿主机上划VLAN而不是全交给交换机
VLAN这东西原本是交换机上的概念,一个物理交换机端口可以属于某个VLAN,也可以做成trunk口让多个VLAN的tagged帧通过。到了虚拟化场景,问题变得有点拧:虚拟机是软件设备,它的“网口”并不直接插在物理交换机上,而是插在宿主机内部的虚拟交换机或者Linux Bridge上。如果你希望不同业务的虚拟机分属不同VLAN,有两种路线。
第一种路线是完全依赖物理交换机。宿主机上插两块物理网线,交换机的两个access口分别属于VLAN 10和VLAN 20,宿主机的每个物理口各自桥接到一组虚拟机。这样物理上是隔离的,但问题也很明显:冗余没了,bond没了,一个物理口挂了,那组虚拟机就断网。而且每新增一个VLAN就要多占一个物理端口,机柜后面乱成一团。
第二种路线就是这篇要展开的:宿主机用一根trunk链路上连交换机,物理口先做bond保证冗余和带宽,然后在bond上创建VLAN子接口,比如bond0.10、bond0.20,每个VLAN子接口再桥接给对应的虚拟机。从交换机的视角看,它只要把这个下行口配置成trunk,允许VLAN 10、20、30通过,剩下的VLAN划分动作全部在宿主机内部完成。物理链路上是带802.1Q tag的帧,到了bond0.10这一层,内核的VLAN模块把tag剥掉,桥接到br10上的虚拟机拿到的就是干净的VLAN 10二Layer帧。
这个方案的收益很明显:物理端口少、冗余架构统一、VLAN的增删全部在宿主机上改配置,不用反复去交换机上插拔网线。风险点也一样明显:所有VLAN共享一根物理链路,trunk口打满时全业务互相影响,所以实际项目中要结合带宽需求评估,别把几十个VLAN压在一根万兆口上还觉得万无一失。
2. 绑定模式怎么选:虚拟化场景中的模式权衡与交换机配合
2.1 七种bond模式与虚拟机的流量特征
bonding驱动提供了多种模式,CentOS7上常见的有mode 0、1、2、4。很多新手第一次接触会纠结“到底用哪个”,其实脱离业务谈模式是选不出来的。虚拟化宿主机的流量特征比较特殊:既有虚拟机之间的东西向流量,也有虚拟机访问外部的南北向流量,还有管理流量和存储备份流量混杂在一起。这种多源流量叠加的局面,决定了我们不希望绑定算法太“憨”,也不能太激进。
| 模式 | 名称 | 是否需交换机配合 | 特点与虚拟化适用性 |
|---|---|---|---|
| mode 0 | balance-rr | 需静态聚合或对端配合 | 轮流发包,可能乱序,重传较多,不推荐生产虚拟化环境 |
| mode 1 | active-backup | 无需配合 | 一主一备,逻辑简单,不提升带宽但冗余可靠,保守首选 |
| mode 2 | balance-xor | 需对端聚合 | 按MAC或IP hash分发,稳定性较好,但对端要支持 |
| mode 4 | 802.3ad LACP | 需交换机开LACP | 动态协商,带宽叠加,虚拟化场景最推荐的常规解 |
| mode 5/6 | balance-tlb/alb | 无需配合,但依赖驱动 | 本地负载均衡,双向均衡,但兼容性参差,生产环境见得少 |
为什么虚拟化环境不推荐mode 0?原因很直接:round-robin是让报文轮流从不同物理口发出,接收方看到的乱序概率会显著上升,TCP对乱序很敏感,一旦触发快速重传,实际吞吐不升反降。别被“轮流发包”这种表面均衡骗了,真实高吞吐场景下它很容易翻车。
对大多数内部虚拟化项目,如果交换机可控,直接上mode 4最省心;如果交换机老旧,或者网管团队不给开LACP,那就用mode 1老老实实做冗余。带宽需求真的很高时,再考虑升级物理口速率,而不是靠阻塞在mode 0里赌运气。
2.2 mode 4的LACP协商细节:不是配完就完事
mode 4需要和交换机跑LACP,双方协商成功后才开始转发数据。这个协商里面有大量“看起来通了,实际上没通”的坑。
先看关键参数。miimon=100表示每100毫秒检测一次链路状态,默认值比较稳妥;lacp_rate=1对应fast LACP,即每秒发送一次LACPDU,让协商收敛更快;xmit_hash_policy=layer3+4表示按IP和端口做hash,这样多路流量能更均匀地打散到不同物理口上。很多人在mode 4里只写了mode=4 miimon=100,不写hash策略,默认的layer2策略在某些多虚拟机场景下会把大量流量单压到一条链路上,带宽池效果出不来。
LACP协商失败的首个症状是bond口始终处于down。常见原因有:物理交换机的端口没有配成LACP模式,或者交换机配置的是静态聚合而不是动态聚合;还有一种情况是两块物理网卡的驱动版本不同,导致发送的LACPDU格式或速率有细微差异,个别交换机对LACP报文很挑食。
实际操作里我习惯配置完先别急着开机自启,手动执行ip link set bond0 up观察几秒,然后去交换机上查看聚合口状态,确认两个成员口都是Selected状态。如果一直是Standby,几乎可以断定是协商参数不一致,优先检查交换机端口的LACP mode和速率。
3. 动手配置bond与VLAN:ifcfg脚本逐行拆解
3.1 清零前置状态:网卡识别、NetworkManager、模块加载
开始配置前,先把环境状态摸清楚。第一步用ip link确认物理网卡名,CentOS7里常见的有eno1、enp3s0f0、em1这类命名。如果服务器上有板载网卡和PCIe网卡混用,名字会很乱,一定要先明确哪块网卡对应哪个物理口,不要凭感觉写进配置文件,否则做完bond发现错把两块口绑在一起,排错会非常痛苦。
第二步处理NetworkManager。CentOS7默认启用NetworkManager,它和network脚本服务同时存在时,很容易出现“配置写了,重启被NM接管”的情况。如果你决定用ifcfg这套传统网络配置,建议直接禁用NetworkManager,或者至少在网卡配置里加上NM_CONTROLLED=no。如果业务上非要保留NM,那就用nmcli来管理bond,别两边混着写,混写的结果往往是重启后bond口没起来,从属网卡却被NM拉成了独立网卡。
第三步确认bonding模块是否加载。执行modprobe bonding后,lsmod | grep bonding能看到模块信息。想开机自动加载,就写一行到/etc/modules-load.d/bonding.conf里,内容就是bonding。这一步很容易被漏掉,尤其是一些精简安装的系统,模块加载时机不一致,可能会在network服务启动时找不到bonding驱动,导致后续配置全都不生效。
3.2 bond口的ifcfg写法与从属网卡约束
下面是一份可以直接照抄的bond配置。假设服务器有两块物理网卡eno1和enp3s0f0,要绑成bond0,模式取mode 4:
先创建/etc/sysconfig/network-scripts/ifcfg-bond0:
DEVICE=bond0 NAME=bond0 TYPE=Bond BONDING_MASTER=yes BOOTPROTO=none ONBOOT=yes MTU=9000 BONDING_OPTS="mode=4 miimon=100 lacp_rate=1 xmit_hash_policy=layer3+4"接着改从属网卡的配置。用ifcfg-eno1举例:
DEVICE=eno1 NAME=eno1 TYPE=Ethernet BOOTPROTO=none ONBOOT=yes MASTER=bond0 SLAVE=yes NM_CONTROLLED=noenp3s0f0的配置同理,只是DEVICE和NAME换成它自己的名字。这里有一个新手常犯的错:有人会在从属网卡上顺手配一个IP,哪怕只是作为备用地址,也会扰乱路由。从属网卡的所有IP都应该收敛到bond口或bond的VLAN子接口上,从属口就是纯粹的“搬运工”,越干净越好。
至于MTU=9000,要结合交换机实际是否支持jumbo frame来决定。如果交换机不支持,或者虚拟机对端MTU保持1500,就不要全局改9000,否则大包直接丢,MTU不一致引发的“能ping通但下载卡住”是最难排查的坑之一。稳妥的做法是先保持1500跑通所有功能,确认业务需要再统一调整。
配置写完后重启网络服务,或者执行systemctl restart network。然后用cat /proc/net/bonding/bond0查看聚合状态,重点看MII Status是否为up,Active Slave是否正常。
3.3 VLAN子接口与Bridge的联动
bond口起来之后,开始划分VLAN。这里的思路是:每个VLAN对应一个bond0.X子接口,再对应一个Linux Bridge,最终把虚拟机网卡挂到Bridge上。链路结构是:
物理网卡 → bond0 → bond0.10 → br10 → vnet0(虚拟机)
创建/etc/sysconfig/network-scripts/ifcfg-bond0.10:
DEVICE=bond0.10 VLAN=yes VLAN_ID=10 PHYSDEV=bond0 BOOTPROTO=none ONBOOT=yes BRIDGE=br10如果这个VLAN子接口在宿主机上只需要做管理地址,不需要接虚拟机,可以把BRIDGE=br10去掉,在BOOTPROTO=static的情况下配上IPADDR、NETMASK。但如果是业务虚拟机的接入VLAN,就必须配合Bridge。
对应的桥配置文件是/etc/sysconfig/network-scripts/ifcfg-br10:
DEVICE=br10 TYPE=Bridge BOOTPROTO=none ONBOOT=yes STP=no为什么桥要单独占一个ifcfg文件?因为Bridge本身是一个二层交换设备,它的状态需要独立管理。STP默认写作no,这是CentOS7的默认行为,通常在单宿主机单桥且没有物理冗余链路时是安全的;如果存在一条物理链路经过交换机还跑到另一台桥接设备上,形成二层环路,就必须开启STP,否则广播风暴会教做人。
重复这套模式,可以配出bond0.20对应br20,bond0.30对应br30。值得注意的是每个VLAN子接口的MAC地址默认继承bond0的MAC,这对业务侧是透明且有利的。编号上不要用bond0.1,VLAN 1往往是交换机的native vlan,默认不打tag,和预期行为经常不一致,建议业务VLAN从10或100开始规划。
4. 让虚拟机按VLAN入网:libvirt桥接与trunk思想的落地
4.1 虚拟机XML里bridge网络怎么填
宿主机上的br10建好之后,KVM虚拟机接入就变得非常自然。创建虚拟机时,在XML里定义网络接口设备:
<interface type='bridge'> <source bridge='br10'/> <model type='virtio'/> </interface>如果虚拟机已经存在,想热添加一块网卡,可以用virsh attach-interface 虚拟机名 bridge br10 --model virtio --live --config,也可以编辑XML后virsh define重新加载。热添加的场景里我要特别提醒一句:部分旧版本libvirt只支持附加到相同bridge的接口,跨bridge操作最好在停机状态下改XML,免得出现网卡已经出现在虚拟机里但始终看不见链路的情况。
背后的转发逻辑是这样的:虚拟机发出的原始以太网帧,没有VLAN tag,它进入br10后被桥交换逻辑转发到bond0.10这个上游口,内核VLAN模块看到这个口对应VLAN 10,就给帧打上802.1Q tag 10,然后再从bond0的某个物理口发出去,最后由物理交换机trunk口接收。反过来也一样,trunk口收到带tag 10的帧,从bond0进到内核,VLAN模块剥掉tag,帧出现在bond0.10上,然后被br10转发给对应虚拟机。整个过程对虚拟机完全透明。
如果不用libvirt,纯qemu命令行方式也可以用-netdev bridge,br=br10,id=net0配合-device virtio-net-pci,netdev=net0,原理一致,只是少了libvirt的管理层和DHCP地址的自动关联能力。生产环境还是推荐libvirt,毕竟虚拟机的生命周期管理不止是网络这一块。
4.2 隔离与广播:不同VLAN/不同Bridge之间的边界
有人问,虚拟机A挂在br10,虚拟机B挂在br20,它们之间到底隔不隔离?答案是隔离的。因为Linux Bridge是独立的二层设备,br10与br20之间没有任何转发关系,广播、组播、ARP请求都不会跨桥传播。这比传统物理交换机还要安全,毕竟物理交换机上如果配置失误还能串VLAN,两个独立Bridge之间连配置失误的机会都没有。
但这不代表万事大吉。如果两个VLAN之间需要通信,就必须走三层路由。常见做法是用宿主机作为路由器,在bond0.10和bond0.20上分别配置IP,然后开启内核IP转发;或者在上游交换机上做VLAN间路由。你在规划时倾向哪种都行,但千万别为了省事把两个Bridge用veth对连起来,那样等于在二层把两个VLAN硬打通,隔离性瞬间归零。
还有一种场景,如果你希望虚拟机自己处理VLAN tag,比如要在虚拟机里再划分多个子VLAN,那就不适合用Linux Bridge接bond0.X,而应该考虑把trunk口直接交给虚拟机。这种模式在KVM里可以用macvtap的VEPA模式,或者Open vSwitch配合仿真办法,但配置复杂度会上升一个台阶。我的建议是:除非有明确的虚拟化内部再划VLAN的需求,否则优先在宿主机层面拆分子接口,这样对虚拟机的网络配置最友好,也最容易理解和排错。
5. 验证链路与VLAN是否真的按预期工作
5.1 从宿主机侧看bond与VLAN状态
配置完成之后,验证这步千万别省。先看bond状态:
cat /proc/net/bonding/bond0重点看MII Status和Link Failure Count。如果两块从属网卡都是up,而且Bonding Mode是802.3ad,说明LACP协商成功。再看VLAN子接口:
ip -d link show bond0.10输出里能看到vlan protocol 802.1Q id 10,说明VLAN子接口已经创建。此时在宿主机上ping一下网关,确认三层通了。但这里有个容易误判的点:宿主机能ping通,只能说明bond0.10和交换机端的配置没问题,不代表虚拟机也能通。因为虚拟机到br10这段还有一层桥接,需要单独验证。
查看桥接状态:
bridge link show这条命令会列出所有Bridge以及挂在上面的接口,包括bond0.10和各个虚拟机的vnetN。如果发现某个Bridge下只有bond0.10而没有虚拟机接口,那说明虚拟机网卡没有正确挂载,问题大概率在libvirt的XML配置。
5.2 从虚拟机侧观察访问与隔离
进入虚拟机,先确认网卡状态,然后用同VLAN的另一台虚拟机做ping测试。如果ping不通,回到宿主机上用tcpdump定位:
tcpdump -i br10 -e -n icmp在br10上抓包应该能看到ICMP请求和应答,而且帧头没有VLAN tag。这符合预期,因为br10内侧已剥离tag。如果你想确认出去的帧确实带了VLAN 10的tag,就去物理口上抓:
tcpdump -i eno1 -e -n 'ether proto 0x8100'这里能直接看到802.1Q header里的vlan id。如果物理口上是EtherType 0x0800的普通IP帧而没有0x8100,说明tag没有打上,链路里某个环节的VLAN配置失效了。
隔离性测试也建议做一下:从VLAN 10的虚拟机ping VLAN 20的虚拟机,预期是超时,收到应答反而是出了严重问题。很多师傅测到这里就满足了,但我还会多测一步广播,用arping从一个VLAN发起对另一个VLAN的探测,确认二层广播完全被隔断。这一步在安全要求高的环境里格外重要,防止出现VLAN间串扰这类隐蔽问题。
6. 踩坑实录:bond起不来、VLAN不通、桥接丢包的排查线索
6.1 bond口没接管物理网卡,配置像被“吞”了
这是我在虚拟化环境里见过最多的问题。现象是配置文件全写了,systemctl restart network也执行了,但ip link show里物理网卡还是独立的,根本没有slave状态。怀疑的第一对象往往是NetworkManager,它会在network服务之前或者之后抢占网卡控制权,导致ifcfg脚本里写的MASTER=bond0被忽略。解决方式前面说过,要么停掉NetworkManager,要么给网卡配置加NM_CONTROLLED=no。
另一个容易被忽略的原因是网卡配置文件的NAME字段和DEVICE字段不一致,某些情况下扫描脚本处理异常。建议每个配置文件里DEVICE和NAME保持完全一致,不要凭兴趣填别名。还有个别服务器板载网卡有biosdevname或net.ifnames命名规则干扰,如果物理网卡名里带有奇怪的.或:字符,可以先统一改名为eth0、eth1这种简单样式再继续。
6.2 VLAN tag在桥中消失:为什么抓包看不到VLAN标签
遇到虚拟机流量不通,在br0上抓包能看到帧能到桥,但物理口侧抓不到VLAN标签,这种情况多半是链路没有按照预期经过VLAN子接口。我排查时先看桥的成员端口,命令是bridge link show,如果发现某个桥的上游口直接是bond0而不是bond0.10,那么VLAN子接口根本没有参与转发,自然不会有tag。
还有人会把虚拟机的vnet口和物理口同时加入同一个Bridge,中间没有子接口,形成“全二层直通”。这时候VLAN的划分在整个链路里实际不存在,流量会以untagged形式穿过,交换机端如果trunk口设置了native vlan,帧还可能会被归到默认VLAN里,表现就是虚拟机莫名其妙能通一部分网络,另一部分完全不通,非常玄学。
6.3 其他常见问题快查表
| 症状 | 可能原因 | 排查命令或检查点 |
|---|---|---|
| bond口一直down | LACP协商失败、从属网卡MAC地址冲突、模块未加载 | cat /proc/net/bonding/bond0、交换机聚合口状态 |
| 虚拟机不通但宿主机通 | 虚拟机网卡未正确挂到Bridge | bridge link show、libvirt XML |
| VLAN ping通过但大包不通 | MTU不一致 | 对比物理口、bond、虚机接口MTU |
| 重启后bond配置丢失 | NetworkManager接管 | 检查网卡是否存在NM连接文件,禁用NM |
| 两个VLAN互相能通 | Bridge配置串连、veth搭线 | 查看Bridge成员,断开多余连线 |
| 广播风暴 | Bridge环路且未开STP | bridge link show确认路径唯一,必要时开STP |
最后再补一个我自己的习惯。改任何网络配置,都要先把改动拆成最小单元,每两步验证一次,别一口气把bond、VLAN、Bridge全写完再重启,否则任何一环出错,排查半径会大到你怀疑人生。尤其在生产环境里,我都是先当场用命令把bond拉起来验证,再回写配置文件持久化。网络这块最怕的就是“看起来通了其实没通”,而bond加VLAN的组合,恰恰是把这种忽悠人的假象放大到极致的一种配置。希望这份记录能帮你少踩几个坑,至少让这两个词不再成为半夜事故的主角。