
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 社区维护，提供了丰富的网络虚拟化功能。

```mermaid
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 给上层管理系统

**数据库表结构：**
```
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 的核心）：
```go
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 表**：
```go
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 详细流程图

```mermaid
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）：

```bash
# 查看 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 协议封装：

```
+--------------------------------------------------+
| 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：

```bash
# 查看 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)

```mermaid
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（发送端）：**
```bash
# 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（接收端）：**
```bash
# 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 的流量也是隔离的：

```
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 同步机制

```mermaid
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 表关键字段：**

```go
type PortBinding struct {
    UUID          string   // 唯一标识
    Chassis       *string  // Pod 所在节点的 UUID（关键！）
    Datapath      string   // 所属的逻辑网络（VPC）
    LogicalPort   string   // 逻辑端口名称（Pod 名称）
    MAC           []string // MAC 地址
    TunnelKey     int      // 端口级 TunnelKey
    Encap         *string  // 隧道封装类型（geneve）
}
```

**查看实际状态：**

```bash
# 查看 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 间跨节点通信 | ✅ 实时同步<br/>✅ 支持多租户<br/>✅ 细粒度控制<br/>❌ 仅限集群内 |
| **BGP** | Underlay/外部 | BGP 协议 | 物理网络访问 Pod | ✅ 跨集群/外部<br/>✅ 标准协议<br/>✅ 可与物理设备互联<br/>❌ 需要物理设备支持<br/>❌ 无多租户隔离 |
| **ARP** | 同子网本地 | ARP 协议 | 二层网络内通信 | ✅ 简单高效<br/>✅ 无需配置<br/>❌ 仅限本地网络<br/>❌ 无法跨节点 |

#### 3.3.5 流量转发流程对比

**Overlay 模式（Port_Binding）：**

```mermaid
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）：**

```mermaid
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）：**

```mermaid
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 中排查

```bash
# 进入 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 中排查

```bash
# 进入 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 数据库（流量标识）

```bash
# 查看 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 网络连通性测试

```bash
# 进入 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 配置

```bash
# 查看 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 日志排查

```bash
# 查看 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

```bash
# 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 无法通信

```bash
# 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 无法通信

```bash
# 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 网络

```bash
# 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 发送端处理流程

```mermaid
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
```

**关键步骤详解：**

```bash
# 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 接收端处理流程

```mermaid
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: 转发数据包
```

**关键步骤详解：**

```bash
# 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 架构概览

```mermaid
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 发送端处理流程

```bash
# 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 接收端处理流程

```bash
# 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 工作机制

```mermaid
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 路由发布

```yaml
# 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 路由学习过程

```bash
# 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 流量路径

```mermaid
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 架构概览

```mermaid
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 配置

```yaml
# 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 流量转发流程

```bash
# 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 混合模式流量分析

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

```mermaid
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 流量流向概览

```mermaid
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)

**详细流程：**

```
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)

**详细流程：**

```
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 的工作原理

---

## 参考资料

- [Kube-OVN 官方文档](https://kubeovn.github.io/docs/)
- [OVN 架构文档](https://www.ovn.org/support/dist-docs/ovn_arch.7.pdf)
- [Open vSwitch 文档](https://docs.openvswitch.org/)
- [Geneve 协议规范](https://datatracker.ietf.org/doc/html/rfc8926)

