在上一篇文章里,我们把范围控制在 OVN 的纯内部逻辑网络:逻辑交换机、逻辑路由器、ACL 和 Port Security。这一篇继续往外走一步,讨论一个更接近真实云网络的问题:
- 租户网络如何访问外部网络?
- 外部网络如何访问内部实例?
- OVN 里的
gateway router、localnet、SNAT、DNAT 到底是什么关系?
先把核心关系列清楚:
localnet 用来把 OVN 的逻辑交换机接到宿主机可达的物理二层网络;
gateway router 是 OVN 里承接南北向流量的逻辑路由器角色;
SNAT 解决“内部出去”;
DNAT 解决“外部进来”;
- 更常见的“给内部实例分配一个对外 IP”通常会用
dnat_and_snat。
1. 核心概念
localnet
localnet 是一种逻辑交换机端口类型,用来把逻辑交换机接到本机 OVS 上的 provider bridge。它不是普通 VM 端口,也不走 Geneve 隧道,而是直接把某个逻辑二层网络映射到宿主机可达的物理二层网络。
典型配置长这样:
1
2
3
4
|
ovn-nbctl lsp-add ls-public lsp-public-localnet
ovn-nbctl lsp-set-type lsp-public-localnet localnet
ovn-nbctl lsp-set-addresses lsp-public-localnet unknown
ovn-nbctl lsp-set-options lsp-public-localnet network_name=provider
|
与此同时,本机 OVS 需要配置:
1
|
ovs-vsctl set open . external-ids:ovn-bridge-mappings=provider:br-provider
|
这里的意思是:OVN 里名为 provider 的 localnet,映射到本机的 br-provider。
注意:
- 这条命令会直接写入
external-ids:ovn-bridge-mappings
- 如果你的环境里已经有别的 bridge mapping,直接执行可能会把原值覆盖掉
- 在共享环境或已有配置的机器上,应该先查看现有值,再决定是追加还是替换
gateway router
gateway router 底层仍然是 Logical_Router,但它不只是一个宽泛的“逻辑角色”描述。在 OVN 架构里,更准确的说法是:当 Logical_Router.options:chassis 被设置为某个 chassis 的名字或 UUID 时,这台逻辑路由器会以中心化方式运行在该 chassis 上,承担南北向流量和 NAT 出入口职责。
可以把它和未设置 options:chassis 的分布式路由器对比理解:
- 设置了
options:chassis:更接近中心化 gateway router,相关流量在指定 chassis 上集中处理
- 未设置
options:chassis:更接近分布式路由语义,转发和部分状态处理会分散到各个相关 chassis
下面这套实验是单机环境,只有一个 chassis,因此即使不显式设置 options:chassis,有些场景也可能跑通。但在有多个 chassis 的环境里,最好显式设置。
后面的实验步骤会直接把 lr-edge 绑定到当前 chassis。
在最小拓扑里,它通常有两侧:
- 一侧连租户内部网络,比如
10.0.1.0/24
- 一侧连 provider/public 网络,比如
192.168.2.0/24
SNAT、DNAT、dnat_and_snat
OVN 支持 3 种常用 NAT 类型:
| 类型 |
作用 |
snat |
内部地址访问外部时,把源地址改成外部地址 |
dnat |
外部访问某个地址时,把目标地址改成内部地址 |
dnat_and_snat |
同时做入站 DNAT 和出站 SNAT,常用来实现 floating IP |
官方命令是:
1
2
3
|
ovn-nbctl lr-nat-add <router> snat <external_ip> <logical_ip_or_cidr>
ovn-nbctl lr-nat-add <router> dnat <external_ip> <logical_ip>
ovn-nbctl lr-nat-add <router> dnat_and_snat <external_ip> <logical_ip> [<logical_port> <external_mac>]
|
其中:
- 单机实验或中心化部署里,常见写法就是前 4 个参数
- 在多节点分布式场景下,
dnat_and_snat 可以额外带上 logical_port 和 external_mac
- 当显式指定这两个参数时,floating IP 的 ARP 应答和相关流量可以更靠近实例所在 chassis 处理
2. 实验目标
这一篇不把实验拆成多个主题,而是围绕一套完整拓扑验证 3 件事:
- 内部实例通过
SNAT 访问外部网络
- 外部客户端通过
dnat_and_snat 访问内部实例
- 理解
localnet + provider bridge + gateway router 的协作关系
实验边界:
- 这套实验是单机环境,用 namespace 模拟内部实例和外部网络;
- 实验关注的是概念闭环,不是高可用、BFD、双网关等生产级细节;
- 本文不再混入 ACL、LB、Port Security 等其他主题。
3. 实验拓扑
flowchart LR
subgraph PRIVATE["Logical Switch ls-private (10.0.1.0/24)"]
VM1["vm1 / lsp-vm1\n10.0.1.10"]
LSP1["ls-private-to-lr\n(type=router)"]
end
subgraph ROUTER["Logical Router lr-edge"]
LRP1["lrp-private\n10.0.1.1/24"]
LRP2["lrp-public\n192.168.2.150/24"]
end
subgraph PUBLIC["Logical Switch ls-public (192.168.2.0/24)"]
LSP2["ls-public-to-lr\n(type=router)"]
LOCALNET["lsp-public-localnet\nlocalnet=provider"]
end
subgraph HOST["Host OVS"]
BR["br-provider"]
end
subgraph EXT["Namespace ext"]
EXTNS["ext\n192.168.2.151"]
end
VM1 --- LSP1
LSP1 --- LRP1
LRP1 --- LRP2
LRP2 --- LSP2
LOCALNET --- BR
BR --- EXTNS
style PRIVATE fill:#4a90d9,color:#fff
style ROUTER fill:#27ae60,color:#fff
style PUBLIC fill:#e67e22,color:#fff
style BR fill:#8e44ad,color:#fff
地址规划:
- 私有网络:
10.0.1.0/24
- 公网/外部网络:
192.168.2.0/24
vm1:10.0.1.10
ext:192.168.2.151
lr-edge 内网侧网关:10.0.1.1
lr-edge 外网侧地址:192.168.2.150
- 对外暴露的 floating IP:
192.168.2.152
说明:
- 这里使用
192.168.2.150-152 作为示例地址
- 在别的环境里复现实验时,应替换成所在二层网络里未被占用的地址
4. 创建实验环境
创建 provider bridge 和外部 namespace
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# provider bridge,用来承接 localnet 映射
ovs-vsctl --may-exist add-br br-provider
# 告诉 OVN:network_name=provider 对应 br-provider
# 注意:如果当前已有 ovn-bridge-mappings,这条命令会覆盖原值
ovs-vsctl set open . external-ids:ovn-bridge-mappings=provider:br-provider
# 创建外部 namespace
ip netns add ext
ip link add veth-ext type veth peer name br-ext
ip link set veth-ext netns ext
ip netns exec ext ip addr replace 192.168.2.151/24 dev veth-ext
ip netns exec ext ip link set veth-ext up
ip netns exec ext ip link set lo up
ip link set br-ext up
ovs-vsctl --may-exist add-port br-provider br-ext
|
创建逻辑网络、路由器和 localnet
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
|
ovn-nbctl ls-add ls-private
ovn-nbctl ls-add ls-public
ovn-nbctl lr-add lr-edge
# 将 lr-edge 显式设为当前 chassis 上的 gateway router
CHASSIS=$(ovn-sbctl --bare --columns=name find Chassis hostname=$(hostname -f))
ovn-nbctl set Logical_Router lr-edge options:chassis="$CHASSIS"
# 私有网络中的 VM 端口
ovn-nbctl lsp-add ls-private lsp-vm1
ovn-nbctl lsp-set-addresses lsp-vm1 "02:ac:10:ff:01:10 10.0.1.10"
# 逻辑路由器端口
ovn-nbctl lrp-add lr-edge lrp-private 02:ac:10:ff:01:01 10.0.1.1/24
ovn-nbctl lrp-add lr-edge lrp-public 02:ac:10:ff:02:01 192.168.2.150/24
# ls-private -> lr-edge
ovn-nbctl lsp-add ls-private ls-private-to-lr
ovn-nbctl lsp-set-type ls-private-to-lr router
ovn-nbctl lsp-set-addresses ls-private-to-lr router
ovn-nbctl lsp-set-options ls-private-to-lr router-port=lrp-private
# ls-public -> lr-edge
ovn-nbctl lsp-add ls-public ls-public-to-lr
ovn-nbctl lsp-set-type ls-public-to-lr router
ovn-nbctl lsp-set-addresses ls-public-to-lr router
ovn-nbctl lsp-set-options ls-public-to-lr router-port=lrp-public
ovn-nbctl set Logical_Switch_Port ls-public-to-lr options:nat-addresses=router
# ls-public -> provider bridge
ovn-nbctl lsp-add ls-public lsp-public-localnet
ovn-nbctl lsp-set-type lsp-public-localnet localnet
ovn-nbctl lsp-set-addresses lsp-public-localnet unknown
ovn-nbctl lsp-set-options lsp-public-localnet network_name=provider
|
创建内部实例 namespace
1
2
3
4
5
6
7
8
9
10
11
12
13
|
ip netns add vm1
ip link add veth-vm1 type veth peer name ovs-vm1
ip link set veth-vm1 netns vm1
ip netns exec vm1 ip link set veth-vm1 address 02:ac:10:ff:01:10
ip netns exec vm1 ip addr replace 10.0.1.10/24 dev veth-vm1
ip netns exec vm1 ip link set veth-vm1 up
ip netns exec vm1 ip link set lo up
ip netns exec vm1 ip route replace default via 10.0.1.1 dev veth-vm1
ip link set ovs-vm1 up
ovs-vsctl --may-exist add-port br-int ovs-vm1
ovs-vsctl set interface ovs-vm1 external_ids:iface-id=lsp-vm1
|
基础连通性检查
1
2
3
4
5
6
7
8
9
10
11
|
ovn-sbctl show
ovs-vsctl show
# vm1 应该能 ping 到自己的逻辑网关
ip netns exec vm1 ping -c2 10.0.1.1
# ext 应该能 ping 到公网侧逻辑网关
ip netns exec ext ping -c2 192.168.2.150
# 检查公网侧 peer LSP 是否已启用 NAT 地址同步
ovn-nbctl get Logical_Switch_Port ls-public-to-lr options
|
5. 实验一:SNAT
目标:让 vm1 访问 ext 时,源地址从 10.0.1.10 变成 192.168.2.150。
配置 SNAT
1
2
|
ovn-nbctl lr-nat-add lr-edge snat 192.168.2.150 10.0.1.0/24
ovn-nbctl lr-nat-list lr-edge
|
验证
在 ext 里启动一个简单 HTTP 服务:
1
2
|
ip netns exec ext sh -c \
'python3 -m http.server 8080 --bind 192.168.2.151 >/tmp/ovn-ext-http.log 2>&1 &'
|
然后从 vm1 访问:
1
|
ip netns exec vm1 curl -s 192.168.2.151:8080 >/dev/null
|
如果需要观察源地址,可以在 ext 里抓包:
1
|
ip netns exec ext tcpdump -ni veth-ext tcp port 8080
|
预期现象:
vm1 能访问 192.168.2.151:8080
ext 看到的源地址应是 192.168.2.150,而不是 10.0.1.10
6. 实验二:DNAT / Floating IP
纯 dnat 适合说明“外部访问某个地址时,目标地址被改到内部实例”;但在 VM/云网络实践里,更常见的是 dnat_and_snat,也就是 floating IP。
这里用 dnat_and_snat 做入站验证。
补充说明:
- 这里采用单机环境里更直观的最短命令
- 在多节点分布式场景下,如果希望 floating IP 的处理更靠近实例所在 chassis,可以显式补上
logical_port 和 external_mac
- 这一节默认你已经完成前面创建阶段里的
options:chassis 和 options:nat-addresses=router 配置,否则外部地址可能无法被正确通告和访问
在 vm1 上启动服务
1
2
|
ip netns exec vm1 sh -c \
'python3 -m http.server 8080 --bind 10.0.1.10 >/tmp/ovn-vm1-http.log 2>&1 &'
|
配置 floating IP
1
2
|
ovn-nbctl lr-nat-add lr-edge dnat_and_snat 192.168.2.152 10.0.1.10
ovn-nbctl lr-nat-list lr-edge
|
多节点分布式场景下,常见写法会进一步写成:
1
|
ovn-nbctl lr-nat-add lr-edge dnat_and_snat 192.168.2.152 10.0.1.10 lsp-vm1 02:ac:10:ff:01:10
|
这不是这里这套单机实验的必需项,但在多节点环境里需要知道这层差异。
验证
从 ext 访问这个 floating IP:
1
2
3
|
ovn-nbctl show lr-edge
ovn-nbctl get Logical_Switch_Port ls-public-to-lr options
ip netns exec ext curl -s 192.168.2.152:8080
|
预期现象:
- 能返回
vm1 上 HTTP 服务的内容
- 从外部看,访问的是
192.168.2.152
- 在 OVN 内部,流量被映射到
10.0.1.10
7. 常用排障命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# 查看整个逻辑拓扑
ovn-nbctl show
# 查看路由器 NAT 规则
ovn-nbctl lr-nat-list lr-edge
# 查看所有 Southbound 绑定
ovn-sbctl show
# 查看 br-int / br-provider 配置
ovs-vsctl show
# 查看 provider bridge 上的数据面流
ovs-appctl dpif/dump-flows br-provider
# 在 ext 中查看来自内部实例的 NAT 后流量
ip netns exec ext tcpdump -ni veth-ext
# 在 vm1 中查看默认路由
ip netns exec vm1 ip route
|
如果实验不通,优先按这个顺序检查:
ovs-vsctl get open . external-ids 中是否包含正确的 ovn-bridge-mappings
ovn-sbctl show 中 lsp-vm1 是否已绑定
ovn-nbctl show lr-edge 中是否已设置 options:chassis
ovn-nbctl get Logical_Switch_Port ls-public-to-lr options 中是否包含 nat-addresses=router
br-provider 是否存在,且 br-ext 是否已加入
vm1 和 ext 的默认路由/IP 是否正确
lr-nat-list lr-edge 是否已包含对应 NAT 规则
8. 清理环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
# 删除 NAT 规则
# 注意:只给 router 名称时,会删除该逻辑路由器上的全部 NAT 规则
ovn-nbctl lr-nat-del lr-edge
# 删除 OVS 端口
ovs-vsctl --if-exists del-port br-int ovs-vm1
ovs-vsctl --if-exists del-port br-provider br-ext
# 删除 namespace
ip netns del vm1 2>/dev/null || true
ip netns del ext 2>/dev/null || true
# 删除逻辑网络
ovn-nbctl --if-exists lr-del lr-edge
ovn-nbctl --if-exists ls-del ls-private
ovn-nbctl --if-exists ls-del ls-public
# 删除 provider bridge
ovs-vsctl --if-exists del-br br-provider
# 清理临时文件
rm -f /tmp/ovn-ext-http.log /tmp/ovn-vm1-http.log
|
9. 总结
localnet 负责把逻辑交换机接到真实可达的二层网络
gateway router 负责承接南北向流量
SNAT 解决“内部出去”
DNAT 解决“外部进来”
dnat_and_snat 更像日常说的 floating IP
如果只做纯内部逻辑网络实验,上一篇文章里的交换机、路由器、ACL、Port Security 就够了。
继续往“外部出口、浮动 IP、公网接入”推进时,OVN 的 gateway 和 NAT 就是下一步要掌握的内容。
10. 参考