OVN 使用3云主机节点部署与验证报告
1. 环境概述
1.1 节点信息
| 节点 | 角色 | 公网 IP (EIP) | 内网 IP | 运营商 | 主机名 |
|---|---|---|---|---|---|
| node1 | Central | 27.152.182.118 | 172.20.2.2 | 电信 | i-grtdkmrwgiygkztb-xmmp02 |
| node2 | Chassis | 27.152.182.117 | 172.20.2.3 | 电信 | i-gu2timrtgnqtozbt-xmmp02 |
| node3 | Chassis | 112.51.122.81 | 172.20.2.8 | 移动 | i-g42taojwgq2tgmjy-xmmp02 |
1.2 软件版本
| 组件 | 版本 |
|---|---|
| OVN | 24.03.6 |
| OVS | 3.3.4 |
| DB Schema | 7.3.0 |
| OS | Ubuntu 24.04 (kernel 6.8.0-53-generic) |
1.3 双网段设计
本环境每个节点拥有两个 IP 地址,承担不同职责:
┌─────────────────────────────────────────────────────────┐
│ 公网 (EIP) │
│ 用于操作机 SSH 远程连接,执行部署和运维命令 │
│ │
│ node1 EIP ←──SSH──→ 操作机 ←──SSH──→ node2 EIP │
│ ←──SSH──→ node3 EIP │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ 内网 (Underlay) │
│ 用于 OVN 组件间通信和隧道封装 │
│ │
│ node2 ──Geneve/VXLAN──→ node3 │
│ (172.20.2.3) (172.20.2.8) │
│ ↑ ↑ │
│ │ ovn-remote │ │
│ └─────── tcp:172.20.2.2:6642 ───────┘ │
│ node1 │
│ (172.20.2.2) │
└─────────────────────────────────────────────────────────┘
| 用途 | 使用的 IP | 说明 |
|---|---|---|
| SSH 远程执行 | EIP | 所有 ssh / scp 操作 |
| ovn-remote (SB 连接) | NODE1 内网 IP | Chassis 通过内网连接 Central SB |
| ovn-encap-ip (隧道源) | 本节点内网 IP | Geneve/VXLAN 隧道封装使用内网 IP |
| NB/SB 监听地址 | 0.0.0.0 | 监听所有网卡,由防火墙控制访问 |
2. 组件分布
2.1 Central 节点 (node1)
运行服务:
| 服务 | systemd 单元 | 监听端口 | 职责 |
|---|---|---|---|
| OVN NB DB | ovn-ovsdb-server-nb | TCP 6641 | 北向数据库,存储逻辑拓扑定义 |
| OVN SB DB | ovn-ovsdb-server-sb | TCP 6642 | 南向数据库,Chassis 注册与端口绑定 |
| ovn-northd | ovn-northd | — | 将 NB 逻辑翻译为 SB 流表,下发至 Chassis |
核心配置文件:
| 文件 | 用途 | 当前内容/状态 |
|---|---|---|
/etc/default/ovn-central | Central 三个服务共用此文件,通过 $OVN_CTL_OPTS 传参 | OVN_CTL_OPTS="--db-nb-create-insecure-yes --db-sb-create-insecure-yes" |
/usr/lib/systemd/system/ovn-ovsdb-server-nb.service | NB DB systemd 单元(包管理,不建议直接改) | EnvironmentFile=-/etc/default/ovn-central,ExecStart=/usr/share/ovn/scripts/ovn-ctl run_nb_ovsdb $OVN_CTL_OPTS |
/usr/lib/systemd/system/ovn-ovsdb-server-sb.service | SB DB systemd 单元(包管理,不建议直接改) | EnvironmentFile=-/etc/default/ovn-central,ExecStart=/usr/share/ovn/scripts/ovn-ctl run_sb_ovsdb $OVN_CTL_OPTS |
/usr/lib/systemd/system/ovn-northd.service | northd systemd 单元(包管理,不建议直接改) | EnvironmentFile=-/etc/default/ovn-central,ExecStart=/usr/share/ovn/scripts/ovn-ctl start_northd --ovn-manage-ovsdb=no $OVN_CTL_OPTS |
/usr/lib/systemd/system/ovn-central.service | 总控服务(oneshot),拉起上面三个 | Wants=ovn-ovsdb-server-nb.service ovn-ovsdb-server-sb.service ovn-northd.service |
/usr/share/ovn/scripts/ovn-ctl | 启停脚本,所有 Central 服务统一经它拉起 | 解析 $OVN_CTL_OPTS,控制 DB 路径/PID/socket 等 |
/var/lib/ovn/ovnnb_db.db | NB 数据库文件(持久化逻辑拓扑定义) | OVSDB 文件,ovn-nbctl 操作的对象 |
/var/lib/ovn/ovnsb_db.db | SB 数据库文件(持久化 Chassis/绑定/逻辑流表) | OVSDB 文件,ovn-sbctl 操作的对象 |
/var/run/ovn/ovnnb_db.sock | NB DB Unix socket(本地 ovn-nbctl 默认连此) | 运行时生成 |
/var/run/ovn/ovnsb_db.sock | SB DB Unix socket(本地 ovn-sbctl 默认连此) | 运行时生成 |
/var/run/ovn/ovnnb_db.pid | NB DB PID 文件 | 运行时生成 |
/var/run/ovn/ovnsb_db.pid | SB DB PID 文件 | 运行时生成 |
/var/run/ovn/ovn-northd.pid | northd PID 文件 | 运行时生成 |
/var/log/ovn/ovsdb-server-nb.log | NB DB 日志 | |
/var/log/ovn/ovsdb-server-sb.log | SB DB 日志 | |
/var/log/ovn/ovn-northd.log | northd 日志 |
运行时数据库连接配置(存储在 OVSDB connection 表中,非独立文件):
NB: target="ptcp:6641:0.0.0.0" inactivity_probe=0
SB: target="ptcp:6642:0.0.0.0" inactivity_probe=0
通过
ovn-nbctl set-connection/ovn-sbctl set-connection写入,重启后持久化在 DB 文件中。
TCP 明文模式下必须设置
--db-*-create-insecure-yes,否则 NB/SB 仅监听 Unix socket,Chassis 无法远程连接。
2.2 Chassis 节点 (node2, node3)
运行服务:
| 服务 | systemd 单元 | 职责 |
|---|---|---|
| openvswitch-switch | openvswitch-switch | OVS 数据面,维护 br-int 网桥和 OpenFlow 流表 |
| ovn-controller | ovn-controller | 从 SB 获取逻辑流表,翻译为 OpenFlow 规则下发给 OVS |
核心配置文件:
| 文件 | 用途 | 当前内容/状态 |
|---|---|---|
/etc/default/ovn-host | ovn-controller 启动参数 | 默认空(OVN_CTL_OPTS 注释掉),未自定义 |
/usr/lib/systemd/system/ovn-controller.service | ovn-controller systemd 单元(包管理,不建议直接改) | EnvironmentFile=-/etc/default/ovn-host,ExecStart=/usr/share/ovn/scripts/ovn-ctl start_controller --ovn-manage-ovsdb=no $OVN_CTL_OPTS |
/usr/lib/systemd/system/ovn-host.service | 总控服务(oneshot),拉起 ovn-controller | Wants=ovn-controller.service |
/etc/default/openvswitch-switch | OVS 启动参数(DPDK 等) | 默认全注释,未自定义 |
/usr/lib/systemd/system/openvswitch-switch.service | OVS 总控,拉起 ovsdb-server + ovs-vswitchd | Requires=ovsdb-server.service ovs-vswitchd.service |
/usr/lib/systemd/system/ovsdb-server.service | OVS 本地配置数据库服务 | ExecStart=/usr/share/openvswitch/scripts/ovs-ctl --no-mlockall start_db |
/usr/lib/systemd/system/ovs-vswitchd.service | OVS 数据面转发守护进程 | ExecStart=/usr/share/openvswitch/scripts/ovs-ctl --no-mlockall start |
/var/lib/openvswitch/conf.db | OVS 配置数据库文件(持久化网桥/端口/external_ids 等) | 包含 br-int 定义、隧道端口、veth 端口等 |
/var/run/openvswitch/db.sock | OVS DB Unix socket(ovn-controller 连此获取 OVN 配置) | 运行时生成 |
/var/run/ovn/ovn-controller.pid | ovn-controller PID 文件 | 运行时生成 |
/var/log/ovn/ovn-controller.log | ovn-controller 日志 | |
/var/log/openvswitch/ovsdb-server.log | OVS DB 日志 | |
/var/log/openvswitch/ovs-vswitchd.log | OVS 转发日志 |
运行时 OVN 连接配置(存储在 OVS conf.db external_ids 中,非独立文件):
ovn-remote = "tcp:172.20.2.2:6642" # SB 连接地址(内网 IP)
ovn-encap-type = "geneve,vxlan" # 隧道封装类型
ovn-encap-ip = "172.20.2.3" # 本节点隧道源 IP(内网 IP)
system-id = "2636c4cc-..." # Chassis UUID
通过
ovs-vsctl set Open_vSwitch . external_ids:xxx写入,持久化在conf.db中,重启不丢失。
OVS external_ids 配置命令(以 node2 为例):
ovs-vsctl set Open_vSwitch . \
external_ids:ovn-remote="tcp:172.20.2.2:6642" \
external_ids:ovn-encap-type="geneve,vxlan" \
external_ids:ovn-encap-ip="172.20.2.3"
2.3 组件交互关系
┌──────────────────────┐
│ 管理员 (操作机) │
│ 通过 EIP SSH 执行 │
└──────────┬───────────┘
│ ovn-nbctl (via SSH)
▼
┌─────────────────────────────────────────────────────────┐
│ node1 (Central) │
│ ┌──────────┐ ┌──────────┐ ┌───────────┐ │
│ │ NB DB │───▶│ovn-northd│───▶│ SB DB │ │
│ │ :6641 │ │ │ │ :6642 │ │
│ └──────────┘ └──────────┘ └─────┬─────┘ │
│ │ │
└────────────────────────────────────────┼────────────────┘
│ tcp:172.20.2.2:6642
┌──────────┴──────────┐
│ │
┌─────────▼──────┐ ┌─────────▼──────┐
│ node2 (Chassis) │ │ node3 (Chassis) │
│ ovn-controller │ │ ovn-controller │
│ │ │ │ │ │
│ ┌──▼──┐ │ │ ┌──▼──┐ │
│ │br-int│ │ │ │br-int│ │
│ └──┬──┘ │ │ └──┬──┘ │
└─────┼───────────┘ └─────┼───────────┘
│ Geneve/VXLAN │
└───────────────────────┘
172.20.2.3 ↔ 172.20.2.8
2.4 配置文件关系图
Central 节点 (node1):
/etc/default/ovn-central ←─ 唯一需要编辑的配置文件
│
▼ $OVN_CTL_OPTS
ovn-ctl run_nb_ovsdb / run_sb_ovsdb / start_northd
│
├── /var/lib/ovn/ovnnb_db.db ←─ NB 持久化数据(含 connection 表)
├── /var/lib/ovn/ovnsb_db.db ←─ SB 持久化数据(含 Chassis/Port_Binding)
└── /var/run/ovn/*.sock ←─ 本地管理 socket
Chassis 节点 (node2/node3):
/etc/default/ovn-host ←─ 默认空,一般不改
/etc/default/openvswitch-switch ←─ 默认空,一般不改
│
▼
ovs-ctl start_db / start
│
├── /var/lib/openvswitch/conf.db ←─ OVS 配置 DB(含 external_ids)
│ └── external_ids: ←─ OVN 连接三件套
│ ovn-remote = tcp:172.20.2.2:6642
│ ovn-encap-type = geneve,vxlan
│ ovn-encap-ip = 172.20.2.3
│
▼
ovn-controller unix:/var/run/openvswitch/db.sock
│
└── 连接 SB DB → 获取逻辑流表 → 下发 OpenFlow 到 br-int
3. 部署过程
3.1 部署 Central (node1)
# 1. 安装软件包
apt-get install -y ovn-central
# 2. 配置 TCP 明文模式
cat > /etc/default/ovn-central <<EOF
OVN_CTL_OPTS="--db-nb-create-insecure-yes --db-sb-create-insecure-yes"
EOF
# 3. 清除旧数据库,重启服务
rm -f /var/lib/ovn/ovn-nb.db /var/lib/ovn/ovn-sb.db
systemctl restart ovn-central
# 4. 设置 NB/SDB 监听所有网卡
ovn-nbctl set-connection ptcp:6641:0.0.0.0 -- set connection . inactivity_probe=0
ovn-sbctl set-connection ptcp:6642:0.0.0.0 -- set connection . inactivity_probe=0
部署验证结果:
[OK] ovn-ovsdb-server-nb is active
[OK] ovn-ovsdb-server-sb is active
[OK] ovn-northd is active
[OK] Port 6641 is listening
[OK] Port 6642 is listening
踩坑记录: Ubuntu 24.04 的 OVN 包中 NB/SB 数据库服务名不是
ovn-nb/ovn-sb,而是ovn-ovsdb-server-nb/ovn-ovsdb-server-sb,总控服务为ovn-central(exited 状态)。包名ovn-tool在 24.04 仓库中不存在,已从安装列表移除。
3.2 部署 Chassis (node2, node3)
# 1. 安装软件包
apt-get install -y openvswitch-switch ovn-host
# 2. 启动 OVS
systemctl restart openvswitch-switch
# 3. 配置 OVN 连接(内网 IP)
ovs-vsctl set Open_vSwitch . \
external_ids:ovn-remote="tcp:172.20.2.2:6642" \
external_ids:ovn-encap-type="geneve,vxlan" \
external_ids:ovn-encap-ip="<本节点内网IP>"
# 4. 重启 ovn-controller
systemctl restart ovn-controller
部署验证结果:
node2: [OK] ovn-controller is active, ovn-remote = tcp:172.20.2.2:6642
node3: [OK] ovn-controller is active, ovn-remote = tcp:172.20.2.2:6642
Chassis 注册确认(在 node1 上执行 ovn-sbctl show):
Chassis "2636c4cc-cb6e-4db3-a7fc-3335a01e273c"
hostname: i-gu2timrtgnqtozbt-xmmp02 # node2
Encap geneve
ip: "172.20.2.3"
Encap vxlan
ip: "172.20.2.3"
Port_Binding lsp-node1
Chassis "7f756ca5-c9ee-4cde-b5bc-f9d5c9f6869b"
hostname: i-g42taojwgq2tgmjy-xmmp02 # node3
Encap geneve
ip: "172.20.2.8"
Encap vxlan
ip: "172.20.2.8"
Port_Binding lsp-node2
4. 逻辑网络拓扑
4.1 拓扑结构
lr1 (逻辑路由器)
10.10.0.1/24 10.20.0.1/24
00:00:00:00:01:01 00:00:00:00:02:01
│ │
lsp-lr1-ls1 lsp-lr1-ls2
(type:router) (type:router)
│ │
┌────┴────┐ ┌────┴────┐
│ ls1 │ │ ls2 │
│10.10.0.0│ │10.20.0.0│
│ /24 │ │ /24 │
└────┬────┘ └────┬────┘
│ │
lsp-node1 lsp-node2
00:00:00:00:01:11 00:00:00:00:02:21
10.10.0.11 10.20.0.21
│ │
┌───────┴───────┐ ┌──────┴────────┐
│ node2 │ │ node3 │
│ (Chassis) │ │ (Chassis) │
│ veth-node1 │ │ veth-node2 │
└───────────────┘ └────────────────┘
4.2 创建命令
以下命令均在 Central 节点 (node1) 上执行,按顺序逐步构建完整拓扑。
1) 创建逻辑交换机
ovn-nbctl ls-add ls1
ovn-nbctl ls-add ls2
2) 创建逻辑路由器
ovn-nbctl lr-add lr1
3) 连接路由器与交换机(ls1 侧)
# 在 lr1 上添加面向 ls1 的路由器端口
ovn-nbctl lrp-add lr1 lrp-lr1-ls1 00:00:00:00:01:01 10.10.0.1/24
# 在 ls1 上添加面向 lr1 的交换机端口,并与路由器端口 peer
ovn-nbctl lsp-add ls1 lsp-lr1-ls1
ovn-nbctl lsp-set-type lsp-lr1-ls1 router
ovn-nbctl lsp-set-addresses lsp-lr1-ls1 router
ovn-nbctl lsp-set-options lsp-lr1-ls1 router-port=lrp-lr1-ls1
4) 连接路由器与交换机(ls2 侧)
# 在 lr1 上添加面向 ls2 的路由器端口
ovn-nbctl lrp-add lr1 lrp-lr1-ls2 00:00:00:00:02:01 10.20.0.1/24
# 在 ls2 上添加面向 lr1 的交换机端口,并与路由器端口 peer
ovn-nbctl lsp-add ls2 lsp-lr1-ls2
ovn-nbctl lsp-set-type lsp-lr1-ls2 router
ovn-nbctl lsp-set-addresses lsp-lr1-ls2 router
ovn-nbctl lsp-set-options lsp-lr1-ls2 router-port=lrp-lr1-ls2
要点:路由器端口(lrp)和交换机端口(lsp)必须通过
lsp-set-options router-port=<lrp名>互相配对,OVN 才会在二者之间建立 patch port 实现跨管道转发。
5) 添加 VM 逻辑端口
# node2 上的 VM 接入 ls1
ovn-nbctl lsp-add ls1 lsp-node1
ovn-nbctl lsp-set-addresses lsp-node1 "00:00:00:00:01:11 10.10.0.11"
# node3 上的 VM 接入 ls2
ovn-nbctl lsp-add ls2 lsp-node2
ovn-nbctl lsp-set-addresses lsp-node2 "00:00:00:00:02:21 10.20.0.21"
要点:
lsp-set-addresses同时绑定 MAC 和 IP,OVN 据此生成端口安全规则(只允许该 MAC/IP 的报文进出)和 L2 转发表项。
6) 配置 ACL 规则
# ls2 → ls1 方向
ovn-nbctl acl-add ls2 from-lport 1002 "ip4.src==10.20.0.0/24 && ip4.dst==10.10.0.0/24 && icmp" allow
ovn-nbctl acl-add ls2 from-lport 1002 "ip4.src==10.20.0.0/24 && ip4.dst==10.10.0.0/24 && tcp && tcp.dst==80" allow
ovn-nbctl acl-add ls2 from-lport 1002 "ip4.src==10.20.0.0/24 && ip4.dst==10.10.0.0/24 && tcp && tcp.dst==443" allow
ovn-nbctl acl-add ls2 from-lport 1001 "ip4.src==10.20.0.0/24 && ip4.dst==10.10.0.0/24 && udp && udp.dst==5000" drop
# ls1 → ls2 方向
ovn-nbctl acl-add ls1 from-lport 1002 "ip4.src==10.10.0.0/24 && ip4.dst==10.20.0.0/24 && icmp" allow
ovn-nbctl acl-add ls1 from-lport 1002 "ip4.src==10.10.0.0/24 && ip4.dst==10.20.0.0/24 && tcp && tcp.dst==80" allow
ovn-nbctl acl-add ls1 from-lport 1002 "ip4.src==10.10.0.0/24 && ip4.dst==10.20.0.0/24 && tcp && tcp.dst==443" allow
# 入向默认放行
ovn-nbctl acl-add ls1 from-lport 0 "inport==\"lsp-node1\"" allow
ovn-nbctl acl-add ls2 from-lport 0 "inport==\"lsp-node2\"" allow
要点:
acl-add参数依次为 交换机名、方向、优先级、匹配条件、动作。优先级数值越大越优先,所以 1002 的 allow 优先于 1001 的 drop,ICMP/TCP:80/TCP:443 被放行,而 UDP:5000 被丢弃。
7) 在 Chassis 节点上创建 veth pair 模拟 VM 接入
# 在 node2 (Chassis) 上执行
ip link add veth-node1 type veth peer name veth-node1-br
ip link set veth-node1 addr 00:00:00:00:01:11
ip link set veth-node1 up
ip addr add 10.10.0.11/24 dev veth-node1
ip route add 10.20.0.0/24 via 10.10.0.1 dev veth-node1
ovs-vsctl add-port br-int veth-node1-br \
-- set interface veth-node1-br external_ids:iface-id=lsp-node1
ip link set veth-node1-br up
# 在 node3 (Chassis) 上执行
ip link add veth-node2 type veth peer name veth-node2-br
ip link set veth-node2 addr 00:00:00:00:02:21
ip link set veth-node2 up
ip addr add 10.20.0.21/24 dev veth-node2
ip route add 10.10.0.0/24 via 10.20.0.1 dev veth-node2
ovs-vsctl add-port br-int veth-node2-br \
-- set interface veth-node2-br external_ids:iface-id=lsp-node2
ip link set veth-node2-br up
要点:
external_ids:iface-id=<LSP名>是 veth 端口与 OVN 逻辑端口关联的唯一纽带。ovn-controller 读取此字段后向 SB 注册 Port_Binding,逻辑流表才能正确下发到此端口。
4.3 逻辑端口清单
| 逻辑端口 | 所属 | 类型 | MAC | IP |
|---|---|---|---|---|
| lrp-lr1-ls1 | lr1 | 路由器端口 | 00:00:00:00:01:01 | 10.10.0.1/24 |
| lrp-lr1-ls2 | lr1 | 路由器端口 | 00:00:00:00:02:01 | 10.20.0.1/24 |
| lsp-lr1-ls1 | ls1 | router (peer: lrp-lr1-ls1) | — | — |
| lsp-lr1-ls2 | ls2 | router (peer: lrp-lr1-ls2) | — | — |
| lsp-node1 | ls1 | 普通 | 00:00:00:00:01:11 | 10.10.0.11 |
| lsp-node2 | ls2 | 普通 | 00:00:00:00:02:21 | 10.20.0.21 |
4.4 ACL 规则
| 方向 | 交换机 | 优先级 | 匹配条件 | 动作 | 说明 |
|---|---|---|---|---|---|
| from-lport | ls1 | 1002 | ip4.src==10.10.0.0/24 && ip4.dst==10.20.0.0/24 && icmp | allow | 允许 ICMP ls1->ls2 |
| from-lport | ls1 | 1002 | ip4.src==10.10.0.0/24 && ip4.dst==10.20.0.0/24 && tcp.dst==80 | allow | 允许 TCP:80 ls1->ls2 |
| from-lport | ls1 | 1002 | ip4.src==10.10.0.0/24 && ip4.dst==10.20.0.0/24 && tcp.dst==443 | allow | 允许 TCP:443 ls1->ls2 |
| from-lport | ls1 | 0 | inport=="lsp-node1" | allow | 允许 node1 入向 |
| from-lport | ls2 | 1002 | ip4.src==10.20.0.0/24 && ip4.dst==10.10.0.0/24 && icmp | allow | 允许 ICMP ls2->ls1 |
| from-lport | ls2 | 1002 | ip4.src==10.20.0.0/24 && ip4.dst==10.10.0.0/24 && tcp.dst==80 | allow | 允许 TCP:80 ls2->ls1 |
| from-lport | ls2 | 1002 | ip4.src==10.20.0.0/24 && ip4.dst==10.10.0.0/24 && tcp.dst==443 | allow | 允许 TCP:443 ls2->ls1 |
| from-lport | ls2 | 1001 | ip4.src==10.20.0.0/24 && ip4.dst==10.10.0.0/24 && udp.dst==5000 | drop | 拒绝 UDP:5000 ls2->ls1 |
| from-lport | ls2 | 0 | inport=="lsp-node2" | allow | 允许 node2 入向 |
5. 数据面链路详解
5.1 VM 模拟方式
在 Chassis 节点上通过 veth pair 模拟 VM 接入:
┌──────────────────────────────────────────┐
│ Chassis 节点 │
│ │
│ ┌─────────┐ ┌──────────────┐ │
│ │ Namespace│ │ OVS br-int │ │
│ │ (模拟VM) │ │ │ │
│ │ │ │ veth-XX-br ─┤──┐ │
│ │ veth-XX ├─────┤ │ │ │
│ │ │ │ ovn-XX-0 ───┤──┤ │
│ │ IP/MAC │ │ (Geneve) │ │ │
│ │ 路由 │ │ │ │ │
│ └─────────┘ └──────────────┘ │ │
│ │ │ │ │
│ │ iface-id=LSP名 │ │
│ │ │ │
└────────┼────────────────────────────┼────┘
│ │
VM 侧接口 OVS 侧接口
(配置IP/MAC/路由) (挂入br-int, 设置iface-id)
实际配置:
| 节点 | veth VM 侧 | veth OVS 侧 | iface-id | IP/路由 |
|---|---|---|---|---|
| node2 | veth-node1 | veth-node1-br | lsp-node1 | 10.10.0.11/24, 默认经 10.10.0.1 到 10.20.0.0/24 |
| node3 | veth-node2 | veth-node2-br | lsp-node2 | 10.20.0.21/24, 默认经 10.20.0.1 到 10.10.0.0/24 |
5.2 跨子网数据包完整路径 (10.10.0.11 → 10.20.0.21)
以 node2 上的 VM (10.10.0.11) ping node3 上的 VM (10.20.0.21) 为例:
步骤1: VM发出ICMP请求
源: 10.10.0.11 (MAC: 00:00:00:00:01:11)
目的: 10.20.0.21 (跨子网,需查路由表)
路由: 经 10.10.0.1 (lr1 端口 MAC: 00:00:00:00:01:01)
封装: Eth dst=00:00:00:00:01:01, IP dst=10.20.0.21
↓ veth-node1 → veth-node1-br
步骤2: OVS br-int (node2) — ls1 ingress 管道
ls_in_check_port_sec: 校验端口安全
ls_in_acl_eval: 匹配ACL规则 (icmp, priority 2002, reg8[16]=1 → allow)
ls_in_acl_action: 执行 allow 动作
ls_in_l2_lkup: 目的MAC=00:00:00:00:01:01 → outport="lsp-lr1-ls1"
↓ output → patch port
步骤3: lr1 ingress 管道
lr_in_admission: 校验 eth.dst==00:00:00:00:01:01, inport=="lrp-lr1-ls1"
lr_in_ip_input: IP路由查找: 10.20.0.0/24 经 lrp-lr1-ls2
TTL递减, 源MAC改为 00:00:00:00:02:01
↓ output → patch port
步骤4: ls2 ingress/egress 管道
ls_in_l2_lkup: 目的MAC=00:00:00:00:02:21 → outport="lsp-node2"
ls_out_apply_port_sec: 输出
步骤5: Geneve隧道封装
node2 (172.20.2.3) ──Geneve──→ node3 (172.20.2.8)
外层: UDP dst=6081, VNI=逻辑网络标识
内层: 原始以太帧
步骤6: OVS br-int (node3) 收到后解封装
根据 VNI 和目的 MAC 转发到 veth-node2-br → veth-node2
VM (10.20.0.21) 收到 ICMP Echo Request
5.3 隧道端口映射
node2 br-int:
Port ovn-7f756c-0 # Geneve隧道端口,remote_ip=172.20.2.8 (node3)
Port veth-node1-br # VM接入端口,iface-id=lsp-node1
node3 br-int:
Port ovn-2636c4-0 # Geneve隧道端口,remote_ip=172.20.2.3 (node2)
Port veth-node2-br # VM接入端口,iface-id=lsp-node2
6. 验证结果
6.1 服务状态
| 节点 | 服务 | 状态 |
|---|---|---|
| node1 | ovn-ovsdb-server-nb | active |
| node1 | ovn-ovsdb-server-sb | active |
| node1 | ovn-northd | active |
| node2 | openvswitch-switch | active |
| node2 | ovn-controller | active |
| node3 | openvswitch-switch | active |
| node3 | ovn-controller | active |
6.2 Chassis 注册
| Chassis | Encap 类型 | Encap IP | Port_Binding |
|---|---|---|---|
| node2 (2636c4cc...) | geneve, vxlan | 172.20.2.3 | lsp-node1 |
| node3 (7f756ca5...) | geneve, vxlan | 172.20.2.8 | lsp-node2 |
6.3 L3 连通性
node2 VM → node3 VM (10.10.0.11 → 10.20.0.21)
PING 10.20.0.21 (10.20.0.21) 56(84) bytes of data.
64 bytes from 10.20.0.21: icmp_seq=1 ttl=63 time=2.41 ms
64 bytes from 10.20.0.21: icmp_seq=2 ttl=63 time=0.439 ms
64 bytes from 10.20.0.21: icmp_seq=3 ttl=63 time=0.429 ms
64 bytes from 10.20.0.21: icmp_seq=4 ttl=63 time=0.389 ms
64 bytes from 10.20.0.21: icmp_seq=5 ttl=63 time=0.386 ms
5 packets transmitted, 5 received, 0% packet loss, time 4096ms
rtt min/avg/max/mdev = 0.386/0.811/2.412/0.800 ms
node3 VM → node2 VM (10.20.0.21 → 10.10.0.11)
PING 10.10.0.11 (10.10.0.11) 56(84) bytes of data.
64 bytes from 10.10.0.11: icmp_seq=1 ttl=63 time=0.975 ms
64 bytes from 10.10.0.11: icmp_seq=2 ttl=63 time=0.533 ms
64 bytes from 10.10.0.11: icmp_seq=3 ttl=63 time=0.500 ms
64 bytes from 10.10.0.11: icmp_seq=4 ttl=63 time=0.455 ms
64 bytes from 10.10.0.11: icmp_seq=5 ttl=63 time=0.426 ms
5 packets transmitted, 5 received, 0% packet loss, time 4064ms
rtt min/avg/max/mdev = 0.426/0.577/0.975/0.201 ms
注:node2 和 node3 分属电信/移动不同运营商,跨运营商 Geneve 隧道仍可正常工作,首包 RTT 约 1-2ms(ARP 学习开销),后续包稳定在 0.4-0.5ms。
6.4 ACL 规则验证 (ovn-trace)
OVN 使用 reg8 寄存器位标记 ACL 判定结果:
reg8[16] = 1→ allow(允许通过)reg8[17] = 1→ drop(直接丢弃)reg8[18] = 1→ reject(拒绝并回复)
| 流量类型 | 方向 | ACL 优先级 | reg8 判定 | 实际结果 |
|---|---|---|---|---|
| ICMP | ls2→ls1 | 2002 | reg8[16]=1 (allow) | 允许通过 |
| TCP:80 | ls2→ls1 | 2002 | reg8[16]=1 (allow) | 允许通过 |
| TCP:443 | ls2→ls1 | 2002 | reg8[16]=1 (allow) | 允许通过 |
| UDP:5000 | ls2→ls1 | 2001 | reg8[17]=1 (drop) | 被丢弃 |
ICMP allow 的完整 trace 片段:
8. ls_in_acl_eval: (ip4.src==10.20.0.0/24 && ip4.dst==10.10.0.0/24 && icmp), priority 2002
reg8[16] = 1; ← 标记为 allow
next;
9. ls_in_acl_action: reg8[16] == 1, priority 1000
reg8[16] = 0; ← 清除标记,继续处理
reg8[17] = 0;
reg8[18] = 0;
next; ← 放行,进入后续管道
UDP:5000 drop 的完整 trace 片段:
8. ls_in_acl_eval: (ip4.src==10.20.0.0/24 && ip4.dst==10.10.0.0/24 && udp && udp.dst==5000), priority 2001
reg8[17] = 1; ← 标记为 drop
next;
9. ls_in_acl_action: reg8[17] == 1, priority 1000
reg8[16] = 0;
reg8[17] = 0;
reg8[18] = 0; ← 不执行 next,包在此被丢弃
6.5 OpenFlow 流表
| 节点 | 流表条目数 |
|---|---|
| node2 | 499 |
| node3 | 499 |
7. 部署踩坑记录
| 问题 | 原因 | 解决方案 |
|---|---|---|
ovn-tool 包安装失败 | Ubuntu 24.04 仓库中无此包 | 从安装列表移除,ovn-nbctl/ovn-sbctl 已包含在 ovn-common 中 |
ovn-nb.service not found | Ubuntu 24.04 中 NB/SB DB 服务名变更 | 使用 ovn-ovsdb-server-nb / ovn-ovsdb-server-sb 或通过 ovn-central 总控服务管理 |
ovn-sbctl show 中 Chassis 名为 UUID | 系统主机名非 node1/node2 格式 | 通过 encap IP 匹配确认 Chassis 归属 |
| nc 测试 UDP:5000 未被阻断 | nc UDP 模式发送不受 OVS 管道约束(内网直接可达) | 以 ovn-trace 作为 ACL 验证的权威手段 |
8. 逻辑网络拓扑查询命令
以下命令用于日常排查和确认逻辑网络状态,按查询对象分为三大类。
8.1 ovn-nbctl — 北向数据库查询(逻辑拓扑定义)
在 Central 节点 (node1) 上执行,查看管理员定义的逻辑资源。
# ===== 全局概览 =====
# 查看完整逻辑拓扑(交换机、路由器、端口、地址绑定)
ovn-nbctl show
# ===== 逻辑交换机 =====
# 列出所有逻辑交换机
ovn-nbctl ls-list
# 查看指定交换机的所有端口及 MAC/IP 绑定
ovn-nbctl lsp-list ls1
ovn-nbctl lsp-list ls2
# 查看指定端口的详细属性(类型、地址、选项)
ovn-nbctl lsp-get-type lsp-node1
ovn-nbctl lsp-get-addresses lsp-node1
ovn-nbctl lsp-get-options lsp-lr1-ls1
# ===== 逻辑路由器 =====
# 列出所有逻辑路由器
ovn-nbctl lr-list
# 查看路由器上的所有端口及 IP
ovn-nbctl lrp-list lr1
# 查看路由器的路由表(静态路由 + 直连路由)
ovn-nbctl lr-route-list lr1
# ===== ACL 规则 =====
# 查看指定交换机上的所有 ACL 规则
ovn-nbctl acl-list ls1
ovn-nbctl acl-list ls2
# ===== 地址映射 =====
# 查看 MAC-IP 绑定(端口安全 / DHCP 静态分配)
ovn-nbctl list Logical_Switch_Port
# ===== 连接配置 =====
# 查看 NB 数据库的远程连接配置
ovn-nbctl get-connection
# 查看数据库本身的运行状态
ovn-nbctl status
ovn-nbctl show 实际输出示例:
switch 3d8ec12a-124e-4264-ac5a-1427d37b2bb2 (ls2)
port lsp-lr1-ls2
type: router
router-port: lrp-lr1-ls2
port lsp-node2
addresses: ["00:00:00:00:02:21 10.20.0.21"]
switch 3c40ca53-8be8-474a-8b54-db0679575b4f (ls1)
port lsp-lr1-ls1
type: router
router-port: lrp-lr1-ls1
port lsp-node1
addresses: ["00:00:00:00:01:11 10.10.0.11"]
router d5b59209-d5ef-48ea-8069-465448abd2cd (lr1)
port lrp-lr1-ls2
mac: "00:00:00:00:02:01"
networks: ["10.20.0.1/24"]
port lrp-lr1-ls1
mac: "00:00:00:00:01:01"
networks: ["10.10.0.1/24"]
8.2 ovn-sbctl — 南向数据库查询(运行时状态)
在 Central 节点 (node1) 上执行,查看 Chassis 注册、端口绑定等运行时信息。
# ===== Chassis 注册 =====
# 查看所有已注册 Chassis 及其隧道封装 IP
ovn-sbctl show
# 列出所有 Chassis 及其封装配置
ovn-sbctl list Chassis
# 查看指定 Chassis 的封装类型和 IP
ovn-sbctl list Encap
# ===== 端口绑定 =====
# 查看所有逻辑端口的绑定状态(绑定到哪个 Chassis)
ovn-sbctl list Port_Binding
# 查看指定端口绑定详情
ovn-sbctl get Port_Binding lsp-node1 chassis
# ===== 逻辑流表 =====
# 查看指定逻辑交换机的 ingress/egress 逻辑流表
ovn-sbctl lflow-list ls1
ovn-sbctl lflow-list ls2
# 查看指定逻辑路由器的逻辑流表
ovn-sbctl lflow-list lr1
# ===== 数据包追踪 =====
# 追踪从 lsp-node2 发出的 ICMP 包(ls2 → ls1,预期 allow)
ovn-trace ls2 'inport=="lsp-node2" && eth.src==00:00:00:00:02:21 && eth.dst==00:00:00:00:02:01 && ip4.src==10.20.0.21 && ip4.dst==10.10.0.11 && icmp'
# 追踪从 lsp-node2 发出的 UDP:5000 包(预期 drop)
ovn-trace ls2 'inport=="lsp-node2" && eth.src==00:00:00:00:02:21 && eth.dst==00:00:00:00:02:01 && ip4.src==10.20.0.21 && ip4.dst==10.10.0.11 && udp.dst==5000'
# 追踪从 lsp-node2 发出的 TCP:80 SYN 包(预期 allow)
ovn-trace ls2 'inport=="lsp-node2" && eth.src==00:00:00:00:02:21 && eth.dst==00:00:00:00:02:01 && ip4.src==10.20.0.21 && ip4.dst==10.10.0.11 && tcp.dst==80 && tcp.flags==0x02'
# ===== 连接配置 =====
# 查看 SB 数据库的远程连接配置
ovn-sbctl get-connection
ovn-sbctl show 实际输出示例:
Chassis "2636c4cc-cb6e-4db3-a7fc-3335a01e273c"
hostname: i-gu2timrtgnqtozbt-xmmp02 # node2
Encap geneve
ip: "172.20.2.3"
Encap vxlan
ip: "172.20.2.3"
Port_Binding lsp-node1
Chassis "7f756ca5-c9ee-4cde-b5bc-f9d5c9f6869b"
hostname: i-g42taojwgq2tgmjy-xmmp02 # node3
Encap geneve
ip: "172.20.2.8"
Encap vxlan
ip: "172.20.2.8"
Port_Binding lsp-node2
ovn-sbctl list Port_Binding 关键字段说明:
| 字段 | 含义 | 示例 |
|---|---|---|
| logical_port | 逻辑端口名 | lsp-node1 |
| chassis | 绑定的 Chassis UUID | 2636c4cc-... |
| mac | 绑定的 MAC 地址 | ["00:00:00:00:01:11"] |
| type | 端口类型(空=普通, router=路由端口) | "" / "router" |
8.3 ovs-vsctl / ovs-ofctl — OVS 数据面查询
在 Chassis 节点 (node2/node3) 上执行,查看本地 OVS 网桥和 OpenFlow 流表。
# ===== OVS 网桥与端口 =====
# 查看本节点所有网桥及端口配置
ovs-vsctl show
# 查看指定端口的 OVN 关联信息(iface-id 是端口绑定的核心)
ovs-vsctl get Interface veth-node1-br external_ids:iface-id
# 输出: "lsp-node1"
# 查看指定端口的链路状态
ovs-vsctl get Interface veth-node1-br link_state
# 输出: up
# 查看 OVN 相关的 external_ids(SB 连接地址、隧道类型、隧道源 IP)
ovs-vsctl get Open_vSwitch . external_ids:ovn-remote
# 输出: "tcp:172.20.2.2:6642"
ovs-vsctl get Open_vSwitch . external_ids:ovn-encap-type
# 输出: "geneve,vxlan"
ovs-vsctl get Open_vSwitch . external_ids:ovn-encap-ip
# 输出: "172.20.2.3"
# ===== OpenFlow 流表 =====
# 查看 br-int 上所有 OpenFlow 流规则
ovs-ofctl dump-flows br-int
# 按表号分类统计流表条目
ovs-ofctl dump-flows br-int | awk -F'table=' '{print $2}' | awk -F' ' '{print $1}' | sort | uniq -c | sort -rn
# 查看指定表号的流规则(如 table=8 即 ls_in_acl_eval)
ovs-ofctl dump-flows br-int table=8
# 查看指定端口相关的流规则
ovs-ofctl dump-flows br-int | grep "in_port=veth-node1-br"
# ===== 隧道端口 =====
# 查看 Geneve 隧道端口配置(remote_ip 指向对端 Chassis 内网 IP)
ovs-vsctl get Interface ovn-7f756c-0 options
# 输出: {csum="true", key=flow, remote_ip="172.20.2.8"}
# ===== 连通性诊断 =====
# 从 VM 侧发 ping(在 veth 接口上)
ping -I veth-node1 10.20.0.21
# 查看 ARP 表(确认是否学到对端 MAC)
ip neigh show dev veth-node1
# 查看 veth 接口的路由表
ip route show dev veth-node1
# 输出: 10.10.0.0/24 proto kernel scope link src 10.10.0.11
# 10.20.0.0/24 via 10.10.0.1
# 抓包:在 OVS 侧查看进入 br-int 的原始报文
tcpdump -i veth-node1-br -nn -e icmp
ovs-vsctl show 实际输出示例(node2):
a6010e53-fba5-412b-be0b-15ffd4fe16c5
Bridge br-int
fail_mode: secure
datapath_type: system
Port ovn-7f756c-0
Interface ovn-7f756c-0
type: geneve
options: {csum="true", key=flow, remote_ip="172.20.2.8"}
Port br-int
Interface br-int
type: internal
Port veth-node1-br
Interface veth-node1-br
ovs_version: "3.3.4"
8.4 命令速查对照表
按排查场景快速索引:
| 排查场景 | 命令 | 执行位置 |
|---|---|---|
| 逻辑拓扑长什么样 | ovn-nbctl show | node1 |
| 某交换机有哪些端口 | ovn-nbctl lsp-list ls1 | node1 |
| 某端口绑定了什么 MAC/IP | ovn-nbctl lsp-get-addresses lsp-node1 | node1 |
| ACL 规则是否配了 | ovn-nbctl acl-list ls2 | node1 |
| Chassis 有没有注册上来 | ovn-sbctl show | node1 |
| 端口有没有绑定到 Chassis | ovn-sbctl list Port_Binding | node1 |
| 包走到哪一步被丢/被放 | ovn-trace ls2 '...' | node1 |
| OVS 上 VM 端口是否挂进去了 | ovs-vsctl show | node2/node3 |
| iface-id 有没有设对 | ovs-vsctl get Interface veth-node1-br external_ids:iface-id | node2/node3 |
| SB 连接地址是否正确 | ovs-vsctl get Open_vSwitch . external_ids:ovn-remote | node2/node3 |
| 隧道有没有建起来 | ovs-vsctl get Interface ovn-7f756c-0 options | node2/node3 |
| OpenFlow 流表有多少条 | ovs-ofctl dump-flows br-int | wc -l | node2/node3 |
| ACL 在 OpenFlow 层怎么生效 | ovs-ofctl dump-flows br-int table=8 | node2/node3 |
| VM 侧是否能 ping 通对端 | ping -I veth-node1 10.20.0.21 | node2/node3 |
9. 清理方式
# 完整清理(拓扑 + 服务 + veth)
export NODE0_IP=172.20.2.2 NODE0_EIP=27.152.182.118
export NODE1_IP=172.20.2.3 NODE1_EIP=27.152.182.117
export NODE2_IP=172.20.2.8 NODE2_EIP=112.51.122.81
cd ovn-deploy
./cleanup.sh all
# 或按范围清理
./cleanup.sh topology # 仅删除逻辑拓扑和 veth
./cleanup.sh services # 仅停止并卸载 OVN/OVS 服务