背景

在使用 Kube-OVN 部署集中式 VPC NAT 网关时,业务侧反馈无法正常通过该网关访问公网服务。

网关pod本身问题排查

进入对应网关的pod,查看以下信息

  1. 项目使用了multus cni来把提供公网服务的网卡添加到网关pod,先判断该网卡是否可以正常ping通网络
  2. 查看对应的iptable规则,确认子网 IP 到指定公网 IP 的 NAT/SNAT 映射已正确下发
  3. 排查网关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层排查

Kubectl 插件使用

借助 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 数量不一致的差值。