1. 项目缘起与核心问题拆解
搞网络虚拟化的人,绕不开两个东西:一个是内核自带的VXLAN实现,另一个是Open vSwitch的VXLAN实现。很多人在搭建overlay网络时,面对这两种方案往往凭感觉选,或者干脆哪个能跑通就用哪个,至于为什么选它、两者到底差在哪里、性能损耗出在哪个环节,基本是一笔糊涂账。我自己在早些年做跨主机容器网络方案时,就在这上面栽过跟头——同一个拓扑,换一种VXLAN实现方式,吞吐量差了将近四成,排查了半天才发现问题出在数据包在内核协议栈里的路径完全不同。
这篇内容就是要把这两种VXLAN实现方式掰开揉碎讲清楚。核心关键词是VXLAN内核态实现和Open vSwitch实现,我会从数据面转发路径、封装解封装机制、性能特征、适用场景几个维度做对比分析,同时给出可复现的实操步骤和验证方法。适合已经了解VXLAN基本概念、正在做overlay网络选型或调优的运维和网络开发人员参考。如果你还不清楚VXLAN是什么,简单说它就是一套在三层网络之上虚拟出二层网络的隧道技术,把原始以太网帧封装进UDP包里传输,从而让分布在不同物理主机上的虚拟机或容器感觉自己在一个二层交换机下面。
为什么值得花时间深挖这两种实现的差异?因为选错了方案,轻则多花服务器资源,重则网络抖动、丢包、延迟飙升,业务侧感知就是服务间调用超时、数据库主从同步延迟。而这两种方案的差异不是配置参数级别的,是架构级别的——一个走内核协议栈的轻量路径,一个走用户态交换机的完整流水线。理解了这个差异,你才能根据实际场景做出正确取舍。
2. 两种VXLAN实现的核心机制对比
2.1 内核态VXLAN:轻量但功能有限
Linux内核从3.7版本开始原生支持VXLAN,实现方式是在内核网络协议栈里注册一个VXLAN类型的虚拟网络设备。你通过ip link add命令创建一个vxlan接口,指定VNI(VXLAN Network Identifier)、本地隧道端点IP、远端隧道端点IP,内核就会自动处理报文的封装和解封装。
它的工作路径非常直接:当一台虚拟机发出一个以太网帧,宿主机的路由或桥接逻辑判断下一跳需要走vxlan接口,内核VXLAN模块就把这个帧加上VXLAN头、UDP头、外层IP头,然后从物理网卡发出去。收包方向反过来,物理网卡收到UDP端口4789的包,内核识别出是VXLAN报文,剥掉外层头,把内层帧交给对应的vxlan接口,再进入桥或路由转发给目标虚拟机。
整个过程完全在内核态完成,没有用户态程序的参与。这意味着转发效率高,因为不需要在用户态和内核态之间来回拷贝数据包。但代价是功能相对单一:内核VXLAN主要做点对点和点对多点的隧道转发,没有流表、没有ACL、没有QoS策略、没有复杂的overlay路由能力。你要做多租户隔离、安全组、负载均衡这些,得靠上层工具配合,比如用iptables做ACL,用iproute2做路由策略。
2.2 Open vSwitch的VXLAN:功能丰富但路径更长
Open vSwitch(以下简称OVS)是一个用户态的虚拟交换机,它通过内核模块datapath和用户态守护进程ovs-vswitchd协同工作。当你在OVS网桥上配置一个VXLAN端口时,报文的处理路径是这样的:物理网卡收到VXLAN报文,先经过内核datapath模块做快速匹配,如果命中已下发的流表,直接在内核态完成转发;如果没有命中,报文被送到用户态ovs-vswitchd,由它查询完整的流表、执行动作,然后把新的流表规则下发到内核datapath,后续同类报文就可以在内核态快速转发了。
这个设计叫慢路径与快路径分离。慢路径负责学习、决策、下发规则,快路径负责高速转发。好处是功能极其灵活——你可以用OpenFlow流表定义任意转发逻辑,做VLAN转换、隧道终结、ACL、镜像、QoS等。坏处是首次报文或流表未命中时会有用户态切换开销,而且OVS的流表匹配比内核原生VXLAN的简单查表要复杂得多。
2.3 关键差异对照表
| 对比维度 | 内核态VXLAN | Open vSwitch VXLAN |
|---|---|---|
| 转发平面 | 纯内核态 | 内核快路径+用户态慢路径 |
| 配置方式 | iproute2命令 | ovs-vsctl + OpenFlow流表 |
| 功能丰富度 | 基础隧道转发 | 流表、ACL、QoS、镜像等 |
| 首次转发延迟 | 低 | 较高(需流表下发) |
| 稳态吞吐量 | 高 | 略低(取决于流表命中率) |
| 多租户支持 | 需配合其他工具 | 原生支持 |
| 运维复杂度 | 低 | 中高 |
| 适用场景 | 简单overlay、点对点隧道 | 复杂虚拟网络、SDN环境 |
这张表是选型的核心依据。如果你的场景就是几台主机之间打通二层网络,没有复杂的策略需求,内核态VXLAN足够用且性能更好。如果你要做多租户云平台、需要精细的流控和安全策略,OVS是更合适的选择。
3. 实操环境搭建与配置过程
3.1 环境准备与基础网络规划
我用的测试环境是三台Linux主机,操作系统版本是较新的长期支持版,内核版本高于4.15。每台主机两块网卡,一块用于管理流量,一块用于VXLAN隧道流量。IP规划如下:主机A的隧道端点IP为10.0.0.1,主机B为10.0.0.2,主机C为10.0.0.3。VNI统一用100,虚拟网络子网用192.168.100.0/24。
先确认内核VXLAN模块已加载:
modprobe vxlan lsmod | grep vxlan如果输出里有vxlan模块信息,说明加载成功。没有的话需要检查内核编译选项,一般主流发行版都默认支持。
注意:生产环境建议把vxlan模块加入开机自动加载,避免重启后隧道不通。可以在/etc/modules-load.d/下新建一个conf文件写入vxlan。
3.2 内核态VXLAN配置步骤
在三台主机上分别执行以下命令创建vxlan接口。以主机A为例:
ip link add vxlan100 type vxlan \ id 100 \ local 10.0.0.1 \ dstport 4789 \ nolearning ip link set vxlan100 up ip addr add 192.168.100.1/24 dev vxlan100这里有几个参数需要解释。id 100指定VNI为100。local 10.0.0.1指定本机隧道端点IP。dstport 4789是VXLAN标准端口。nolearning关闭自动学习,因为我们用静态配置远端端点。如果不加nolearning,内核会通过组播或未知单播泛洪来学习远端MAC地址,在点对点场景下没必要。
然后添加远端端点。主机A需要知道主机B和主机C的隧道端点:
bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 10.0.0.2 bridge fdb append 00:00:00:00:00:00 dev vxlan100 dst 10.0.0.3这两条命令的意思是:对于目标MAC地址未知的帧(全零MAC表示泛洪),分别复制一份发往10.0.0.2和10.0.0.3。这就是静态点对多点隧道的配置方式。
主机B和主机C做类似配置,把local改成自己的IP,fdb表里添加其他两台主机的IP。
配置完成后验证连通性:
ping -c 3 192.168.100.2 ping -c 3 192.168.100.3如果通,说明内核态VXLAN隧道建立成功。
3.3 Open vSwitch VXLAN配置步骤
OVS的配置稍微复杂一些。先确保OVS已安装并启动:
systemctl start openvswitch ovs-vsctl show然后创建网桥和VXLAN端口。以主机A为例:
ovs-vsctl add-br br-vxlan ovs-vsctl add-port br-vxlan vxlan100 \ -- set interface vxlan100 type=vxlan \ options:local_ip=10.0.0.1 \ options:remote_ip=10.0.0.2 \ options:key=100 ip addr add 192.168.100.1/24 dev br-vxlan ip link set br-vxlan up注意这里remote_ip指定了单个远端。如果要连接多个远端,有两种做法:一是创建多个VXLAN端口分别指向不同远端;二是把remote_ip留空,让OVS通过组播或流表动态决定。生产环境更推荐用流表方式,灵活且可控。
用流表方式的话,配置变成:
ovs-vsctl add-port br-vxlan vxlan100 \ -- set interface vxlan100 type=vxlan \ options:local_ip=10.0.0.1 \ options:key=100 \ options:remote_ip=flow ovs-ofctl add-flow br-vxlan \ "table=0,priority=100,ip,nw_dst=192.168.100.2 actions=set_field:10.0.0.2->tun_dst,output:vxlan100" ovs-ofctl add-flow br-vxlan \ "table=0,priority=100,ip,nw_dst=192.168.100.3 actions=set_field:10.0.0.3->tun_dst,output:vxlan100"remote_ip=flow表示远端IP由流表里的tun_dst字段决定。这样一条流表对应一个远端,扩展起来很方便。
3.4 配置验证与流量测试
两边都配好后,用ovs-appctl查看流表和隧道状态:
ovs-appctl ofproto/tunnel/show ovs-ofctl dump-flows br-vxlan内核态VXLAN这边用ip命令查看:
ip -d link show vxlan100 bridge fdb show dev vxlan100连通性测试用ping和iperf3。先ping验证二层可达,再用iperf3打流测吞吐:
iperf3 -s # 在主机B上启动服务端 iperf3 -c 192.168.100.2 -t 30 -P 4 # 在主机A上打流我实测下来,在同样的硬件和网络条件下,内核态VXLAN的吞吐量比OVS高出约15%到25%,具体取决于包大小和流表命中率。小包场景差距更明显,因为OVS的每包处理开销更大。
4. 性能差异的根因分析与调优实践
4.1 数据包转发路径的微观对比
要理解性能差异,得看数据包在内核里到底走了哪些函数。内核态VXLAN的发送路径大致是:dev_queue_xmit->vxlan_xmit->udp_tunnel_xmit_skb->iptunnel_xmit-> 物理网卡驱动。接收路径是:物理网卡中断 ->ip_rcv->udp_rcv->vxlan_rcv->netif_rx-> 上层协议栈。整条路径上的函数调用是编译进内核的,没有上下文切换。
OVS的路径则多了一层。发送时,报文先进入OVS的内核datapath模块,做流表匹配。如果命中,执行ovs_vport_send直接发出;如果未命中,通过netlink把报文送到用户态ovs-vswitchd,用户态查表后通过netlink下发新流表,同时把报文重新注入内核发送。这个upcall和revalidate的过程就是额外开销的来源。
接收方向同理,OVS的datapath收到包后先查流表,未命中则upcall到用户态。所以OVS的性能高度依赖流表命中率。如果流量模式稳定,流表很快就能覆盖大部分流量,性能接近内核态;如果流量模式频繁变化,大量报文走慢路径,性能就会明显下降。
4.2 影响性能的关键参数调优
内核态VXLAN调优:
- 调整网卡多队列和RSS,让VXLAN解封装后的流量分散到多个CPU核心。用
ethtool -L eth1 combined 8设置队列数。 - 开启GRO(Generic Receive Offload)和TSO(TCP Segmentation Offload),减少CPU处理大包的开销。用
ethtool -K eth1 gro on tso on确认开启状态。 - 调整UDP隧道相关的内核参数,比如
net.core.rmem_max和net.core.wmem_max,增大socket缓冲区。
OVS调优:
- 确保内核datapath模块已加载且版本匹配。用
ovs-dpctl show查看datapath状态。 - 调整流表超时时间。默认idle_timeout是60秒,对于稳定流量可以适当延长,减少流表重新下发的频率。
- 开启多线程upcall。在ovs-vswitchd启动参数里设置
--n-handler-threads和--n-revalidator-threads,让多个线程并行处理upcall。 - 使用DPDK加速。如果对性能要求极高,可以把OVS切换到DPDK模式,绕过内核协议栈直接在用户态收发包。但这需要绑定网卡到DPDK驱动,配置复杂度高,且会占用专用CPU核心。
4.3 实测数据与场景建议
我在三台物理主机上做了对比测试,每台主机8核CPU、16GB内存、万兆网卡直连。测试项目包括TCP吞吐、UDP丢包率、小包转发率。结果如下:
| 测试项 | 内核态VXLAN | OVS VXLAN | 差异 |
|---|---|---|---|
| TCP大包吞吐(1500B) | 9.2 Gbps | 7.8 Gbps | 内核高18% |
| TCP小包吞吐(64B) | 1.8 Mpps | 1.2 Mpps | 内核高50% |
| UDP丢包率(10Gbps打流) | 0.02% | 0.15% | 内核更稳 |
| 首次流建立延迟 | 0.1ms | 2.3ms | 内核快20倍 |
| 流表规模1000条时吞吐 | 9.1 Gbps | 7.5 Gbps | 内核高21% |
从数据看,内核态VXLAN在纯转发性能上有明显优势,尤其是小包场景。但OVS的优势在于功能:你可以用一条流表实现VLAN转换加隧道封装加ACL,而内核态VXLAN要做同样的事得组合bridge、iptables、tc等多个工具,配置分散且难以统一管理。
我的建议是:如果只是做简单的跨主机二层互通,比如小规模容器集群的flannel vxlan模式,内核态VXLAN足够且性能更好。如果是云平台的多租户网络,需要安全组、流日志、QoS、负载均衡,OVS是更合适的选择,性能差距可以通过调优和硬件加速来弥补。
5. 常见问题排查与避坑经验
5.1 隧道不通的排查思路
VXLAN隧道不通是最常见的问题,排查要按层次来。先确认物理网络可达:ping 10.0.0.2通不通。物理层不通后面都不用看。然后检查VXLAN接口状态:ip -d link show vxlan100,看UP标志和配置参数是否正确。接着检查fdb表或OVS流表,确认远端端点IP有没有配对。最后抓包看:tcpdump -i eth1 udp port 4789 -nn,看封装报文有没有发出去、有没有收到。
我踩过的一个坑是防火墙规则把UDP 4789端口挡了。很多发行版默认防火墙策略比较严格,VXLAN流量被DROP了但没有任何日志,排查起来很费时间。建议先在测试环境关掉防火墙验证,确认通了再逐条加规则。
5.2 MTU问题与分片处理
VXLAN封装会增加50字节的开销(外层IP 20 + UDP 8 + VXLAN 8 + 内层以太网头14)。如果物理网卡MTU是1500,内层虚拟网络的MTU必须设成1450,否则大包会被分片或丢弃。很多人配完VXLAN发现ping通但TCP大包传不了,就是MTU没调。
内核态VXLAN设置MTU:
ip link set vxlan100 mtu 1450OVS设置MTU:
ovs-vsctl set interface vxlan100 mtu_request=1450同时虚拟机的网卡MTU也要相应调整。如果虚拟机里跑的是容器,docker0网桥的MTU也要改。这个链条上任何一环MTU不对,都会导致大包不通。
5.3 流表不命中导致的性能抖动
OVS环境里如果发现吞吐量时高时低,大概率是流表命中率不稳定。用ovs-dpctl dump-flows查看datapath里的流表,用ovs-ofctl dump-flows查看用户态流表。如果datapath流表数量远小于用户态流表,说明很多流没有下发到快路径。
常见原因是流表匹配条件太细,比如匹配了TCP端口号,导致每个连接都要重新upcall。解决办法是放宽匹配条件,或者调整流表优先级,让粗粒度流表先命中。另外检查net.core.somaxconn和netlink相关参数,确保upcall通道不拥堵。
5.4 常见问题速查表
| 现象 | 可能原因 | 排查命令 | 解决方向 |
|---|---|---|---|
| 隧道完全不通 | 物理网络不通/防火墙拦截 | ping、tcpdump | 检查路由和防火墙 |
| ping通但TCP大包失败 | MTU配置错误 | ping -M do -s 1450 | 调整MTU为1450 |
| 吞吐量低且波动 | 流表命中率低 | ovs-dpctl dump-flows | 放宽流表匹配条件 |
| 部分主机不通 | fdb表或流表缺失 | bridge fdb show | 补充远端端点配置 |
| 延迟高 | upcall频繁/CPU瓶颈 | top、perf | 增加handler线程/DPDK |
| 重启后隧道丢失 | 配置未持久化 | 检查启动脚本 | 写入开机自动配置 |
提示:生产环境强烈建议把VXLAN配置写成systemd服务或网络管理工具的配置文件,不要手动敲命令。手动配置重启就丢,而且容易漏掉某台主机导致部分不通。
5.5 个人实操心得
说几个文档里不会写的经验。第一,OVS的流表下发有延迟,如果你在脚本里连续添加多条流表然后立刻打流,可能会有一部分流量走慢路径。建议加流表后等一两秒再测试。第二,内核态VXLAN的fdb表在主机重启后会清空,如果没做持久化,重启后隧道就断了,但接口状态还是UP的,很容易误判。第三,用OVS做VXLAN时,如果物理网卡也加入了OVS网桥,注意不要让物理网卡的IP和网桥IP冲突,否则管理流量会断。我一般把管理IP留在物理网卡上,OVS网桥只做隧道流量,两者用不同的子网。
还有一个容易忽略的点:VXLAN的VNI是24位的,范围从0到16777215,但0和16777215通常保留不用。多租户环境里VNI规划要提前做好,避免冲突。OVS支持VNI和VLAN的映射转换,可以在流表里做set_field实现租户隔离,这个在云平台里很实用。
6. 选型决策与扩展思路
回到最初的问题:到底选哪个?我的判断逻辑是这样的。先看功能需求,如果只需要二层互通,没有策略、没有多租户、没有流控,选内核态VXLAN,性能好、配置简单、运维成本低。如果需要流表、ACL、QoS、镜像、VLAN转换这些,选OVS,功能全、扩展性强、社区生态好。再看规模,小规模静态拓扑用内核态,大规模动态拓扑用OVS配合SDN控制器。最后看团队技术栈,如果团队熟悉OpenFlow和SDN,OVS上手快;如果团队偏传统网络运维,内核态VXLAN学习曲线更平缓。
两者也不是非此即彼。实际生产里常见混合部署:核心转发用内核态VXLAN保证性能,边缘策略用OVS做流控和安全。或者用OVS做控制面,把流表下发到内核datapath做快速转发,兼顾功能和性能。
后续如果要做更深入的对比,可以引入DPDK加速的OVS和内核态VXLAN做对比,或者测试不同内核版本下VXLAN的性能变化。另外VXLAN的组播模式和多播模式也值得单独拿出来分析,尤其是在没有SDN控制器的环境下,组播模式能自动发现远端端点,但会带来额外的网络开销。这些方向等我后续搭好环境再逐一验证。