Skip to content

排错方法论 ​

前面十九章介绍了一堆命令。这一章把它们串成一套有顺序的流程 —— 遇到"包不通"时该按什么顺序查,每一步排除什么。

核心原则只有一条:

从配置查到数据面,每一层都有明确的判定命令,不要跳步、不要猜。

四层模型 ​

text
┌──────────────────────────────────────────────────────────┐
│ ① 配置层    ovs-vsctl show                                │
│    问题:端口在不在?接对网桥了吗?有没有 error?            │
│    排除:拓扑接错、端口没建、设备不存在                       │
└───────────────────────┬──────────────────────────────────┘
                        ▼
┌──────────────────────────────────────────────────────────┐
│ ② 决策层    ovs-appctl ofproto/trace                      │
│    问题:按现在的流表,这个包会被怎么处理?                    │
│    排除:流表规则写错、优先级被截胡、匹配条件不对              │
└───────────────────────┬──────────────────────────────────┘
                        ▼
┌──────────────────────────────────────────────────────────┐
│ ③ 缓存层    ovs-appctl dpctl/dump-flows                   │
│    问题:内核里实际装的是什么?和 trace 一致吗?               │
│    排除:缓存过期未刷新、缓存爆炸                             │
└───────────────────────┬──────────────────────────────────┘
                        ▼
┌──────────────────────────────────────────────────────────┐
│ ④ 数据面    tcpdump                                       │
│    问题:包到底有没有来?发出去了吗?                         │
│    排除:对端没发、物理链路故障、MTU                          │
└──────────────────────────────────────────────────────────┘

顺序不能反。 直接上 tcpdump 是最常见的错误做法 —— 它只能告诉你"没看到包",无法告诉你为什么。

第一步:配置层 ​

bash
ovs-vsctl show

三个必看的点:

一、端口在不在、接对网桥了吗。 多网桥环境(br-int / br-ex / br-tun)里接错网桥非常常见。

二、有没有 error: 字样。

text
Port eth9
    Interface eth9
        error: "could not open network device eth9 (No such device)"

这说明设备不存在,add-port 时写错名字了。

三、端口类型和参数对不对。

bash
ovs-vsctl list Interface <端口名>

看 type、options、link_state、ofport。

接口是不是 up ​

bash
ip -br link

add-port 不会自动拉起接口(实验 1)。这是新手最常踩的坑。

快速判定表 ​

现象结论
端口不在 ovs-vsctl show 里没 add-port,或加错了网桥
有 error:设备不存在或类型配置有误
link_state: down接口没 up,或对端不通
ofport: -1端口创建失败,看 error

第二步:决策层(最关键) ​

bash
ovs-appctl ofproto/trace br0 "<描述你的包>"

这一步能解决大部分问题。 它直接回答"按当前流表,这个包会怎么样"。

看两行:

text
Datapath actions: drop          → 会被丢弃,往上找是哪条规则
Datapath actions: 3             → 会从 datapath 3 号口发出

如果是 drop,往上看命中了哪条规则、在哪张表:

text
 1. priority 0
    drop                        → 落到兜底规则,说明没有规则匹配

或者:

text
 0. No match.
    drop                        → 这张表里一条规则都没匹配上

三个高频误用

一、字段没写全。 不写 dl_type 的话它是 0,ip 规则永远匹配不上。用十六进制报文最保险(实验 8)。

二、只 trace 单向。 "能 ping 通"需要来回都有规则。反向也要 trace,把源和目的对调、in_port 换成对方的口。

三、trace 错了网桥。 多网桥环境要 trace 包进入的那个网桥。

ARP 单独验一遍 ​

三层不通,根子经常在二层:

bash
ovs-appctl ofproto/trace br0 "arp,in_port=1,dl_dst=ff:ff:ff:ff:ff:ff"

ARP 出不去,IP 包根本构造不出来。实验 6 和实验 16 都强调过这一点。

在主机侧确认:

bash
ip neigh

INCOMPLETE 就是 ARP 没通。

第三步:缓存层 ​

trace 说应该通,但实际不通 —— 检查内核缓存是不是还是旧的:

bash
ovs-appctl dpctl/dump-flows

对比 trace 给出的 Megaflow 和这里实际装的条目。不一致就清一下:

bash
ovs-appctl dpctl/del-flows

一律用 ovs-appctl dpctl/,不要用 ovs-dpctl

ovs-dpctl 直接通过 netlink 操作内核,绕过 ovs-vswitchd,会造成两边状态不一致,出现莫名其妙的反复 upcall(实验 1 有说明)。

用户态 datapath 下 ovs-dpctl 更是完全看不到东西(实验 19)。

顺便看整体健康度:

bash
ovs-dpctl show
text
lookups: hit:227 missed:164 lost:0
flows: 2
masks: hit:319 total:1 hit/pkt:0.82
指标健康有问题
lost0非零 = upcall 队列溢出,包被丢
missed / hitmissed 占少数missed 占比高 = 缓存命中率差
flows远低于 limit接近 limit = 缓存要爆
masks hit/pkt接近 1大于 5 = 掩码太多,查找成本高

第四步:数据面 ​

前三步都正常,才轮到抓包。

bash
# 在主机侧抓
tcpdump -i eth1 -n -l icmp

# 在 OVS 的端口上抓(内部端口不能直接抓,用 mirror)

脚本里抓包一定要加 -l

tcpdump 写文件时是块缓冲,被 timeout 杀掉时缓冲区内容会全部丢失,看起来像"什么都没抓到"(实验 12 踩过这个坑)。

抓不到的地方用端口镜像:

bash
ovs-vsctl \
  -- --id=@p get Port <要监控的口> \
  -- --id=@out get Port <抓包口> \
  -- --id=@m create Mirror name=m0 select-src-port=@p select-dst-port=@p output-port=@out \
  -- set Bridge br0 mirrors=@m

patch 端口抓不了(实验 3),只能用镜像。

隧道场景要抓underlay 接口才能看到封装包(实验 14)。

按现象索引 ​

完全不通 ​

bash
ovs-vsctl show                      # ① 端口在吗?有 error 吗?
ip -br link                         # ① 接口 up 吗?
ovs-ofctl dump-flows br0            # ② 流表是空的吗?(空 = 全丢)
ovs-appctl ofproto/trace br0 "..."  # ② 正向
ovs-appctl ofproto/trace br0 "..."  # ② 反向
ovs-appctl ofproto/trace br0 "arp,..." # ② ARP

时通时不通 ​

  • 检查 bond 是否在收敛(实验 11)
  • 检查是不是 MAC 表老化后重新泛洪
  • ovs-dpctl show 看 lost 是不是非零

ping 通但传大文件卡死 ​

MTU 问题,几乎一定是隧道场景(实验 13):

bash
ping -c 2 -M do -s 1400 <对端>     # 应该通
ping -c 2 -M do -s 1472 <对端>     # 不通 = 确诊

解法:underlay 上巨帧,或把 overlay MTU 调到 1450。

规则加了但 n_packets 一直是 0 ​

bash
ovs-ofctl dump-flows br0 | sort -t= -k2 -rn   # 按优先级看有没有被截胡
ovs-appctl ofproto/trace br0 "..."             # 看实际命中哪条

三个原因:匹配条件写错、被高优先级规则截胡、流量根本没到这个网桥。

配了 QoS 但限速没生效 ​

bash
ovs-appctl qos/show <端口>

流量全在 Default 队列 = set_queue 的规则没命中(实验 17)。

隧道不通 ​

严格按这个顺序(实验 14):

bash
ping <对端 underlay 地址>                        # 1. underlay 通吗
ovs-vsctl get Interface vxlan0 options:key      # 2. 两端 VNI 一致吗
ovs-vsctl get Interface vxlan0 options:remote_ip # 3. 互指了吗
tcpdump -i <underlay口> -n -l udp port 4789     # 4. 有封装包吗

流表是空的但还在转发 ​

fail_mode=standalone 且控制器断开,OVS 装了一条隐藏的高优先级 NORMAL(实验 18):

bash
ovs-appctl ofproto/trace br0 "..."     # 会看到 priority 15790320

dump-flows 看不到它,只有 trace 能看到。

从报错命令定位组件 ​

实验 2 那张表反过来用,是个很快的定位手段:

什么命令出问题说明哪个组件有问题
ovs-vsctl 卡住ovsdb-server 挂了,或 db.sock 不在
ovs-ofctl 连不上ovs-vswitchd 挂了,或网桥不存在
ovs-appctl 报错ovs-vswitchd 挂了
ovs-dpctl 空输出内核模块没加载,或者是 netdev datapath

进程和日志:

bash
ps -ef | grep -E "ovsdb-server|ovs-vswitchd"
tail -100 /var/log/openvswitch/ovs-vswitchd.log
tail -100 /var/log/openvswitch/ovsdb-server.log

一份可以照抄的检查脚本 ​

bash
#!/usr/bin/env bash
# OVS 快速体检
BR=${1:-br0}

echo "=== 进程 ==="
ps -eo pid,args | grep -E "ovsdb-server|ovs-vswitchd" | grep -v grep

echo "=== 配置 ==="
ovs-vsctl show

echo "=== 有 error 的接口 ==="
ovs-vsctl --columns=name,error list Interface | grep -B1 -i error || echo "无"

echo "=== 链路状态 down 的接口 ==="
ovs-vsctl --columns=name,link_state list Interface | grep -B1 down || echo "无"

echo "=== 流表条数 ==="
ovs-ofctl dump-flows "$BR" | wc -l

echo "=== datapath 健康度 ==="
ovs-dpctl show 2>/dev/null || ovs-appctl dpctl/show

echo "=== 最近的错误日志 ==="
grep -iE "err|warn" /var/log/openvswitch/ovs-vswitchd.log | tail -20

小结 ​

  • 四层顺序:配置 → 决策 → 缓存 → 数据面。不要跳步,不要一上来就 tcpdump
  • ① ovs-vsctl show 看端口、error:、接错网桥;ip -br link 看接口是否 up
  • ② ofproto/trace 是最关键的一步,能解决大部分问题
    • 字段要写全,正反两个方向都要 trace,ARP 单独验
  • ③ ovs-appctl dpctl/dump-flows 对比缓存,ovs-dpctl show 看 lost / missed / masks
    • 一律用 ovs-appctl dpctl/,不要用 ovs-dpctl 做修改
  • ④ tcpdump 抓包,脚本里必须加 -l;抓不到的地方用 mirror;隧道要抓 underlay
  • 从报错的命令能直接定位是哪个组件出了问题
  • 大文件卡死 = MTU;限速无效 = 看队列统计;隧道不通 = 先查 underlay

下一章:常见坑合集。