排查问题简单方法

近日调试Linux主机切换网络时IP地址不发生变化,总结了一下排查问题的方法。

问题的闭环:

1- 核心认知:现象不是问题本身

最开始的现象是:

1- 路由器A切到路由器B网络不通
2- 有线网络A切到有线网络B网络不通
3- 旧IP未刷新
4- DHCP没有Request/Discover

要素:

谁:eth0 / NM / DHCP client

时间:carrier down/up

预期:重新DHCP,刷新IP

实际:没有DHCP,IP不变

影响:切换新网络后仍使用旧地址

证据:dmesg 有NIC down / up 事件,tcpdump eth0 没有DHCP discovery/ request, ip addr 不变

2- 定义问题的方法

模板1:现象定义模板

在 【触发条件】下,【组件A】已经发生了【事实1】,但【组件B】没有发生【预期动作】,导致【结果】

情况:

在切换eth0网络时,
dmesg 已经发生了 NIC down/up
但 NM 没有发生 DHCP request
导致 eth0 无法正常工作

模板2: 预期 VS 实际

预期:

eth link down 
NM deactive connection 
ip route cleanup
eth0 link up
NM  activate connection 
DHCP Discover/Request
New IP Addr

实际

eth0 link down
NM 未 deactive connection 
No DHCP Request
ip addr 不变

模板3:边界定义

一定要问:

这个问题发生在哪一层?

这个问题没有发生在哪一层?

边界非常重要,防止在错误方向钻太久

不是PHY 完全没事件,Dmesg能看到 Down/up
不是DHCP Server不可用,因为改配置后能发DHCP
不是有线有异常,因为 Carrier变化事件能拿到
问题在NetworkManager 对 carrier 事件的策略处理

3- 方法论一:分层排查法

物理层

内核层

管理层

地址层

路由层

应用层

核心认知:每一层都只回答自己的问题,不要用一层的结果替代下一层的结论。

4- 方法论二:状态机排查法

NetworkManager不是简单执行命令,内部状态有:

disconnected
preparing
configuring
ip-config
activated
deactivating
failed

理想状态:

activated -- carrier lost -> deactivating --> disconnected - carrier on -> preparing/configuring -> ip-config -> activated

异常状态

activated - carrier down /up -> activated -> nothing 

遇到系统问题时先问,

这个组件有状态机吗?
当前卡在哪个状态?
预期应该跳到哪个状态?
是什么事件触发跳转?
这个事件到了吗?
事件到了以后为什么没有跳转?

5- 方法论三:事件链排查法

将问题抽象成一条事件链:

网线变化:
-> NIC carrier down /up 
-> kernel netlink event
-> NM device event
-> NM connection state change
-> DHCP client action
-> IP address update
-> route update
-> app traffic recovery

只需要找到问题:“链条断在哪一环?”

Kernel netlink evnet 到了
但NM conn/DHCP 无后续操作

查看相关的 NetworkManager.conf 配置文档

6- 方法论四:控制面和数据面分详离

控制面

控制面负责“决定网络应该怎么配置”

包括:

NetworkManager
DHCP
route table
DNS 
connection state
primary connection

数据面

数据面负责“真实流量怎么走”

包括:

实际 TCP/UDP 包
SSH 流量
PING 包
ARP/ND
网卡收发包

问题

控制面:
NM没有重新DHCP Request, IP addr 不变

数据面:
无DHCP包

7- 方法论五:反证和排除法

排查不要只寻找支持自己猜想的证据,也要主动找反证。

什么证据能证明我这个猜想是错的?


8- 方法论六: 从“谁拥有状态”入手

这个状态谁拥有?
谁负责更新它?
谁可能阻止他更新?

示例:

dmesg -> NIC down /up 
但是DHCP由 NM 的策略控制

总结

第一步定义触发条件

什么操作触发
拔线?
插线?
切交换机?
切DHCP网段?
重启NM?
开机阶段?

拒绝写“网络不通”这种模糊定义。

第二步:定义期望链路

比如:

拔旧网络
-> eth0 carrier down
-> NM deactivate wired conn
-> 删除旧 IP /route
-> 插新网线
-> eth0 carrier up
-> NM activate wired conn
-> DHCP Request / Discover
-> DHCP IP Offer
-> default route 更新

必须知道“正确的行为链路是什么”,否则就不知道哪儿错了。

第三步:实际打点

ip monitor link addr route
nmcli monitor
journalctl -u NetworkManager -f
tcpdump -i eth0 -n -vvv 'port 67 or 68'
watch -n 0.5 ip addr show eth0

第四步:找到第一处偏差

不要看最后哪里坏,要找第一处和预期不一样的地方

预期:NM connection 应该deactivate
实际:一直 activated

偏差:

carrier down/up 之后,NM没有进入重新配置流程

第五步:做最小修改验证

修改NetworkManager

[device-eth0-carrier]
match-device=interface-name:eth0
ignore-carrier=no
carrier-wait-timeout=0 

然后观察是否能正常ping 通8.8.8.8

认知:

1- 不要把看到事件等同于处理完成

2- 先缩小问题空间,再谈解决方案

Tips:

1- 当前现象是什么?
2- 触发条件是什么?
3- 正常情况下状态链画出来
4- 现在实际走在层一步,哪一层
5- 第一处偏差在哪里?
6- 哪些证据证明了这点?
7- 哪些假设已经被排除?
8- 下一步最小验证是什么?
9- 临时 workaround 是什么?
10- 长期修复方案和副作用是什么?

方法

1- 这条从外部事件到最终结果的状态链,断在哪一环?
2- 这一环是没有收到事件,还是收到了但策略不处理?
3- 是控制面没更新,还是数据面问题?
4- 是组件状态没跳转,还是跳转后执行失败?

发表评论: