背景
在使用 Kube-OVN 部署集中式 VPC NAT 网关时,业务侧反馈无法正常通过该网关访问公网服务。
网关pod本身问题排查
进入对应网关的pod,查看以下信息
- 项目使用了multus cni来把提供公网服务的网卡添加到网关pod,先判断该网卡是否可以正常ping通网络
- 查看对应的iptable规则,确认子网 IP 到指定公网 IP 的 NAT/SNAT 映射已正确下发
- 排查网关pod是否可通对应的子网ip(所影响的业务pod的ip为10.0.1.11)
kubectl exec -it -n kube-system vpc-nat-gw-v-gw-xxxx-0 -- bash
# 判断公网是否正常
ping 119.29.29.29 -c 3
PING 119.29.29.29 (119.29.29.29) 56(84) bytes of data.
64 bytes from 119.29.29.29: icmp_seq=1 ttl=51 time=6.52 ms
64 bytes from 119.29.29.29: icmp_seq=2 ttl=51 time=6.40 ms
64 bytes from 119.29.29.29: icmp_seq=3 ttl=51 time=13.0 ms
--- 119.29.29.29 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 6.398/8.622/12.950/3.060 ms
# 查看iptable规则
iptables-legacy-save
# Generated by iptables-save v1.8.11 on xxx 2026
...
-A EXCLUSIVE_DNAT -d xx.xx.xx.140/32 -j DNAT --to-destination 10.0.1.11
-A EXCLUSIVE_SNAT -s 10.0.1.0/24 -d 10.0.1.11/32 -m conntrack --ctstate DNAT -j SNAT --to-source 10.0.1.254
-A EXCLUSIVE_SNAT -s 10.0.1.11/32 -j SNAT --to-source xx.xx.xx.140
....
# ping 对应nat规则的ip
ping 10.0.1.11 -c 3
PING 10.0.1.11 (10.0.1.11) 56(84) bytes of data.
From 10.0.1.254 icmp_seq=1 Destination Host Unreachable
From 10.0.1.254 icmp_seq=2 Destination Host Unreachable
From 10.0.1.254 icmp_seq=3 Destination Host Unreachable
--- 10.0.1.11 ping statistics ---
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2044ms
上述排查发现问题并非出在公网侧或 NAT 规则本身,而是网关 Pod 与 OVN 内部网络之间的通信中断
OVN层排查
借助 Kube-OVN 提供的 ko 插件追踪报文在 OVN 逻辑数据面的走向
在使用该命令前,确保对应的pod的安全组是开放icmp协议
# 我们后面的子网IP为运行于 Underlay 网络下的虚拟机,需要额外添加mac地址
kubectl ko trace kube-system/vpc-nat-gw-v-gw-xxx-0 10.0.1.11 56:7f:db:12:9d:3f icmp
+ kubectl exec ovn-central-xxx-xxx -n kube-system -c ovn-central -- ovn-trace subnet-xxx 'inport == "vpc-nat-gw-v-gw-xxx-0.kube-system" && ip.ttl == 64 && icmp && eth.src == ce:6e:be:fd:44:61 && ip4.src == 10.0.1.254 && eth.dst == 56:7f:db:12:9d:3f && ip4.dst == 10.0.1.11 && ct.new'
...
ingress(dp="subnet-xxx", inport="vpc-nat-gw-v-gw-xxx-0.kube-system")
----------------------------------------------------------------------------------------------
...
ct_next(ct_state=est|trk /* default (use --ct to customize) */)
---------------------------------------------------------------
...
egress(dp="subnet-xxxxx", inport="vpc-nat-gw-v-gw-xxxx-0.kube-system", outport="vi-mpzqrret1he80ahpmeyc.xxx")
----------------------------------------------------------------------------------------------------------------------------------------
...
ct_next(ct_state=est|trk /* default (use --ct to customize) */)
---------------------------------------------------------------
....
/* output to "vi-mpzqrret1he80ahpmeyc.xxx", type "" */
--------
#上面的日志中显示了 output to "vi-mpzqrret1he80ahpmeyc.xxx" 说明ovn层流量是没有问题的
Start OVS Tracing
+ kubectl exec ovs-ovn-xxx -c openvswitch -n kube-system -- ovs-appctl ofproto/trace br-int in_port=,icmp,nw_ttl=64,nw_src=10.0.1.254,nw_dst=10.0.1.11,dl_src=ce:6e:be:fd:44:61,dl_dst=56:7f:db:12:9d:3f
Bad openflow flow syntax: in_port=,icmp,nw_ttl=64,nw_src=10.0.1.254,nw_dst=10.0.1.11,dl_src=ce:6e:be:fd:44:61,dl_dst=56:7f:db:12:9d:3f: bad value for in_port (: invalid or unknown port for in_port)
ovs-appctl: ovs-vswitchd: server returned an error
command terminated with exit code 2
在执行ovs层流量模拟时,发现参数错误导致的无法继续执行,缺少了in_port参数
正常来说 kubectl ko trace 命令执行应该都是没有问题,哪怕流量被安全组之前类拦截了,所以推断问题可能在ovs上面
手动补齐参数执行 ovs-appctl ofproto/trace
获取in_port参数
# 到对应网关pod节点上面的ovs中执行ovs-vsctl命令获取相关接口信息
kubectl exec ovs-ovn-xx -n kube-system -- ovs-vsctl --columns=name,ofport,external_ids find Interface external_ids:pod_name=vpc-nat-gw-b-gw-xxx-0
Defaulted container "openvswitch" out of: openvswitch, hostpath-init (init)
name : xxxx_h
ofport : 144
external_ids : {ip="10.0.1.254", ovn-installed="true", ovn-installed-ts="1774409185160", pod_name=vpc-nat-gw-b-gw-xxx-0, pod_namespace=kube-system, pod_netns="/var/run/netns/cni-xxxxxx-xxx-xxx-xxx-xxx", vendor=kube-ovn}
获得到的ofport为144,手动补齐并执行
kubectl exec ovs-ovn-xxx -c openvswitch -n kube-system -- \
ovs-appctl ofproto/trace br-int \
'in_port=144,icmp,nw_ttl=64,nw_src=10.0.1.254,nw_dst=10.0.1.11,dl_src=ce:6e:be:fd:44:61,dl_dst=56:7f:db:12:9d:3f'
....
bridge("br-int")
----------------
0. priority 0
drop
Final flow: unchanged
Megaflow: recirc_id=0,eth,ip,in_port=144,nw_frag=no
Datapath actions: drop
发现ovs中没有对应的规则,数据包被丢弃,从而导致网关pod无法ping通子网的IP,排查焦点转向 OVS 端口与 OVN 的绑定状态。
定位根本问题
获取对应ovs接口信息
# 在对应pod网关的节点上面执行相关命令
kubectl exec ovs-ovn-xxx -n kube-system -- \
ovs-vsctl get Interface fbe82fdce1a1_h external_id
...
external_ids : {ip="10.0.1.254", ovn-installed="true", ovn-installed-ts="1774409185160", pod_name=vpc-nat-gw-b-gw-xxx-0, pod_namespace=kube-system, pod_netns="/var/run/netns/cni-f0f3f7b3-b13d-0d26-4a53-fcb05e46b346", vendor=kube-ovn}
发现在返回的 external_ids里面缺失了 iface-id字段,这个字段作用是把对应的ovs的接口跟ovn的端口进行绑定,具体可以查看我另一篇文章 虚拟化初探–libvirt+OVN实践
查看对应的ovn中对应Port状态
# ovn-central 中可以执行 ovn-nbclt、ovn-sbctl 等ovn命令
kubectl exec -n kube-system ovn-central-xxx-xxx -c ovn-central -- ovn-sbctl find Port_Binding logical_port="vpc-nat-gw-v-gw-xxxx-0.kube-system"
....
logical_port : vpc-nat-gw-v-gw-xxx-0.kube-system
mac : ["ce:6e:be:fd:44:61 10.0.1.254"]
....
up : false
发现up状态为false,就确定时因为ovs中的接口缺少了 iface-id 导致无法正常工作
kube-ovn-cni 新建网关 Pod 时未正确设置 iface-id
↓
ovn-controller 无法识别该 OVS 端口
↓
Port Binding up = false
↓
OVS table 0 未下发流表规则
↓
所有从网关 Pod 发出的流量在 table 0 直接 drop
↓
网关 ping 不通子网 Pod → NAT 失效 → 公网无法访问
解决
由于当前环境基于 Kube-OVN 集成架构,所以没有必要通过ovs-vsctl命令手动补齐iface-id
直接重启对应的网关的资源即可
kubectl rollout restart statefulset -n kube-system vpc-nat-gw-v-gw-xxxx
重启后,CNI 会自动为 OVS 接口补全 iface-id,OVN Port_Binding 状态恢复为 up: true,底层流表正常下发,NAT 网关功能即刻恢复。
后续
为避免同类问题在生产环境潜伏,结合 Kube-OVN 官方提供的 Metrics 体系,对相关问题添加监控告警
根据 Kube-OVN 官方文档 提供的指标,添加了以下指标的监控
- 指标名称:pinger_inconsistent_port_binding(类型:Gauge)
- 指标含义:OVN-SB 中记录的 Port_Binding 数量与宿主机 OVS Interface 数量不一致的差值。