Skip to content

为什么需要虚拟交换机

从一个具体问题开始

你有一台物理服务器,上面跑着 30 个虚拟机。每个虚拟机都要能上网,也要能互相通信。

服务器只有 2 块物理网卡。

text
          ┌──────────────────────────────────────────┐
          │  物理服务器                                │
          │                                          │
          │  VM1  VM2  VM3  ...  VM29  VM30          │
          │   │    │    │          │     │           │
          │   └────┴────┴──── ? ───┴─────┘           │
          │                                          │
          │            eth0    eth1                  │
          └──────────────┬───────┬───────────────────┘
                         │       │
                    ─────┴───────┴─────  物理网络

中间那个问号该放什么?

最朴素的两个答案

答案一:给每个虚拟机一块物理网卡。 30 个虚拟机就插 30 块网卡。PCIe 插槽不够,成本也荒谬。排除。

答案二:在软件里做一个交换机。 每个虚拟机分一个虚拟网口,全都接到这个软件交换机上,交换机再从 eth0 出去。

这就是虚拟交换机(virtual switch)要解决的问题 —— 它是服务器内部的接入层交换机

Linux 内核早就自带了一个:Linux bridge

text
          ┌──────────────────────────────────────────┐
          │  VM1     VM2     VM3    ...   VM30       │
          │   │       │       │            │         │
          │  tap1    tap2    tap3         tap30      │
          │   └───────┴───────┴─────┬──────┘         │
          │                    ┌────┴────┐           │
          │                    │  br0    │  Linux    │
          │                    │         │  bridge   │
          │                    └────┬────┘           │
          │                       eth0               │
          └────────────────────────┬─────────────────┘
                              物理网络

Linux bridge 已经够用的场景

先说清楚:很多时候 Linux bridge 完全够用

你现在跑 Docker,默认的 docker0 就是一个 Linux bridge。容器之间二层互通、出网做 NAT,这套组合支撑了海量的生产负载。不要因为学了 OVS 就觉得 Linux bridge 是落后的东西。

Linux bridge 该干的事都干得挺好:

  • 学习 MAC 地址,维护转发表
  • 未知目的地址泛洪,已知地址单播转发
  • STP 防环
  • VLAN 过滤(内核 3.8 以后支持,用 bridge vlan 配置)

那什么时候不够?

四个 Linux bridge 接不住的需求

需求一:按 IP 或端口号做转发决策

"去 80 端口的流量走这条链路,去 443 的走那条。"

Linux bridge 的转发逻辑写死在内核 C 代码里,只做一件事:查目的 MAC,找出口。它不看 IP,不看 TCP 端口 —— 二层交换机本来就不该看。

你可以拿 nftablestc 在旁边打补丁,但那是在网桥之外另做一套东西,两套配置各管一半,很快就没人能说清一个包到底怎么走的。

需求二:跨物理机的二层网络

"机器 A 上的 VM1 和机器 B 上的 VM2,我要它们在同一个广播域里。"

这需要 Overlay:把二层帧封装进 UDP,跨过三层网络送到对面再解开。

内核有 vxlan 设备,你可以:

bash
ip link add vxlan10 type vxlan id 10 remote 192.0.2.2 dstport 4789
ip link set vxlan10 master br0

单条隧道能跑。但真实场景是几十个租户、每个租户一个 VNI、几百台宿主机两两之间的隧道 —— 用 ip link 一条条建,还要自己维护对端列表和租户到 VNI 的映射。规模一上来就没法管了。

需求三:从控制中心统一下发策略

"我要在一个地方定义整个集群的网络策略,自动同步到 500 台宿主机。"

Linux bridge 的配置接口只有本机的 ipbridge 命令。要远程管理,你得自己写 agent、自己定义协议、自己做状态同步和一致性。

需求四:知道每条流的流量

"这个虚拟机到那个数据库的流量有多大?"

Linux bridge 有每个端口的收发计数,但没有每条流的粒度。想要 per-flow 统计、端口镜像、流量采样,都得另外搭。

逐项对比

能力Linux bridgeOpen vSwitch
MAC 学习、泛洪、单播转发内置内置(NORMAL 动作)
STP支持支持,另有 RSTP
VLAN access / trunk支持(bridge vlan支持,且能被流表任意改写
按 IP / 协议 / 端口号转发不支持,需要 nftables/tc 旁路原生支持,流表可匹配几十个字段
多级流表 pipeline无此概念支持resubmit / goto_table
改写报文字段(NAT、改 MAC/VLAN)需要 nftables/tc流表动作原生支持
VXLAN / GRE / Geneve 隧道手工建隧道设备再桥接端口就是隧道,配置项里声明即可
远程集中配置无,只有本机命令OVSDB 协议,可远程读写
外部控制器接管转发OpenFlow 协议
每条流的统计无,只有端口级每条流表项自带包数/字节数
端口镜像需要 tc mirred内置 mirror
sFlow / NetFlow / IPFIX内置
有状态防火墙(连接跟踪)需要 nftablesct 动作,集成进流表
逐包转发路径追踪ofproto/trace

看这张表容易得出"OVS 全面胜出"的结论,但真正该记住的是最后一行之外的那个规律:多出来的能力,全都来自同一个设计选择

OVS 的核心思路:把转发决策变成数据

Linux bridge 和 OVS 最本质的区别在这里:

text
Linux bridge
    包 ──▶ ┌──────────────────────────┐ ──▶ 出口
           │ 内核里编译好的转发逻辑      │
           │  查 MAC 表 → 单播 / 泛洪   │
           └──────────────────────────┘
           行为固定。想改,得改内核代码。

Open vSwitch
    包 ──▶ ┌──────────────────────────┐ ──▶ 出口
           │ 一张流表(数据,不是代码)  │
           │  匹配任意字段 → 执行动作    │
           └──────────────────────────┘
           行为由表内容决定。想改,改表。

转发逻辑从代码变成了数据。数据可以远程下发、可以版本化、可以由控制器统一生成 —— 上面那些能力全是这一步的自然结果。

一张流表长这样(这就是第 2 部分的主要内容):

text
优先级  匹配条件                                动作
 200   tcp, tp_dst=80                          output:3
 200   tcp, tp_dst=443                         output:4
 150   dl_vlan=100                             pop_vlan, output:5
 100   ip, nw_src=10.0.0.0/8                   mod_nw_src:203.0.113.1, output:2
   0   (匹配所有包)                            NORMAL

最后那条 NORMAL 值得注意

NORMAL 这个动作的含义是"按传统二层交换机的方式处理这个包" —— 查 MAC 表、该泛洪就泛洪。

也就是说 Linux bridge 的全部行为,在 OVS 里只是流表中的一条特例。你新建一个 OVS 网桥,里面默认就有这一条 priority=0 actions=NORMAL,所以它开箱就是个普通交换机。你在实验 1 里会亲眼看到这条流表项。

可编程是有代价的

每个包都去查一张可能上万条、每条都能匹配几十个字段的表 —— 这比"查一下 MAC 表"慢太多了。如果 OVS 真这么干,性能会崩。

OVS 的解法是把自己拆成两层:

text
   ┌────────────────────────────────────────┐
   │ 用户态:完整的流表,慢,但什么都能表达      │
   │        只处理"没见过的包"                 │
   └───────────────┬────────────────────────┘
                   │ 处理完把结论缓存下去

   ┌────────────────────────────────────────┐
   │ 内核态:简单的精确匹配缓存,极快           │
   │        处理绝大多数包                    │
   └────────────────────────────────────────┘

一条连接的第一个包走上面那条慢路,OVS 把"这类包该怎么处理"的结论装进下面的缓存;后续的包全部在内核里命中缓存直接转发。

这个两级结构是理解 OVS 的关键,下一章会把它拆开讲,实验 1 里你会亲手验证它 —— 清空缓存后 ping 五个包,第一个包的 RTT 明显更高,因为只有它跑了完整的一趟。

OVS 现在跑在哪

不是实验室玩具:

  • OpenStack 的 Neutron 默认用 OVS 做计算节点的虚拟交换
  • OVN(Open Virtual Network)是 OVS 项目自己的上层控制面,ovn-kubernetesAntrea 这些 Kubernetes CNI 建立在它之上
  • 电信云 / NFV 大量使用 OVS-DPDK 做高性能转发
  • 内核里的 openvswitch 模块在 Linux 主线中,从 3.3 版本进入

小结

  • 虚拟交换机解决的是"服务器内部几十个虚拟网口怎么接入网络"
  • Linux bridge 把转发逻辑写死在内核里,够用、但只能按 MAC 转发
  • OVS 把转发逻辑变成一张可编程的流表,由此获得了 L3/L4 转发、报文改写、隧道、集中管理、per-flow 观测这些能力
  • 代价是可编程带来的性能开销,OVS 用"用户态决策 + 内核态缓存"的两级结构来抵消
  • NORMAL 动作让 OVS 可以完全退化成一个 Linux bridge

下一章:OVS 的三个进程 —— 把这个两级结构落到具体的进程和命令上。