主题
排错方法论
前面十九章介绍了一堆命令。这一章把它们串成一套有顺序的流程 —— 遇到"包不通"时该按什么顺序查,每一步排除什么。
核心原则只有一条:
从配置查到数据面,每一层都有明确的判定命令,不要跳步、不要猜。
四层模型
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 linkadd-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 neighINCOMPLETE 就是 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 showtext
lookups: hit:227 missed:164 lost:0
flows: 2
masks: hit:319 total:1 hit/pkt:0.82| 指标 | 健康 | 有问题 |
|---|---|---|
lost | 0 | 非零 = upcall 队列溢出,包被丢 |
missed / hit | missed 占少数 | 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=@mpatch 端口抓不了(实验 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 15790320dump-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
下一章:常见坑合集。