ftrace calico netns问题3
Linux/Kubernetes unregister_netdevice: waiting for eth0 to become free. Usage count = 1 完整分析文档
1. 故障现象
节点:
spider50
持续出现:
kernel: unregister_netdevice: waiting for eth0 to become free. Usage count = 1
频率:
约每 10 秒一次。
表现:
-
Kubernetes pod 正常运行
-
Docker 无明显异常容器
-
lsns -t net无孤儿 netns -
/proc/*/ns/net无异常引用 -
重启 canal/calico 后仍存在
2. 基础原理
2.1 Linux 网络设备生命周期
Linux 网络设备:
eth0|v
struct net_device|v
引用计数 refcnt|v
unregister_netdevice()
删除网络设备流程:
ip link del xxx|v
unregister_netdevice()|v
netdev_run_todo()|v
等待引用释放|v
dev_put()|v
refcnt = 0|v
free net_device
你的错误:
Usage count = 1
表示:
net_device refcount != 0
还有一个对象持有:
eth0 ---> refcount +1
所以内核不能释放。
3. 为什么看不到异常进程?
这是关键。
很多人会误认为:
有引用 = 有进程
实际不是。
Linux 网络设备引用来源:
| 类型 | 示例 |
|---|---|
| socket | TCP/UDP socket |
| route | 路由缓存 |
| neighbor | ARP/neigh |
| iptables | conntrack |
| bridge | bridge port |
| veth peer | 容器网卡 |
| netfilter | hook |
| kernel thread | 内核线程 |
| CNI残留 | calico/flannel |
所以:
lsns
docker ps
ps
都正常,不代表没有引用。
4. 已完成排查分析
4.1 netns检查
执行:
lsns -t net
结果:
4026532004 host
...
无孤儿 namespace。
说明:
不是:
deleted container netns
问题。
4.2 /proc namespace检查
执行:
ls -l /proc/*/ns/net
发现:
/proc/188886/ns/net
对应:
[calico] <defunct>
但是:
readlink /proc/188886/ns/net
为空。
原因:
Zombie 进程:
task_struct还存在
mm/ns已经释放
排除。
5. 关键突破点
你抓到了:
unregister_netdevice_many
调用栈:
unregister_netdevice_many|notifier_call_chain|call_netdevice_notifiers_info|netdev_run_todo|rtnetlink_rcv|netlink_unicast|netlink_sendmsg|SYSC_sendto|system_call
说明:
删除动作来源:
不是 kubelet
不是 docker
不是 container stop
而是:
用户态通过 NETLINK_ROUTE 删除网卡
也就是:
ip link del xxx
或者:
CNI DEL
6. 真正的问题定位
继续看:
ps -fp 188907
得到:
/opt/cni/bin/calico
同时:
cat /proc/188907/cgroup
看到:
system.slice/kubelet.service
结论:
calico CNI 在不停执行 DEL 网络操作
流程:
kubelet||
CNI DEL||
calico CNI binary||
netlink||
删除 caliXXX||
触发 unregister_netdevice||
eth0引用无法释放||
内核打印
7. 为什么重启 canal 仍然失败?
因为真正残留不是 canal 容器。
你的数据:
Pod IP
172.18.105.238
对应:
zfzhaobiao-middleware-extractor
节点:
spider50
查看:
ip ne | grep 172.18.105.238
得到:
172.18.105.238 dev cali99badac5327
说明:
当前:
cali99badac5327
仍然存在。
但是 CNI cache:
/var/lib/cni/networks/k8s-pod-network/
也存在:
172.18.105.238
说明:
IPAM记录存在
+
真实veth存在
形成:
CNI状态不一致
8. 根因模型
正常流程
Pod删除:
kubelet|
CNI DEL|
calico|
删除 veth|
释放IP|
删除cache
当前流程
异常:
Pod生命周期异常||
CNI DEL执行||
veth删除失败||
IPAM cache未清||
caliXXX残留||
net_device refcount=1||
内核等待
9. 为什么 eth0 报错,不是 cali?
这是 Linux 网络命名空间机制。
容器:
eth0|veth pair|
caliXXXX|
host namespace
结构:
container nseth0||
veth peer||
host nscali99badac5327
删除:
cali99badac5327
过程中:
kernel检查:
eth0 peer
还有引用。
所以打印:
eth0 Usage count=1
10. 处理方案
方案1(推荐):清理残留 Pod 网络
1. 确认pod
kubectl get pod -A -o wide | grep 172.18.105.238
得到:
zfzhaobiao-middleware-extractor
删除:
kubectl delete pod \
zfzhaobiao-middleware-extractor-6c8b854797-pmvtl \
-n spider
等待。
2. 查看残留
ip link | grep cali
找到:
cali99badac5327
删除:
ip link delete cali99badac5327
3. 删除CNI IPAM残留
确认:
cat /var/lib/cni/networks/k8s-pod-network/172.18.105.238
如果为空或旧:
删除:
rm -f /var/lib/cni/networks/k8s-pod-network/172.18.105.238
方案2:重启 kubelet + calico
顺序:
不要先重启calico。
正确:
systemctl stop kubelet
删除残留:
ip link delete caliXXXX
清理:
rm -f /var/lib/cni/networks/k8s-pod-network/*
然后:
systemctl restart kubelet
最后:
kubectl delete pod -n kube-system -l k8s-app=canal
方案3:强制刷新整个CNI状态
适合大量污染节点。
停业务调度
kubectl cordon spider50
驱逐:
kubectl drain spider50 \
--ignore-daemonsets \
--delete-emptydir-data
停止:
systemctl stop kubelet
清:
rm -rf /var/lib/cni/*
rm -rf /var/run/calico/*
rm -rf /var/lib/calico/*
重启:
systemctl start kubelet
恢复:
kubectl uncordon spider50
11. 如何证明修复成功
查看错误消失
dmesg -w
不再出现:
unregister_netdevice
查看calico设备
ip link | grep cali
数量正常。
查看引用
ip addr
无孤儿。
12. 长期优化
12.1 升级calico
你环境:
canal
calico-node
docker
k8s旧版本
存在已知CNI残留风险。
建议:
升级:
calico >= v3.24
12.2 开启CNI日志
修改:
/etc/cni/net.d/*
增加:
log_level: debug
12.3 监控残留
定期检查:
ip link | grep cali | wc -lls /var/lib/cni/networks/k8s-pod-network | wc -l
异常:
cali数量 >> pod数量
说明泄露。
13. 最终结论
本次不是:
-
❌ sandbox泄露
-
❌ netns泄露
-
❌ zombie进程
-
❌ docker残留
真正原因:
Calico CNI DEL流程异常||
残留cali veth||
net_device引用计数未释放||
unregister_netdevice等待||
内核每10秒打印
当前最有效处理:
删除172.18.105.238对应pod
↓
删除残留cali99badac5327
↓
清理/var/lib/cni/networks/k8s-pod-network/172.18.105.238
↓
重启kubelet
↓
观察dmesg
你已经通过 kprobe 定位到了 netlink → CNI → calico 这一层,方向是正确的。下一步不是查 namespace,而是清理 CNI state + cali veth + pod sandbox状态一致性。
