ᕕ( ᐛ )ᕗ Jimyag's Blog

深入理解 Kube-OVN 网络:从组件到实践

· 4569 字 · 约 22 分钟

Kube-OVN 是一个基于 OVN(Open Virtual Network)的 Kubernetes 网络插件,它将成熟的 SDN 技术引入云原生环境,提供了丰富的网络功能:

  • 多租户隔离:通过 VPC 实现网络多租户
  • 灵活的网络策略:支持 ACL、QoS、流量镜像等
  • 多种网络模式:Overlay、Underlay、Hybrid
  • 跨集群通信:支持多集群网络互联

本文将从以下四个维度深入分析:

  1. 组件架构:OVN 和 Kube-OVN 的核心组件及其作用
  2. 工作流程:从 Pod 创建到网络就绪的完整流程
  3. 隔离机制:VPC 如何实现多租户网络隔离
  4. 流量分析:不同场景下的网络流量路径

第一部分:组件架构

1.1 OVN 整体架构

OVN(Open Virtual Network)是一个开源的网络虚拟化系统,由 Open vSwitch 社区维护,提供了丰富的网络虚拟化功能。

  graph TB
    subgraph "控制平面"
        A[Cloud Management System<br/>如 Kube-OVN]
        B[OVN Northbound DB<br/>逻辑网络配置]
        C[ovn-northd<br/>转换引擎]
        D[OVN Southbound DB<br/>物理网络流表]
    end
    
    subgraph "数据平面 - 节点1"
        E1[ovn-controller]
        F1[ovs-vswitchd]
        G1[OVS Datapath]
        H1[VMs/Containers]
    end
    
    subgraph "数据平面 - 节点2"
        E2[ovn-controller]
        F2[ovs-vswitchd]
        G2[OVS Datapath]
        H2[VMs/Containers]
    end
    
    A -->|写入逻辑配置| B
    B -->|读取配置| C
    C -->|写入流表| D
    D -->|下发流表| E1
    D -->|下发流表| E2
    E1 -->|配置| F1
    E2 -->|配置| F2
    F1 -->|转发| G1
    F2 -->|转发| G2
    G1 -->|数据包| H1
    G2 -->|数据包| H2
    
    G1 <-->|Geneve Tunnel| G2

1.2 OVN 核心组件

1.2.1 OVN Northbound Database (ovn-nb-db)

作用: 存储逻辑网络配置,是 OVN 的北向接口数据库。

核心功能:

  • 存储逻辑网络拓扑(Logical Switch, Logical Router)
  • 存储网络策略(ACL, Load Balancer)
  • 存储端口配置(Logical Switch Port, Logical Router Port)
  • 提供北向 API 给上层管理系统

数据库表结构:

1
2
3
4
5
6
7
Logical_Switch        # 逻辑交换机(二层网络)
Logical_Router        # 逻辑路由器(三层网络)
Logical_Switch_Port   # 逻辑交换机端口
Logical_Router_Port   # 逻辑路由器端口
ACL                   # 访问控制列表
Load_Balancer         # 负载均衡器
DHCP_Options          # DHCP 配置

1.2.2 ovn-northd

作用: OVN 北向守护进程,将逻辑配置转换为逻辑流表。

核心功能:

  • 监听 OVN NB DB 的变化
  • 将逻辑网络配置转换为逻辑流表
  • 写入 OVN SB DB
  • 实现逻辑网络的语义(如路由、ACL、NAT)

1.2.3 OVN Southbound Database (ovn-sb-db)

作用: 存储逻辑流表和物理网络状态。

核心功能:

  • 存储逻辑流表(Logical_Flow)
  • 存储物理网络拓扑(Chassis, Encap)
  • 存储端口绑定状态(Port_Binding)
  • 存储隧道信息(Datapath_Binding)

关键表结构详解:

Datapath_Binding 表(区分不同 VPC 的核心):

1
2
3
4
5
6
type DatapathBinding struct {
    UUID          string            `ovsdb:"_uuid"`
    ExternalIDs   map[string]string `ovsdb:"external_ids"`
    LoadBalancers []string          `ovsdb:"load_balancers"`
    TunnelKey     int               `ovsdb:"tunnel_key" validate:"min=1,max=16777215"`
}
  • TunnelKey:即 VNI(Virtual Network Identifier),范围 1-16777215(24位)
  • 每个 Logical Switch/Router 都有一个唯一的 TunnelKey
  • 用于 Geneve 隧道封装时标识所属的逻辑网络

Port_Binding 表

1
2
3
4
5
6
7
8
9
type PortBinding struct {
    UUID          string  `ovsdb:"_uuid"`
    Datapath      string  `ovsdb:"datapath"`      // 指向 Datapath_Binding
    LogicalPort   string  `ovsdb:"logical_port"`   // 逻辑端口名称
    Chassis       *string `ovsdb:"chassis"`        // 绑定的物理节点
    TunnelKey     int     `ovsdb:"tunnel_key"`     // 端口级 TunnelKey
    MAC           []string `ovsdb:"mac"`           // MAC 地址
    Type          string  `ovsdb:"type"`           // 端口类型
}

1.2.4 ovn-controller

作用: 每个节点上运行的 OVN 控制器,将逻辑流表转换为物理流表。

核心功能:

  • 监听 OVN SB DB 的变化
  • 将逻辑流表转换为 OpenFlow 规则
  • 管理 OVS 网桥配置
  • 处理本地端口绑定

1.3 Kube-OVN 核心组件

1.3.1 kube-ovn-controller

作用: 中央控制器,监听 Kubernetes 资源并配置 OVN。

核心功能:

  • 监听 Pod、Service、Subnet、VPC 等 CRD
  • 转换 K8s 资源为 OVN 逻辑网络配置
  • IPAM(IP 地址管理)
  • 配置 ACL、Load Balancer 等

1.3.2 kube-ovn-daemon

作用: 每个节点上的守护进程,处理本地网络配置。

核心功能:

  • 处理 CNI RPC 请求
  • 配置 OVS 端口
  • 配置主机网络(网关、路由等)
  • 监控节点网络状态

1.3.3 kube-ovn-cni

作用: CNI 插件二进制,由 kubelet 调用。

核心功能:

  • 实现 CNI spec 接口(ADD、DEL、CHECK)
  • 与 kube-ovn-daemon 通过 gRPC 通信
  • 返回网络配置给 kubelet

第二部分:Pod 网络就绪流程

2.1 流程概览

从用户创建 Pod 到网络完全就绪,整个过程可以分为六个阶段:

  1. Pod 创建与调度:用户提交 Pod,scheduler 选择节点
  2. kube-ovn-controller 处理:分配 IP,创建 OVN 逻辑端口
  3. kubelet 创建容器:拉取镜像,准备容器运行时
  4. kube-ovn-daemon 配置网络:创建网络接口,配置 OVS
  5. ovn-controller 下发流表:将逻辑流表转为物理流表
  6. 网络就绪与数据包转发:Pod 可以正常通信

2.2 详细流程图

  sequenceDiagram
    participant User as 用户
    participant API as kube-apiserver
    participant Scheduler as scheduler
    participant Ctl as kube-ovn-controller
    participant IPAM as IPAM 模块
    participant NB as OVN NB DB
    participant Northd as ovn-northd
    participant SB as OVN SB DB
    participant Kubelet as kubelet
    participant CNI as kube-ovn-cni
    participant Daemon as kube-ovn-daemon
    participant OvnCtl as ovn-controller
    participant OVS as ovs-vswitchd
    participant Pod as Pod 容器

    User->>API: kubectl apply -f pod.yaml
    API->>API: 创建 Pod 对象(Pending)
    
    API->>Scheduler: 触发调度
    Scheduler->>Scheduler: 选择节点
    Scheduler->>API: 更新 Pod.spec.nodeName
    
    API->>Ctl: Informer 触发 AddPod
    Ctl->>IPAM: 分配 IP/MAC 地址
    IPAM-->>Ctl: 返回 IP/MAC
    Ctl->>API: 更新 Pod annotation
    
    Ctl->>NB: 创建 Logical Switch Port
    NB->>Northd: 触发转换
    Northd->>SB: 写入流表
    
    API->>Kubelet: 监听到 Pod 绑定
    Kubelet->>CNI: 调用 cmdAdd
    CNI->>Daemon: RPC 请求
    Daemon->>API: 查询 Pod 信息
    Daemon->>OVS: 创建 host interface
    Daemon->>OVS: 配置端口映射
    Daemon-->>CNI: 返回响应
    CNI-->>Kubelet: 返回结果
    
    Kubelet->>Pod: 创建容器
    Pod->>OVS: 发送数据包
    OVS->>OVS: 应用 OpenFlow 规则
    OVS->>Pod: 转发数据包
    
    OvnCtl->>SB: 监听 Port_Binding
    OvnCtl->>OVS: 下发物理流表
    
    API->>API: Pod 状态更新为 Running

第三部分:VPC 隔离机制

3.1 VPC 隔离的核心原理

Kube-OVN 通过 OVN 的 Logical Router 实现不同 VPC 的隔离:

关键点:

  • 每个 VPC 创建一个独立的 OVN Logical Router
  • Router 名称 = VPC 名称
  • 不同 VPC 的 Router 完全独立,实现三层隔离

3.2 VPC 流量区分机制详解

3.2.1 Datapath TunnelKey(VNI)分配

每个 Logical Switch/Router 在创建时,ovn-northd 会为其分配一个唯一的 Datapath TunnelKey(即 VNI):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 查看 Datapath_Binding 表
ovn-sbctl list Datapath_Binding

# 输出示例
_uuid               : a1b2c3d4-e5f6-7890-abcd-ef1234567890
external_ids        : {logical-router="vpc-a", name="lr-vpc-a"}
tunnel_key          : 1

_uuid               : b2c3d4e5-f6a7-8901-bcde-f12345678901
external_ids        : {logical-switch="subnet-a", name="ls-subnet-a"}
tunnel_key          : 2

_uuid               : c3d4e5f6-a7b8-9012-cdef-123456789012
external_ids        : {logical-router="vpc-b", name="lr-vpc-b"}
tunnel_key          : 3

关键说明:

  • tunnel_key:就是 VNI,范围 1-16777215(24位)
  • VPC-A 的 Logical Router:tunnel_key = 1
  • VPC-A 下的 Logical Switch:属于同一个 datapath,tunnel_key 相同
  • VPC-B 的 Logical Router:tunnel_key = 3,完全不同的 VNI

3.2.2 Geneve 隧道封装

当数据包跨节点传输时,OVN 使用 Geneve 协议封装:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
+--------------------------------------------------+
| Geneve Header                                    |
| - Version: 0                                     |
| - Opt Len: 变长                                  |
| - OAM/Critical: 标志位                          |
| - Proto: 0x6558 (Ethernet)                      |
| - VNI: tunnel_key (例如:1)                      |
+--------------------------------------------------+
| Outer UDP Header                                 |
| - Src Port: Random                               |
| - Dst Port: 6081 (Geneve)                       |
+--------------------------------------------------+
| Outer IP Header                                  |
| - Src IP: Node1_IP (例如:192.168.1.1)          |
| - Dst IP: Node2_IP (例如:192.168.1.2)          |
+--------------------------------------------------+
| Inner Ethernet Frame                             |
| - Src MAC: Pod-A MAC                             |
| - Dst MAC: Pod-B MAC                             |
| - Type: IPv4 (0x0800)                           |
+--------------------------------------------------+
| Inner IP Packet                                  |
| - Src IP: Pod-A IP                               |
| - Dst IP: Pod-B IP                              |
+--------------------------------------------------+

关键点:

  • VNI = tunnel_key:标识数据包属于哪个逻辑网络(VPC)
  • 接收端根据 VNI 值,将数据包转发到对应的 datapath

3.2.3 OVS 流表匹配机制

在接收端节点,ovn-controller 下发的 OpenFlow 规则会匹配 VNI:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 查看 OVS 流表
ovs-ofctl dump-flows br-int

# 示例流表(简化)
# table=0: 匹配 tunnel_id,设置 metadata
cookie=0x0, table=0, priority=100, tun_id=0x1 actions=load:0x1->NXM_NX_TUN_ID[0..31],load:0x2->NXM_NX_METADATA,in_port=1,output:2

# table=0: 匹配 tunnel_id=0x1 (VPC-A)
# actions=设置 metadata,转发到正确的端口

# table=0: 匹配 tunnel_id=0x3 (VPC-B)
cookie=0x0, table=0, priority=100, tun_id=0x3 actions=load:0x3->NXM_NX_TUN_ID[0..31],load:0x4->NXM_NX_METADATA,in_port=1,output:3

流表逻辑:

  1. 入方向:从隧道接口收到数据包

    • 匹配 tun_id(VNI)
    • 设置 metadata 标识所属 datapath
    • 根据目标 MAC/IP 查找输出端口
  2. 出方向:从 Pod 接口收到数据包

    • 匹配 in_port
    • 查找目标位置(本地或远程)
    • 如果远程,封装 Geneve 头部(设置 VNI)

3.2.4 跨节点流量转发完整流程

场景: Pod-A (VPC-A, Node1) → Pod-B (VPC-A, Node2)

  sequenceDiagram
    participant PodA as Pod-A<br>(10.0.1.2)
    participant OVS1 as OVS Node1
    participant Net as 物理网络
    participant OVS2 as OVS Node2
    participant PodB as Pod-B<br>(10.0.1.3)
    
    PodA->>OVS1: 数据包<br>(dst=10.0.1.3)
    Note over OVS1: table=0: 匹配 in_port
    Note over OVS1: table=1: 查找 MAC<br>发现目标在 Node2
    Note over OVS1: table=2: 路由查找
    Note over OVS1: 封装 Geneve<br>VNI=1 (VPC-A)
    OVS1->>Net: 发送封装包<br>(外层 IP: Node1→Node2)
    Net->>OVS2: 接收封装包
    Note over OVS2: table=0: 匹配 tun_id=1
    Note over OVS2: 解封装数据包
    Note over OVS2: table=1: 查找目标 MAC
    Note over OVS2: table=2: 输出到 Pod-B 端口
    OVS2->>PodB: 转发数据包

详细步骤:

Node1(发送端):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 1. Pod-A 发送数据包
#    src_ip=10.0.1.2, dst_ip=10.0.1.3
#    src_mac=pod-a-mac, dst_mac=gateway-mac(或直接是 pod-b-mac)

# 2. OVS br-int table=0 处理
#    匹配 in_port="pod-a"
#    设置 tunnel_id 和 metadata

# 3. OVS br-int table=1 MAC 学习
#    查找 MAC_Binding 表,发现 10.0.1.3 在 Node2

# 4. OVS br-int table=2 路由
#    确定输出端口为隧道接口

# 5. 封装 Geneve
#    VNI = 1 (VPC-A 的 datapath tunnel_key)
#    外层 IP: src=Node1_IP, dst=Node2_IP
#    外层 UDP: dst_port=6081

Node2(接收端):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 1. OVS br-int 从 genev_sys_6081 接收数据包
#    匹配 tun_id=1

# 2. 设置 metadata
#    load:0x1->NXM_NX_TUN_ID
#    load:datapath_metadata->NXM_NX_METADATA

# 3. 解封装
#    移除 Geneve 头部

# 4. table=1: 查找目标 MAC
#    匹配 eth_dst=pod-b-mac

# 5. 输出到 pod-b 端口
#    output:"pod-b"

3.2.5 同一节点内不同 VPC 的隔离

即使 Pod 在同一节点上,不同 VPC 的流量也是隔离的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
Node1:
  Pod-A1 (VPC-A) ---+
                    |
  Pod-A2 (VPC-A) ---+--> OVS br-int --> 不同 datapath (tunnel_key=1)
                    |
                    |    完全隔离(通过 metadata 匹配)
                    |
  Pod-B1 (VPC-B) ---+--> OVS br-int --> 不同 datapath (tunnel_key=3)
                    |
  Pod-B2 (VPC-B) ---+

OVS 流表逻辑:
- Pod-A1 的数据包:metadata=0x1(VPC-A)
- Pod-B1 的数据包:metadata=0x3(VPC-B)
- 流表匹配时,必须同时匹配 metadata,确保不会混淆

关键机制:

  • metadata 字段:OVS 内部用于标识 datapath
  • 流表匹配:不仅匹配 MAC/IP,还匹配 metadata
  • 端口隔离:不同 VPC 的端口属于不同的 datapath

3.3 VPC 内节点间路由机制详解

核心问题: 在 VPC 中,如果 Pod 在不同节点上,节点之间如何知道 Pod 的位置?

答案:通过 OVN Port_Binding 同步,而不是 BGP 或 ARP

3.3.1 为什么不是 BGP?

BGP 用于:

  • Underlay 模式:Pod 使用物理网络 IP,需要物理路由器学习路由
  • 外部访问:外部网络需要直接访问 Pod IP

不用于 Overlay VPC 内部通信,因为:

  • Overlay 是逻辑网络,对物理网络透明
  • VPC 是多租户隔离的,BGP 无法区分不同 VPC
  • BGP 路由表会过于庞大(每个 Pod 一个路由)

3.3.2 为什么不是 ARP?

ARP 用于:

  • 同子网内的二层通信
  • 解析 IP 地址对应的 MAC 地址

不用于 跨节点通信,因为:

  • ARP 只在本地网络(同一广播域)有效
  • 跨节点需要三层路由,不经过 ARP

3.3.3 Port_Binding 同步机制

  sequenceDiagram
    participant Ctl as kube-ovn-controller
    participant NB as OVN NB DB
    participant Northd as ovn-northd
    participant SB as OVN SB DB
    participant OvnCtl1 as ovn-controller<br/>(Node1)
    participant OvnCtl2 as ovn-controller<br/>(Node2)
    
    Note over Ctl: Pod-A 创建在 Node1
    Ctl->>NB: 创建 Logical Switch Port<br/>options:requested-chassis=Node1
    NB->>Northd: 触发转换
    Northd->>SB: 写入 Port_Binding<br/>chassis=Node1_UUID
    SB->>OvnCtl1: 监听到变化
    SB->>OvnCtl2: 监听到变化
    Note over OvnCtl1: 本地端口<br/>配置 OVS 流表
    Note over OvnCtl2: 学习到 Pod-A<br/>位置: Node1

Port_Binding 表关键字段:

1
2
3
4
5
6
7
8
9
type PortBinding struct {
    UUID          string   // 唯一标识
    Chassis       *string  // Pod 所在节点的 UUID(关键!)
    Datapath      string   // 所属的逻辑网络(VPC)
    LogicalPort   string   // 逻辑端口名称(Pod 名称)
    MAC           []string // MAC 地址
    TunnelKey     int      // 端口级 TunnelKey
    Encap         *string  // 隧道封装类型(geneve)
}

查看实际状态:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 查看 Port_Binding 表
ovn-sbctl list Port_Binding

# 查看特定 Pod 的位置
ovn-sbctl find Port_Binding logical_port=pod-default-my-pod

# 输出示例
_uuid               : a1b2c3d4-e5f6-7890-abcd-ef1234567890
chassis             : node1-uuid    # Pod 在 Node1
datapath            : vpc-a-dp      # 属于 VPC-A
logical_port        : pod-a         # 端口名称
mac                 : ["00:00:00:00:00:01"]
tunnel_key          : 1             # 端口 TunnelKey
encap               : geneve        # 使用 Geneve 封装

3.3.4 三种机制对比

机制 作用域 控制平面 适用场景 优缺点
Port_Binding Overlay VPC 内 OVN SB DB 同步 Pod 间跨节点通信 ✅ 实时同步
✅ 支持多租户
✅ 细粒度控制
❌ 仅限集群内
BGP Underlay/外部 BGP 协议 物理网络访问 Pod ✅ 跨集群/外部
✅ 标准协议
✅ 可与物理设备互联
❌ 需要物理设备支持
❌ 无多租户隔离
ARP 同子网本地 ARP 协议 二层网络内通信 ✅ 简单高效
✅ 无需配置
❌ 仅限本地网络
❌ 无法跨节点

3.3.5 流量转发流程对比

Overlay 模式(Port_Binding):

  sequenceDiagram
    participant PodA as Pod-A<br/>(Node1)
    participant OVS1 as OVS Node1
    participant SB as OVN SB DB
    participant OVS2 as OVS Node2
    participant PodB as Pod-B<br/>(Node2)
    
    PodA->>OVS1: 发送数据包<br/>dst=Pod-B-IP
    Note over OVS1: 查找本地流表
    Note over OVS1: 流表由 ovn-controller<br/>根据 Port_Binding 生成
    Note over OVS1: Port_Binding 显示<br/>Pod-B 在 Node2
    Note over OVS1: 封装 Geneve<br/>VNI=VPC-A
    OVS1->>OVS2: 发送到 Node2_IP
    Note over OVS2: 匹配 VNI<br/>解封装
    Note over OVS2: 根据 Port_Binding<br/>确定输出端口
    OVS2->>PodB: 转发数据包

Underlay 模式(BGP):

  sequenceDiagram
    participant PodA as Pod-A<br/>(Node1)
    participant Router as 物理路由器
    participant Node2 as Node2
    participant PodB as Pod-B<br/>(Node2)
    
    Note over Router: 通过 BGP 学习路由<br/>Pod-CIDR → Node-IP
    PodA->>Router: 发送数据包<br/>dst=Pod-B-IP
    Note over Router: 查找路由表<br/>Pod-B → Node2-IP
    Router->>Node2: 转发到 Node2
    Node2->>PodB: 本地转发

本地网络(ARP):

  sequenceDiagram
    participant PodA as Pod-A
    participant Bridge as OVS Bridge
    participant PodB as Pod-B<br/>(同节点)
    
    PodA->>Bridge: 发送数据包<br/>dst=Pod-B-IP
    Note over Bridge: ARP 解析<br/>Pod-B-IP → Pod-B-MAC
    Bridge->>PodB: 直接转发

3.3.6 什么时候使用哪种机制?

Overlay VPC 内(默认):

  • ✅ 使用 Port_Binding 同步
  • ❌ 不需要 BGP
  • ❌ 不需要 ARP(跨节点)

Underlay 网络:

  • ✅ 使用 BGP 发布路由到物理网络
  • ✅ 使用 ARP(同子网内)

外部访问 Pod:

  • ✅ 使用 BGP 发布 Pod 路由到外部网络

同节点同子网:

  • ✅ 使用 ARP 解析 MAC 地址
  • ✅ 直接二层转发

第四部分:常用排查命令

4.1 在 kube-ovn-controller Pod 中排查

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 进入 kube-ovn-controller Pod
kubectl exec -it -n kube-ovn deploy/kube-ovn-controller -- bash

# 查看 OVN NB 数据库
# 列出所有 Logical Switch
ovn-nbctl list Logical_Switch

# 列出所有 Logical Router
ovn-nbctl list Logical_Router

# 查看 VPC 对应的 Logical Router
ovn-nbctl lr-list | grep <vpc-name>

# 查看子网对应的 Logical Switch
ovn-nbctl ls-list | grep <subnet-name>

# 查看端口信息
ovn-nbctl list Logical_Switch_Port | grep <port-name>

# 查看路由策略
ovn-nbctl lr-route-list <vpc-name>

# 查看 ACL 规则
ovn-nbctl acl-list <logical-switch>

# 查看 Load Balancer
ovn-nbctl list Load_Balancer

4.2 在 kube-ovn-daemon Pod 中排查

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 进入 kube-ovn-daemon Pod(在目标节点上)
kubectl exec -it -n kube-ovn -l app=kube-ovn-daemon --node <node-name> -- bash

# 查看 OVS 网桥
ovs-vsctl show

# 查看端口信息
ovs-vsctl list Interface | grep <port-name>

# 查看端口统计
ovs-vsctl get Interface <port-name> statistics

# 查看 OpenFlow 流表
ovs-ofctl dump-flows br-int

# 查看流表(带详细信息)
ovs-ofctl dump-flows br-int --names

# 查看端口状态
ovs-ofctl show br-int

# 查看端口映射
ovs-vsctl get Interface <port-name> external_ids

# 抓包分析
tcpdump -i <interface> -nn -vvv

4.3 查看 OVN SB 数据库(流量标识)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 查看 Datapath_Binding(VNI 分配)
ovn-sbctl list Datapath_Binding

# 查看端口绑定状态
ovn-sbctl list Port_Binding | grep <port-name>

# 查看端口在哪个节点上
ovn-sbctl list Port_Binding | grep -A 5 <port-name> | grep chassis

# 查看 Chassis 信息(节点注册)
ovn-sbctl list Chassis

# 查看逻辑流表
ovn-sbctl lflow-list

# 查看特定 datapath 的流表
ovn-sbctl lflow-list <datapath-uuid>

# 查看 MAC 绑定
ovn-sbctl list MAC_Binding

4.4 网络连通性测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 进入 Pod 网络命名空间
kubectl exec -it <pod-name> -- sh

# 查看 Pod 网络接口
ip addr
ip link

# 查看 Pod 路由表
ip route

# 查看 Pod ARP 表
ip neigh

# Ping 测试
ping <target-ip>

# 查看网络连通性
# 从 Pod 内部
kubectl exec -it <pod-name> -- ping <target-ip>

# 从节点
kubectl exec -it -n kube-ovn -l app=kube-ovn-daemon -- ping <pod-ip>

# 抓包分析(在节点上)
# 找到 Pod 的 veth pair 名称
kubectl get pod <pod-name> -o jsonpath='{.metadata.name}'

# 在节点上查找对应的接口
ip link | grep <pod-name>

# 抓包
tcpdump -i <interface> -nn -vvv

4.5 查看 VPC 配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 查看 VPC CRD
kubectl get vpc

# 查看 VPC 详情
kubectl describe vpc <vpc-name>

# 查看 Subnet CRD
kubectl get subnet

# 查看 Subnet 详情
kubectl describe subnet <subnet-name>

# 查看 Pod annotation(IP/MAC)
kubectl get pod <pod-name> -o jsonpath='{.metadata.annotations}'

# 查看特定 annotation
kubectl get pod <pod-name> -o jsonpath='{.metadata.annotations.ovn\.kubernetes\.io/ip_address}'
kubectl get pod <pod-name> -o jsonpath='{.metadata.annotations.ovn\.kubernetes\.io/mac_address}'

4.6 日志排查

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 查看 kube-ovn-controller 日志
kubectl logs -n kube-ovn deploy/kube-ovn-controller --tail=100 -f

# 查看 kube-ovn-daemon 日志
kubectl logs -n kube-ovn -l app=kube-ovn-daemon --tail=100 -f

# 查看特定节点的 daemon 日志
kubectl logs -n kube-ovn -l app=kube-ovn-daemon --field-selector spec.nodeName=<node-name>

# 搜索错误日志
kubectl logs -n kube-ovn deploy/kube-ovn-controller | grep -i error
kubectl logs -n kube-ovn deploy/kube-ovn-controller | grep -i "failed to"

# 查看事件
kubectl get events --sort-by='.lastTimestamp'
kubectl get events -n kube-ovn --sort-by='.lastTimestamp'

4.7 常见问题排查流程

4.7.1 Pod 无法获取 IP

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 1. 检查子网状态
kubectl get subnet
kubectl describe subnet <subnet-name>

# 2. 检查子网是否有可用 IP
kubectl get subnet <subnet-name> -o jsonpath='{.status.availableIPs}'

# 3. 查看 controller 日志
kubectl logs -n kube-ovn deploy/kube-ovn-controller | grep -i "allocate"

# 4. 检查 webhook 是否正常
kubectl get pods -n kube-ovn -l app=kube-ovn-webhook
kubectl logs -n kube-ovn -l app=kube-ovn-webhook

4.7.2 Pod 无法通信

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1. 检查 Pod IP/MAC annotation
kubectl get pod <pod-name> -o jsonpath='{.metadata.annotations}'

# 2. 检查 OVN NB 端口是否创建
ovn-nbctl list Logical_Switch_Port | grep <pod-name>

# 3. 检查 OVN SB 端口绑定状态
ovn-sbctl list Port_Binding | grep <pod-name>

# 4. 检查端口是否绑定到正确节点
ovn-sbctl list Port_Binding | grep -A 10 <pod-name> | grep chassis

# 5. 检查 OVS 端口是否创建
ovs-vsctl show | grep <pod-name>

# 6. 检查流表是否正确下发
ovs-ofctl dump-flows br-int | grep <port-number>

# 7. 抓包分析
# 在源 Pod 所在节点
tcpdump -i genev_sys_6081 -nn -vvv
# 在目标 Pod 所在节点
tcpdump -i <pod-interface> -nn -vvv

4.7.3 跨 VPC 无法通信

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 1. 检查 VPC Peering 配置
kubectl get vpc <vpc-name> -o yaml | grep -A 20 staticRoutes

# 2. 检查 Peer Router Port
ovn-nbctl list Logical_Router_Port | grep <vpc-name>

# 3. 检查路由是否正确
ovn-nbctl lr-route-list <vpc-a-name>
ovn-nbctl lr-route-list <vpc-b-name>

# 4. 检查 ACL 是否阻止
ovn-nbctl acl-list <logical-switch>

# 5. 测试连通性
kubectl exec -it <pod-a> -- ping <pod-b-ip>

4.7.4 节点无法访问 Pod 网络

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 1. 检查节点网关配置
kubectl get node <node-name> -o jsonpath='{.metadata.annotations}'

# 2. 检查 ovn0 接口
ip addr show ovn0
ip link show ovn0

# 3. 检查路由表
ip route | grep <pod-cidr>

# 4. 检查 OVS 端口
ovs-vsctl show | grep node-

# 5. 检查流表
ovs-ofctl dump-flows br-int | grep node

第五部分:网络模式与流量转发

Kube-OVN 支持多种网络模式,不同的模式下流量转发机制完全不同。

5.1 网络模式对比

模式 特点 适用场景 流量转发
Overlay 逻辑网络隔离,使用隧道封装 多租户、网络隔离需求强 Geneve/VXLAN 隧道
Underlay 使用物理网络,Pod 有真实 IP 高性能、与物理网络互通 物理网络直接路由
BGP 发布路由到外部网络 需要外部直接访问 Pod BGP 路由学习
U2O 互联 Underlay 与 Overlay 互通 混合网络场景 通过网关转发

5.2 Overlay 模式流量转发

场景: Pod-A (Node1, VPC-A) → Pod-B (Node2, VPC-A)

5.2.1 发送端处理流程

  sequenceDiagram
    participant Pod as Pod-A<br>(10.0.1.2)
    participant OVS as OVS br-int
    participant Tunnel as genev_sys_6081
    participant Network as 物理网络
    
    Pod->>OVS: 数据包<br>src=10.0.1.2, dst=10.0.1.3
    Note over OVS: table=0: 匹配 in_port
    Note over OVS: table=1: 查找 MAC<br>发现目标在 Node2
    Note over OVS: table=2: 路由查找<br>确定输出端口
    Note over OVS: 准备封装参数:<br>VNI=1, dst=Node2_IP
    OVS->>Tunnel: 封装 Geneve
    Note over Tunnel: 添加外层 IP/UDP<br>内层保持原始数据包
    Tunnel->>Network: 发送到 Node2

关键步骤详解:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1. Pod 发送原始数据包
# Ethernet Header:
#   src_mac: 00:00:00:00:00:01 (Pod-A MAC)
#   dst_mac: 00:00:00:00:00:03 (Pod-B MAC, 通过 ARP 获取)
# IP Header:
#   src_ip: 10.0.1.2
#   dst_ip: 10.0.1.3

# 2. OVS br-int 处理
# table=0: 匹配 in_port="ovn-pod-a"
ovs-ofctl dump-flows br-int | grep "in_port=\"ovn-pod-a\""

# 示例流表
cookie=0x0, table=0, priority=100, in_port="ovn-pod-a"
  actions=load:0x1->NXM_NX_TUN_ID[],load:0x2->NXM_NX_METADATA[],output:2

# table=1: MAC 地址查找
# 查找 MAC_Binding 表,发现 00:00:00:00:00:03 在 Node2

# table=2: 封装 Geneve
# VNI = 1 (VPC-A 的 datapath tunnel_key)
# 外层 IP: src=Node1_IP, dst=Node2_IP
# 外层 UDP: dst_port=6081

5.2.2 接收端处理流程

  sequenceDiagram
    participant Network as 物理网络
    participant Tunnel as genev_sys_6081
    participant OVS as OVS br-int
    participant Pod as Pod-B<br>(10.0.1.3)
    
    Network->>Tunnel: 接收 Geneve 包
    Note over Tunnel: 解析外层 IP/UDP
    Note over OVS: table=0: 匹配 tun_id=1
    Note over OVS: 设置 metadata<br>标识 VPC-A
    Note over OVS: 解封装数据包
    Note over OVS: table=1: 匹配 dst_mac
    Note over OVS: table=2: 输出到 Pod-B 端口
    OVS->>Pod: 转发数据包

关键步骤详解:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 1. 从隧道接口接收数据包
# 外层 IP: dst=Node2_IP
# 外层 UDP: dst_port=6081 (Geneve)
# Geneve Header: VNI=1

# 2. OVS br-int 处理
# table=0: 匹配 tun_id=1
ovs-ofctl dump-flows br-int | grep "tun_id=0x1"

# 示例流表
cookie=0x0, table=0, priority=100, tun_id=0x1
  actions=load:0x1->NXM_NX_TUN_ID[],load:0x3->NXM_NX_METADATA[],output:"ovn-pod-b"

# 3. 解封装并转发
# 移除 Geneve 头部
# 匹配 dst_mac=00:00:00:00:00:03
# 输出到 ovn-pod-b 端口

5.3 Underlay 模式流量转发

场景: Pod-A (Node1, ProviderNetwork, VLAN 100) → Pod-B (Node2, ProviderNetwork, VLAN 100)

5.3.1 架构概览

  graph TB
    subgraph "Node1"
        A1[Pod-A<br>192.168.1.2]
        B1[OVS br-phys]
        C1[物理网卡 eth0]
    end
    
    subgraph "物理网络"
        D[交换机<br>VLAN 100]
    end
    
    subgraph "Node2"
        A2[Pod-B<br>192.168.1.3]
        B2[OVS br-phys]
        C2[物理网卡 eth0]
    end
    
    A1 -->|veth pair| B1
    B1 -->|VLAN tag=100| C1
    C1 -.->|VLAN trunk| D
    D -.->|VLAN trunk| C2
    C2 -->|VLAN tag=100| B2
    B2 -->|veth pair| A2

5.3.2 发送端处理流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 1. Provider Network 配置
# 创建 ProviderNetwork CRD
apiVersion: kubeovn.io/v1
kind: ProviderNetwork
metadata:
  name: net1
spec:
  defaultInterface: eth0
  excludedNodes: []
  customInterfaces: []

# 2. 节点上创建 OVS bridge
# kube-ovn-daemon 会自动配置
ovs-vsctl add-br br-phys
ovs-vsctl add-port br-phys eth0

# 3. 创建 VLAN
apiVersion: kubeovn.io/v1
kind: Vlan
metadata:
  name: vlan100
spec:
  provider: net1
  vlanId: 100
  id: 100

# 4. 创建 Subnet 使用 VLAN
apiVersion: kubeovn.io/v1
kind: Subnet
metadata:
  name: subnet-underlay
spec:
  provider: net1
  vlan: vlan100
  cidrBlock: 192.168.1.0/24
  gateway: 192.168.1.1

# 5. Pod 发送数据包(无隧道封装)
# Ethernet Header:
#   src_mac: Pod-A MAC
#   dst_mac: Pod-B MAC(通过 ARP 获取,物理网络学习)
# VLAN Header: tag=100
# IP Header:
#   src_ip: 192.168.1.2
#   dst_ip: 192.168.1.3

# 6. OVS br-phys 处理
# 直接转发到物理网卡
# 添加 VLAN tag=100

关键点:

  • 无隧道封装:数据包直接在物理网络传输
  • VLAN 标签:用于隔离不同子网
  • 物理网络路由:依赖物理网络设备路由

5.3.3 接收端处理流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 1. 物理网卡接收数据包
# 已经带有 VLAN tag=100

# 2. OVS br-phys 处理
# 匹配 VLAN tag
ovs-ofctl dump-flows br-phys | grep "dl_vlan=100"

# 示例流表
cookie=0x0, table=0, priority=100, dl_vlan=100
  actions=strip_vlan,output:"ovn-pod-b"

# 3. 移除 VLAN tag,转发到 Pod

关键点:

  • 物理网络学习:交换机通过 MAC 学习知道 Pod-B 在哪个端口
  • VLAN 隔离:不同 VLAN 的流量完全隔离
  • 高性能:无隧道封装开销

5.4 BGP 路由发布

场景: 外部网络需要直接访问 Pod IP

5.4.1 BGP Speaker 工作机制

  sequenceDiagram
    participant Subnet as Subnet CRD
    participant Speaker as kube-ovn-speaker
    participant BGP as GoBGP Server
    participant Router as 物理路由器
    
    Subnet->>Speaker: 监听 annotation<br>ovn.kubernetes.io/bgp=true
    Speaker->>Speaker: 计算要发布的路由
    Note over Speaker: 策略:<br>- cluster: 所有节点发布<br>- local: 本地节点发布
    Speaker->>BGP: 添加 BGP 路由
    BGP->>Router: BGP UPDATE
    Note over Router: 学习路由:<br>Pod-CIDR → Node-IP

5.4.2 配置 BGP 路由发布

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 1. 启用 Subnet 的 BGP 发布
apiVersion: kubeovn.io/v1
kind: Subnet
metadata:
  name: subnet-bgp
  annotations:
    ovn.kubernetes.io/bgp: "true"  # 发布整个子网 CIDR
spec:
  cidrBlock: 10.16.0.0/16
  
# 2. 启用 Pod 的 BGP 发布
apiVersion: v1
kind: Pod
metadata:
  name: pod-bgp
  annotations:
    ovn.kubernetes.io/bgp: "local"  # 只从本节点发布 Pod IP
spec:
  containers:
  - name: nginx
    image: nginx

# 3. 启用 Service ClusterIP 的 BGP 发布
apiVersion: v1
kind: Service
metadata:
  name: svc-bgp
  annotations:
    ovn.kubernetes.io/bgp: "true"
spec:
  type: ClusterIP
  clusterIPs:
  - 10.96.0.100

5.4.3 BGP 路由学习过程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 1. kube-ovn-speaker 配置
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: kube-ovn-speaker
spec:
  template:
    spec:
      containers:
      - name: kube-ovn-speaker
        args:
        - --neighbor-address=192.168.1.1  # BGP 邻居(物理路由器)
        - --neighbor-as=65000            # 邻居 AS 号
        - --cluster-as=65001              # 集群 AS 号
        - --router-id=$(POD_IP)

# 2. 查看发布的路由
# 在 kube-ovn-speaker Pod 中
kubectl exec -it -n kube-ovn kube-ovn-speaker-xxx -- gobgp global rib

# 输出示例
Network          Next Hop        AS_PATH    Age        Attrs
10.16.0.0/16     192.168.1.10    65001      00:01:23   origin: i-incomplete
10.16.0.2/32     192.168.1.10    65001      00:00:45   origin: i-incomplete

# 3. 在物理路由器上查看路由
# Cisco 示例
show ip route bgp
# 输出
     10.16.0.0/16 [20/0] via 192.168.1.10, 00:01:23
     10.16.0.2/32  [20/0] via 192.168.1.10, 00:00:45

# Juniper 示例
show route protocol bgp
# 输出
10.16.0.0/16    *[BGP/20] 00:01:23, localpref 100
                  > to 192.168.1.10 via eth0
10.16.0.2/32     *[BGP/20] 00:00:45, localpref 100
                  > to 192.168.1.10 via eth0

5.4.4 外部访问 Pod 流量路径

  sequenceDiagram
    participant Client as 外部客户端<br>192.168.2.100
    participant Router as 物理路由器
    participant Node as K8s 节点<br>192.168.1.10
    participant OVS as OVS br-int
    participant Pod as Pod<br>10.16.0.2
    
    Client->>Router: 发送数据包<br>dst=10.16.0.2
    Note over Router: 查找路由表<br>10.16.0.2 → 192.168.1.10
    Router->>Node: 转发数据包<br>外层 IP: src=Client, dst=Node
    Note over Node: 内层 IP: src=Client, dst=Pod
    Node->>OVS: 接收数据包
    Note over OVS: 查找目标 MAC<br>转发到 Pod 端口
    OVS->>Pod: 转发数据包
    
    Note over Pod: 处理请求
    
    Pod->>OVS: 发送响应<br>src=10.16.0.2, dst=192.168.2.100
    Note over OVS: 查找路由<br>目标在节点外
    OVS->>Node: 转发到节点网络
    Node->>Router: 发送响应包
    Router->>Client: 路由转发

关键点:

  • 下一跳是 Node IP:路由器将 Pod IP 的下一跳设为 Node IP
  • 节点负责最后一段:节点接收数据包后,通过 OVS 转发到 Pod
  • 对称路由:响应流量通过相同路径返回

5.5 U2O Interconnection (Underlay ↔ Overlay 互联)

场景: Underlay Pod (VLAN 100) 需要访问 Overlay Pod (Geneve 隧道)

5.5.1 架构概览

  graph TB
    subgraph "Underlay Network"
        A1[Pod-Underlay<br>192.168.1.2]
        B1[OVS br-phys<br>VLAN 100]
        C1[物理网络]
    end
    
    subgraph "U2O Gateway"
        D[U2O Interconnection<br>192.168.1.254 ↔ 10.0.1.254]
    end
    
    subgraph "Overlay Network"
        A2[Pod-Overlay<br>10.0.1.2]
        B2[OVS br-int<br>Geneve Tunnel]
    end
    
    A1 --> B1
    B1 --> C1
    C1 -.-> D
    D -.-> B2
    B2 --> A2

5.5.2 U2O 配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 1. Underlay Subnet
apiVersion: kubeovn.io/v1
kind: Subnet
metadata:
  name: subnet-underlay
spec:
  provider: net1
  vlan: vlan100
  cidrBlock: 192.168.1.0/24
  gateway: 192.168.1.1
  u2oInterconnection: true  # 启用 U2O 互联

# 2. Overlay Subnet
apiVersion: kubeovn.io/v1
kind: Subnet
metadata:
  name: subnet-overlay
spec:
  cidrBlock: 10.0.1.0/24
  gateway: 10.0.1.1

5.5.3 U2O 流量转发流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1. Underlay Pod 发送数据包
# src_ip: 192.168.1.2 (Underlay Pod)
# dst_ip: 10.0.1.2 (Overlay Pod)

# 2. Underlay Subnet 的路由表
# 默认网关: 192.168.1.254 (U2O Gateway IP)
ip route
# default via 192.168.1.254

# 3. U2O Gateway 处理
# 接收数据包,查找路由表
# 目标 10.0.1.2 在 Overlay 网络
# 封装 Geneve 包,VNI=Overlay Datapath ID

# 4. 发送到 Overlay 网络
# 外层 IP: src=Node_IP, dst=Overlay_Node_IP
# Geneve: VNI=Overlay_VNI
# 内层 IP: src=192.168.1.2, dst=10.0.1.2

# 5. Overlay Node 接收
# 匹配 VNI,解封装
# 转发到 Pod-Overlay

关键机制:

  • U2O Gateway IP:Underlay 子网的特殊网关 IP
  • NAT 转换:可能需要 NAT(取决于配置)
  • 路由策略:U2O Gateway 维护双向路由表

5.6 混合模式流量分析

在实际生产环境中,可能同时存在多种网络模式:

  graph TB
    subgraph "Underlay Pods"
        A1[Pod-1<br>192.168.1.2<br>VLAN 100]
        A2[Pod-2<br>192.168.1.3<br>VLAN 100]
    end
    
    subgraph "Overlay Pods"
        B1[Pod-3<br>10.0.1.2<br>VPC-A]
        B2[Pod-4<br>10.1.1.2<br>VPC-B]
    end
    
    subgraph "BGP Published"
        C1[Service<br>10.96.0.100<br>ClusterIP]
    end
    
    subgraph "External"
        D[外部客户端]
    end
    
    A1 -.->|VLAN 100| A2
    B1 -.->|Geneve VNI=1| B2
    
    A1 -.->|U2O Gateway| B1
    B2 -.->|U2O Gateway| A2
    
    D -->|BGP Route| C1
    C1 -.->|Overlay| B1

第六部分:VPC 间流量分析

5.1 流量流向概览

  graph TB
    subgraph "VPC-A (VRF-A)"
        A1[Pod-A1<br>10.0.1.2]
        A2[Pod-A2<br>10.0.1.3]
        LS-A[Logical Switch<br>subnet-a]
        LR-A[Logical Router<br>vpc-a]
    end
    
    subgraph "VPC-B (VRF-B)"
        B1[Pod-B1<br>10.1.1.2]
        B2[Pod-B2<br>10.1.1.3]
        LS-B[Logical Switch<br>subnet-b]
        LR-B[Logical Router<br>vpc-b]
    end
    
    subgraph "Peering Connection"
        PEER-A[Peer Port<br>vpc-a-vpc-b]
        PEER-B[Peer Port<br>vpc-b-vpc-a]
    end
    
    A1 --> LS-A
    A2 --> LS-A
    LS-A --> LR-A
    LR-A --> PEER-A
    PEER-A -.-> PEER-B
    PEER-B --> LR-B
    LR-B --> LS-B
    LS-B --> B1
    LS-B --> B2

5.2 同 VPC 内 Pod 通信(跨节点)

场景: Pod-A1 (Node1, VPC-A) → Pod-A2 (Node2, VPC-A)

详细流程:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
Node1:
  1. Pod-A1 发送数据包
     - src_ip: 10.0.1.2
     - dst_ip: 10.0.1.3
     - src_mac: 00:00:00:00:00:01
     - dst_mac: 00:00:00:00:00:03 (通过 ARP 或 L3 获取)

  2. veth pair: eth0 (Pod) ↔ ovn-pod-a1 (Node)
     - 数据包进入 OVS br-int

  3. OVS table=0: 入口处理
     - 匹配 in_port="ovn-pod-a1"
     - 设置 tunnel_id 和 metadata
     - metadata = VPC-A 的 datapath identifier

  4. OVS table=1: MAC 查找
     - 查找 eth_dst=00:00:00:00:00:03
     - 发现目标在远程节点 Node2

  5. OVS table=2: 封装准备
     - 查找输出端口: tunnel interface (genev_sys_6081)
     - 准备 Geneve 封装

  6. Geneve 封装
     - VNI = tunnel_key = 1 (VPC-A 的 Datapath_Binding.tunnel_key)
     - 外层 IP: src=Node1_IP, dst=Node2_IP
     - 外层 UDP: dst_port=6081

物理网络:
  7. 数据包通过物理网络传输

Node2:
  8. OVS br-int 从 genev_sys_6081 接收
     - 匹配 tun_id=0x1 (VNI=1)
     - 设置 metadata(标识 VPC-A)

  9. OVS table=0: 解封装
     - 移除 Geneve 头部
     - 恢复原始以太网帧

  10. OVS table=1: MAC 匹配
      - 匹配 eth_dst=00:00:00:00:00:03
      - 确定输出端口为 ovn-pod-a2

  11. OVS table=2: 输出
      - 输出到 ovn-pod-a2 端口

  12. veth pair: ovn-pod-a2 (Node) ↔ eth0 (Pod)
      - 数据包到达 Pod-A2

  13. Pod-A2 接收数据包

5.3 跨 VPC Pod 通信

场景: Pod-A1 (VPC-A, Node1) → Pod-B1 (VPC-B, Node2)

详细流程:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
VPC-A (Node1):
  1. Pod-A1 发送数据包
     - src_ip: 10.0.1.2
     - dst_ip: 10.1.1.2 (不同 VPC 的 IP)

  2. OVS table=0: 匹配 in_port="ovn-pod-a1"
     - 设置 metadata = VPC-A identifier

  3. OVS table=1: L2/L3 查找
     - 发现目标 IP 不在本 VPC 子网内
     - 需要路由到 VPC Router

  4. OVS table=2: 路由处理
     - 发送到 VPC-A 的 Logical Router
     - 匹配静态路由: dst=10.1.0.0/16, next_hop=10.0.255.2

  5. Logical Router: 路由转发
     - 查找路由表
     - 输出到 Peer Router Port: vpc-a-vpc-b

  6. OVS table=3: 发送到 Peer Port
     - 数据包进入 VPC-A ↔ VPC-B 的连接

VPC-B (同一节点或不同节点):
  7. 数据包从 Peer Port 接收
     - Peer Port: vpc-b-vpc-a

  8. OVS table=0: 匹配 in_port="vpc-b-vpc-a"
     - 设置 metadata = VPC-B identifier

  9. OVS table=1: L2 查找
     - 查找 MAC: 00:00:00:00:00:04 (Pod-B1)
     - 发现目标在远程节点 Node2

  10. OVS table=2: 封装 Geneve
      - VNI = tunnel_key = 3 (VPC-B 的 Datapath_Binding.tunnel_key)
      - 外层 IP: src=Node_IP, dst=Node2_IP

  11. Geneve 封装发送

Node2 (VPC-B):
  12. OVS br-int 接收
      - 匹配 tun_id=0x3 (VNI=3, VPC-B)
      - 设置 metadata = VPC-B identifier

  13. OVS table=1: MAC 匹配
      - 匹配 eth_dst=00:00:00:00:00:04

  14. 输出到 Pod-B1
      - 输出端口: ovn-pod-b1

关键点:

  • VPC-A 到 VPC-B:通过 Peer Router Port 转发,不涉及隧道
  • VPC-B 内跨节点:使用 VPC-B 的 VNI(tunnel_key=3)封装
  • metadata 隔离:不同 VPC 的流量在 OVS 中通过 metadata 区分

总结

核心知识点回顾

  1. 组件架构

    • OVN 采用三层架构:管理平面(NB DB)、控制平面(northd、SB DB)、数据平面(ovn-controller、OVS)
    • Kube-OVN 在 OVN 之上构建了 K8s 友好的抽象层
  2. VPC 流量区分机制

    • Datapath_Binding.tunnel_key:即 VNI,范围 1-16777215(24位)
    • Geneve 封装:跨节点传输时携带 VNI
    • OVS metadata:节点内通过 metadata 标识不同 VPC
    • 流表匹配:不仅匹配 MAC/IP,还匹配 metadata/tunnel_id
  3. Pod 网络就绪流程

    • 六个阶段:调度 → IP 分配 → OVN 端口 → 容器创建 → 网络配置 → 流表下发
  4. 排查要点

    • OVN NB/SB 数据库是配置和状态的真相来源
    • OVS 流表是数据转发的关键
    • Port_Binding 的 chassis 字段标识端口在哪个节点上
    • VNI/metadata 是区分 VPC 流量的核心标识

实践建议

  1. 熟练使用 ovn-nbctl、ovn-sbctl、ovs-vsctl、ovs-ofctl 命令
  2. 理解 Datapath_Binding 和 Port_Binding 的作用
  3. 掌握 OVS 流表的匹配逻辑
  4. 善用 tcpdump 和日志排查问题
  5. 理解 VPC 隔离和 VPC Peering 的工作原理

参考资料

#Kubernetes #Kube-Ovn #Network #Ovn #Sdn