Skip to content

实验 13:隧道原理 ​

实验 9 结尾提到一个数字:VLAN ID 只有 12 位,最多 4094 个。

这一章从这个限制出发,讲清楚 Overlay 网络为什么存在、隧道到底做了什么。下一章动手把它建起来。

VLAN 撑不住的三件事 ​

一、4094 个不够 ​

一个公有云上有几万个租户,每个租户至少要一个隔离的网络。4094 个 VLAN 一个月就分完了。

二、VLAN 是二层的,跨不过路由器 ​

VLAN 标签在以太网帧里。路由器转发时会重新构造以太网头,标签直接没了。

所以同一个 VLAN 必须在同一个二层域内。数据中心里这意味着:要让两台机器上的虚拟机在同一个 VLAN 里,这两台机器之间必须是二层可达的。 于是整个数据中心得是一个大二层 —— 广播域巨大、生成树收敛缓慢、故障域没边界。

三、物理交换机也要配 ​

每加一个 VLAN,路径上所有物理交换机的 trunk 口都得放行它。虚拟机迁移到另一个机架,可能整条链路的配置都要动。虚拟网络的变更被绑死在物理网络的配置上。

Overlay 的思路:套一层 ​

text
   想要的效果
   ┌──────────────────────────────────────────────┐
   │  h1 和 h2 觉得自己接在同一台交换机上           │
   │     h1 ──────── 虚拟的二层 ──────── h2        │
   └──────────────────────────────────────────────┘

   实际的物理情况
   ┌──────────────────────────────────────────────┐
   │  h1 ── 宿主机A ══ 三层网络(路由器若干)══ 宿主机B ── h2 │
   └──────────────────────────────────────────────┘

   做法:把 h1 发出的整个以太网帧塞进一个 UDP 包,
        通过三层网络送到宿主机B,再拆出来交给 h2。

这就是隧道:把一个协议的数据包整个当成载荷,塞进另一个协议的包里传输。

于是:

  • 租户数量由隧道标识决定,不再受 12 位限制
  • 不要求二层可达,只要三层能通就行(IP 路由已经解决了这个问题)
  • 物理网络不用感知虚拟网络,它只看到普通的 UDP 流量

underlay 和 overlay

  • underlay(承载网):真实的物理网络,用 IP 路由。上面跑的是隧道包
  • overlay(叠加网):虚拟机看到的那个网络。它"跑在" underlay 之上

两套网络的地址空间完全独立。overlay 里用 192.168.10.0/24 和 underlay 用什么地址毫无关系,重叠也没问题。

VXLAN 的封装格式 ​

text
原始帧(h1 发出来的,也叫内层帧)
┌──────────┬──────────┬─────────────────────────┐
│ 内层目MAC │ 内层源MAC │  内层 IP 包(h1→h2)      │
└──────────┴──────────┴─────────────────────────┘
                     │
                     │  ovs1 加上四层封装
                     ▼
┌──────────┬─────────┬─────────┬────────┬──────────────────┐
│ 外层以太头 │ 外层 IP  │ 外层 UDP │ VXLAN  │  原始帧(整个)    │
│  14 字节  │ 20 字节  │  8 字节  │ 8 字节  │                  │
└──────────┴─────────┴─────────┴───┬────┴──────────────────┘
              ↑          ↑          │
      underlay 地址   目的端口 4789   │
      10.0.0.1        源端口是哈希值  │
      → 10.0.0.2                    │
                    ┌───────────────┴────────────────┐
                    │ flags │ 保留 │   VNI   │ 保留   │
                    │ 1字节 │ 3字节│  3字节  │ 1字节  │
                    │       │      │  24 位  │        │
                    └────────────────────────────────┘

  总开销 = 14 + 20 + 8 + 8 = 50 字节
  VNI 24 位 → 1677 万个隧道标识

VNI 是 24 位,一千六百多万个。这是 VXLAN 取代 VLAN 的直接理由。

外层 UDP 源端口是个哈希值 ​

这个设计很巧妙。外层 UDP 的目的端口固定 4789(标识"这是 VXLAN"),但源端口是根据内层报文算出来的哈希。

看实验里抓到的真实包:

text
10.0.0.1.32922 > 10.0.0.2.4789: VXLAN, vni 100      ← 源端口 32922
10.0.0.1.48601 > 10.0.0.2.4789: VXLAN, vni 100      ← 源端口 48601

两个不同的内层流,外层源端口不同。目的是让 underlay 的路由器能做 ECMP —— 路由器按五元组哈希选路,源端口变化就意味着不同的内层流会走不同的物理路径,多条链路的带宽才用得上。

如果源端口固定,所有隧道流量都会挤在同一条物理路径上。

四种隧道类型 ​

OVS 支持的隧道类型(Open_vSwitch.iface_types 里能看到全部):

类型封装标识位数标准端口说明
vxlanUDP24 位 VNI4789最通用,硬件支持最广
geneveUDP24 位 VNI6081支持可变长度的元数据选项,OVN 的默认选择
greIP 协议 4732 位 key——不走 UDP,做不了 ECMP
stt伪装成 TCP64 位——能利用网卡的 TCP 分段卸载,性能好但非标准

在 OVS 里切换类型只需要改一个字段:

bash
ovs-vsctl set Interface vxlan0 type=geneve

实验里试过,改完立刻就通,datapath 里换成了 genev_sys_6081。

怎么选 ​

默认用 VXLAN。 它是事实标准,物理交换机和网卡的硬件卸载支持最好。

用 Geneve 的理由:需要在隧道头里携带自定义元数据。OVN 就靠这个传递逻辑端口 ID 等信息,这是 VXLAN 的固定头做不到的。

避免 GRE:它不走 UDP,underlay 的路由器无法用端口号做 ECMP,多路径带宽用不上。

三个必须注意的坑 ​

一、MTU ​

封装要吃掉 50 字节。underlay MTU 是 1500 的话,overlay 的可用 MTU 只有 1450。

text
   underlay MTU 1500
   ├── 外层封装 50 字节 ──┼────── overlay 可用 1450 ──────┤

虚拟机还按 1500 发包的话,封装后变成 1550,超过 underlay MTU:

  • 如果外层 IP 设了 DF 位(OVS 默认设),包被直接丢弃
  • 没设 DF 就会分片,性能大幅下降

症状很有特点:ping 通,小包正常,大包或者 TCP 传大文件就卡死。 这是隧道环境最经典的故障。

三种解法:

  1. underlay 用巨帧(推荐)。物理网络 MTU 设成 9000,overlay 保持 1500
  2. 调小 overlay MTU。虚拟机网卡设成 1450
  3. 靠 TCP MSS clamping。只能救 TCP,UDP 救不了

二、underlay 必须先通 ​

隧道建立在三层可达的基础上。配隧道之前先确认 underlay 能 ping 通:

bash
ping <对端的 underlay 地址>

这个顺序不能反。underlay 不通,隧道端口会显示成 up,但一个包都过不去。

三、两端 VNI 必须一致 ​

VNI 不匹配时,包能到对面,但会被丢弃(对面找不到对应 VNI 的隧道端口)。现象是"抓包能看到,但业务不通" —— 排查时很容易忽略。

隧道端口在 OVS 里的样子 ​

隧道端口就是一个 type=vxlan 的 Interface:

bash
ovs-vsctl add-port br0 vxlan0 \
  -- set Interface vxlan0 type=vxlan \
       options:remote_ip=10.0.0.2 options:key=100

常用的 options:

选项含义
remote_ip对端的 underlay 地址
local_ip本端 underlay 地址(多网卡时指定用哪个)
keyVNI。写 flow 表示由流表动态决定
dst_port改默认端口
df_default是否设 DF 位,默认 true
tos / ttl外层 IP 的字段,写 inherit 表示继承内层

key=flow 是多租户的关键

options:key=100 是固定 VNI。写成 options:key=flow 则由流表的 set_field:...->tun_id 动作决定 —— 一个隧道端口就能承载所有租户。

实验 15 会用到。

建好之后,它在网桥里就是个普通端口。 MAC 学习、VLAN、流表全都照常工作,转发逻辑一行都不用改 —— 这是 OVS 隧道设计最优雅的地方。

小结 ​

  • VLAN 的三个限制:只有 4094 个、跨不过路由器、物理网络要跟着配
  • Overlay 的做法是把整个以太网帧塞进 UDP 包,通过三层网络传输
  • underlay 和 overlay 的地址空间完全独立
  • VXLAN 封装开销 50 字节,VNI 24 位(1677 万)
  • 外层 UDP 源端口是内层报文的哈希,为了让 underlay 能做 ECMP
  • 默认选 VXLAN;需要携带元数据用 Geneve;避免 GRE(做不了 ECMP)
  • 三个坑:MTU(小包通大包不通)、underlay 必须先通、两端 VNI 必须一致
  • 隧道端口就是 type=vxlan 的 Interface,建好后和普通端口一样用

下一章:实验 14:打通 VXLAN,动手建一条隧道并抓包验证封装。