1. 从一个真实需求说起:为什么要在OVS上折腾VXLAN
数据中心里的虚拟机迁移、跨机架的二层互通、多租户网络隔离,这几个需求凑在一起,传统VLAN方案基本就顶不住了。VLAN只有12位标识符,满打满算4094个可用网络,而且跨三层设备透传二层广播域本身就是个麻烦事。我在实际项目里遇到过最头疼的场景是:两个机柜各有一台宿主机,上面跑着同一业务的虚拟机,要求它们像插在同一台交换机上一样通信,但中间隔着三层路由设备。这时候VXLAN就成了绕不开的选择。
VXLAN的本质是把二层以太网帧封装进UDP里,通过三层网络传输,在两端解封装还原出二层帧。它用24位VNI标识符,理论支持1600万个隔离网络,彻底解决了VLAN数量瓶颈。而Open vSwitch作为一款开源虚拟交换机,原生支持VXLAN隧道,配置灵活,还能和Linux内核的数据平面配合,性能上也有保障。
这篇内容适合谁看?如果你已经对Linux网络基础、网桥概念、IP路由有一定了解,并且手头有两台以上的Linux主机(物理机或虚拟机都行),想通过实际操作把VXLAN隧道跑通,那接下来的内容就是为你准备的。我会从整体设计思路讲起,把每一步配置背后的原因说清楚,再给出完整的实操流程和排错经验。整个实验环境我用的是两台运行主流Linux发行版的虚拟机,各自带一块业务网卡,通过一个三层可达的网络互联。
需要提前说明的是,VXLAN本身只是一个封装协议,它不负责控制平面。也就是说,隧道对端地址、VNI与流量的映射关系,需要你自己维护。在小规模场景下手工配置静态隧道完全够用,大规模部署才需要引入EVPN之类的控制平面。我们这个实验走的是静态配置路线,简单直接,适合理解和入门。
2. 实验环境搭建与核心概念对齐
2.1 两台主机的网络规划
先把实验拓扑说清楚。我准备了两台Linux主机,分别叫hostA和hostB。每台主机有两块网卡:一块用于管理/底层传输,一块用于承载业务流量。管理网卡的地址段是192.168.100.0/24,hostA是192.168.100.10,hostB是192.168.100.20。这两台主机之间三层可达,能互相ping通,这是VXLAN隧道建立的前提。
业务侧我规划了两个网段:10.10.10.0/24和10.10.20.0/24,分别对应VNI 100和VNI 200。hostA上创建两个内部端口,IP分别是10.10.10.1和10.10.20.1;hostB上对应10.10.10.2和10.10.20.2。目标是让hostA的10.10.10.1能ping通hostB的10.10.10.2,同时10.10.20.1能ping通10.10.20.2,而且两个VNI的流量互相隔离。
这里有个关键点:VXLAN的底层传输依赖UDP,默认目的端口是4789。有些环境里会用到8472,这是早期Linux内核实现的历史遗留,现在标准端口是4789。我建议统一用4789,避免兼容性问题。
2.2 OVS的安装与基础检查
安装OVS在不同发行版上命令略有差异。以常见的RPM系和DEB系为例,RPM系用包管理器安装openvswitch和openvswitch-kmod(如果需要内核模块),DEB系则是openvswitch-switch和openvswitch-common。安装完成后,用ovs-vsctl show检查数据库连接是否正常,用ovs-vsctl --version确认版本。我用的版本是2.17.x,这个版本对VXLAN的支持已经很成熟了。
安装完第一件事是启动服务。systemd环境下执行systemctl enable --now openvswitch,然后确认ovsdb-server和ovs-vswitchd两个进程都在跑。如果ovs-vsctl show报数据库连接失败,八成是服务没起来或者socket路径不对。可以用systemctl status openvswitch看详细日志。
提示:有些发行版默认没加载openvswitch内核模块,需要手动
modprobe openvswitch。如果modprobe报错,检查内核头文件包是否安装,必要时重新编译内核模块。
2.3 VXLAN、VNI、VTEP这几个概念的关系
在动手之前,把几个术语理清楚能少走很多弯路。VTEP是VXLAN隧道端点,每台参与VXLAN通信的主机就是一个VTEP,它负责封装和解封装。VNI是VXLAN网络标识符,类似VLAN ID但位数更多,用来区分不同的虚拟二层网络。隧道的目的地址就是对端VTEP的底层IP。
打个比方:VXLAN隧道就像两个小区之间修的地下通道,VTEP是通道两端的入口,VNI是通道里划分的不同车道,底层IP是通道入口的地址。数据帧从hostA的某个VNI车道进入通道,到达hostB后从对应VNI车道出来,中间走的是三层网络,但两端感觉像直连。
OVS里创建VXLAN端口时,需要指定remote_ip和key。remote_ip是对端VTEP地址,key就是VNI。如果有多对端,可以创建多个VXLAN端口,或者用ovs-vsctl的set interface命令动态添加remote_ip。
3. 核心配置实操:从零把隧道跑通
3.1 hostA上的完整配置流程
先创建网桥。我习惯用br-vxlan作为网桥名,清晰好记。执行ovs-vsctl add-br br-vxlan。这个网桥是OVS管理的虚拟交换机,后面所有端口都挂到它上面。
接着创建VXLAN隧道端口。命令是ovs-vsctl add-port br-vxlan vxlan100 -- set interface vxlan100 type=vxlan options:remote_ip=192.168.100.20 options:key=100。这条命令做了三件事:在br-vxlan上添加一个名为vxlan100的端口,指定类型为vxlan,设置对端IP为hostB的管理地址,VNI为100。同理再创建vxlan200,key改成200。
然后创建内部端口,也就是业务侧的虚拟网卡。ovs-vsctl add-port br-vxlan veth100 -- set interface veth100 type=internal。这个internal类型的端口会在宿主机上生成一个同名网卡,可以配IP。给它配上10.10.10.1/24:ip addr add 10.10.10.1/24 dev veth100,然后ip link set veth100 up。veth200同理,配10.10.20.1/24。
这里有个细节:internal端口默认可能在DOWN状态,必须手动up。另外,如果宿主机有NetworkManager或systemd-networkd在管理网卡,可能会干扰OVS创建的internal端口,建议把这些端口加入排除列表,或者干脆停掉网络管理服务,用ip命令手工管理。
3.2 hostB上的对称配置
hostB的配置和hostA镜像对称。创建br-vxlan网桥,添加vxlan100和vxlan200端口,但remote_ip要指向hostA的192.168.100.10。internal端口veth100配10.10.10.2/24,veth200配10.10.20.2/24。
配置完成后,用ovs-vsctl show检查两端的端口状态。正常情况下,vxlan端口的type是vxlan,options里能看到remote_ip和key。internal端口应该显示为internal类型,并且有对应的Linux网卡。
3.3 验证隧道连通性
先在hostA上ping hostB的底层地址,确认三层可达。然后ping业务地址:ping 10.10.10.2。如果通了,说明VNI 100的隧道工作正常。再ping 10.10.20.2验证VNI 200。
如果ping不通,按这个顺序排查:先看ovs-vsctl show里vxlan端口是否真的创建成功,再看ip link show确认internal端口是UP状态,然后用tcpdump -i <底层网卡> udp port 4789抓包,看封装后的UDP报文有没有发出去、有没有收到回包。我遇到过最常见的问题是防火墙没放行UDP 4789,或者底层网卡的路由不对导致封装报文发不出去。
注意:VXLAN封装后的报文源地址是底层网卡的IP,目的地址是对端VTEP的IP。如果底层网络有NAT设备,VXLAN可能无法正常工作,因为NAT会改写端口和地址,导致解封装失败。这个实验要求底层网络是纯三层可达,不做NAT。
3.4 用流表验证VNI隔离
两个VNI都通了之后,可以进一步验证隔离性。在hostA的veth100上ping hostB的10.10.20.2,应该不通,因为VNI 100和VNI 200是隔离的。如果通了,说明配置有问题,可能是两个VXLAN端口挂到了同一个网桥上但没有正确区分VNI,或者流表把流量泛洪了。
OVS默认的NORMAL流表会根据VLAN和VNI做转发决策。在VXLAN场景下,每个VXLAN端口对应一个VNI,internal端口如果不打VLAN标签,默认走VNI 0。所以更严谨的做法是给internal端口打上VLAN标签,再通过流表把VLAN和VNI做映射。不过对于这个基础实验,两个VNI各自独立,不配VLAN也能工作,因为OVS会把不同VXLAN端口的流量按VNI隔离。
4. 进阶话题:多播、流表与性能调优
4.1 从静态单播到多播的取舍
前面配的是静态单播隧道,每端都要手动指定对端IP。如果只有两台主机,这没问题。但如果有三台、五台甚至更多VTEP需要互通,手工配置的工作量就上来了,而且新增节点要改所有现有节点的配置。这时候可以考虑多播模式:VXLAN端口不指定remote_ip,而是加入一个多播组,底层网络负责把广播、未知单播、多播流量复制到组内所有VTEP。
多播模式配置更简单,但依赖底层网络支持多播路由。很多云环境或跨网段场景下多播是被禁用的,所以实际生产里静态单播配合自动化脚本更常见。我的建议是:小规模(5台以内)用静态单播,脚本批量下发;大规模上EVPN控制平面,让BGP自动分发VTEP和VNI信息。
4.2 流表精细化控制
默认的NORMAL流表虽然能用,但不够灵活。比如你想让VNI 100的流量走特定的底层路径,或者做QoS限速,就需要写自定义流表。OVS的流表语法和OpenFlow兼容,可以用ovs-ofctl add-flow添加。
举个例子,把VNI 100的广播流量限速到1Mbps:先创建QoS策略,再在流表里匹配VNI字段并应用QoS。这个操作需要先ovs-vsctl set interface vxlan100 ingress_policing_rate=1000,再配合流表匹配。实际配置时要注意,VXLAN的VNI在OVS里对应的是tunnel_id字段,流表匹配用tun_id=100。
流表调试可以用ovs-ofctl dump-flows br-vxlan查看当前流表,用ovs-appctl ofproto/trace模拟报文转发路径。这两个命令是排错利器,能清楚看到报文在OVS内部走了哪条流表、做了哪些动作。
4.3 性能相关的几个关键参数
VXLAN封装解封装有CPU开销,尤其是小包场景。几个优化方向:一是开启网卡的TSO、GRO、GSO等卸载功能,让硬件分担部分计算;二是调整OVS的other_config参数,比如max-idle和max-revalidator,影响流表老化时间;三是如果内核支持,用ovs-vsctl set Open_vSwitch . other_config:dpdk-init=true启用DPDK数据平面,把转发放到用户态轮询,大幅提升吞吐但会占用专用CPU核。
我实测下来,在普通千兆环境下,不开启DPDK,单隧道吞吐能到900Mbps左右,接近线速。万兆环境下如果不开DPDK,小包转发率会明显下降,这时候DPDK或者智能网卡卸载就很有必要了。不过DPDK配置复杂,对网卡型号有要求,入门阶段先把基础隧道跑通,性能优化可以后续再搞。
5. 踩坑实录与常见问题速查
5.1 隧道不通的排查思路
隧道不通是最常见的问题,我整理了一个排查顺序表,按这个顺序走基本能定位到根因。
| 排查步骤 | 检查命令 | 预期结果 | 常见异常 |
|---|---|---|---|
| 底层连通性 | ping 对端VTEP IP | 通 | 路由缺失、防火墙拦截 |
| OVS服务状态 | ovs-vsctl show | 正常显示网桥和端口 | 数据库连接失败、服务未启动 |
| VXLAN端口状态 | ovs-vsctl list interface vxlan100 | type=vxlan,options正确 | remote_ip写错、key不匹配 |
| internal端口状态 | ip link show veth100 | UP状态,有IP | 端口DOWN、IP未配 |
| 封装报文抓包 | tcpdump -i eth0 udp port 4789 | 有UDP报文进出 | 只有出没有进,对端未响应 |
| 流表检查 | ovs-ofctl dump-flows br-vxlan | 有NORMAL或自定义流表 | 流表为空或匹配错误 |
我遇到过一次诡异的情况:两端配置完全对称,底层也通,但业务ping就是不通。抓包发现hostA发出了封装报文,hostB也收到了,但hostB没有解封装。最后查出来是hostB的OVS版本和hostA不一致,hostB的VXLAN实现有个bug,升级到相同版本后解决。所以实验环境尽量保持两端软件版本一致,能省很多事。
5.2 MTU问题:最容易被忽略的坑
VXLAN封装会增加开销:外层IP头20字节,UDP头8字节,VXLAN头8字节,总共36字节。如果底层网卡MTU是1500,那么业务侧能用的最大MTU就是1500-36=1464。如果业务报文超过1464且设置了DF位,就会被丢弃,表现为ping大包不通、TCP连接卡顿。
解决办法有两个:一是把业务侧internal端口的MTU调小到1464,ip link set veth100 mtu 1464;二是把底层网卡MTU调大到1550或更高,让封装后不超过底层MTU。生产环境通常用第二种,因为调小业务MTU可能影响应用性能。但调大底层MTU需要全网设备支持,包括中间的路由器和交换机,所以要先确认底层网络的最大MTU。
提示:用
ping -M do -s 1472 对端业务IP测试,1472+8=1480是ICMP载荷,加上IP头20字节正好1500。如果这个ping不通但小包通,基本就是MTU问题。
5.3 多网桥场景下的路由冲突
如果宿主机上已经有其他网桥或网络命名空间,可能会和OVS的internal端口产生路由冲突。比如默认路由指向了错误的网卡,导致封装报文的源地址不对。这时候要用ip route get <对端VTEP IP>确认路由走向,必要时添加策略路由,指定VXLAN流量走特定网卡。
另一个常见问题是ARP表混乱。internal端口和物理网卡如果在同一网段,ARP响应可能从错误的接口发出。解决办法是给internal端口配一个独立的子网,或者用VRF做隔离。OVS本身支持多网桥,可以把管理流量和业务流量分到不同网桥,减少干扰。
5.4 重启后配置丢失的应对
OVS的配置默认保存在数据库里,重启后应该自动恢复。但internal端口的IP地址是用ip命令配的,不在OVS数据库里,重启后会丢。解决办法是把IP配置写进启动脚本,或者用ovs-vsctl的set interface命令把IP配置也纳入OVS管理(部分版本支持)。更稳妥的做法是用配置管理工具统一管理,确保每次重启后状态一致。
我在实际项目里还遇到过OVS数据库损坏导致配置全丢的情况,虽然概率很低,但一旦发生很麻烦。建议定期备份/etc/openvswitch/conf.db,出问题时可以快速恢复。
6. 从实验到生产的距离
把两台主机的VXLAN隧道跑通只是第一步。真实生产环境里,VTEP数量可能几十上百,VNI数量上千,手工配置完全不现实。这时候需要引入自动化工具,比如用Ansible批量下发OVS配置,或者用Python脚本调用OVSDB接口动态创建隧道。再往上就是EVPN方案,用BGP作为控制平面,VTEP和VNI信息自动分发,支持多活和快速收敛。
另一个生产环境必须考虑的是高可用。单台VTEP故障会导致该节点上所有业务网络中断。常见的做法是双网卡绑定做底层冗余,OVS层面用bond端口聚合,再配合BFD快速检测链路故障。如果业务对中断极度敏感,还需要考虑多活VTEP方案,这涉及更复杂的控制平面设计。
监控方面,OVS提供了ovs-appctl系列命令查看端口统计、流表命中率、丢包计数。可以对接Prometheus做长期监控,设置告警阈值。我习惯重点关注rx_dropped和tx_dropped两个指标,它们能反映MTU问题、缓冲区溢出等常见故障。
最后分享一个我在实际调试中总结的小技巧:每次修改OVS配置前,先用ovs-vsctl show和ovs-ofctl dump-flows把当前状态保存下来,改完后对比差异。这样一旦出问题,能快速回滚到已知正常的状态。OVS的配置命令大多支持--may-exist参数,重复执行不会报错,适合写在脚本里做幂等操作。