不要使用 lsof 检查打开的文件描述符 file descriptor 数量

前言

生产环境的 K8s 节点又被 NodeHasFDPressure 坑了,修改完 limits.conf 直接终端都进不去了……为了快速使服务恢复可用状态,没有对节点内部进行更细的排查,直接将节点进行排水处理才使服务正常……

令我略无语的,是在事后的复盘以及对 FD 问题解决详细的索引中,发现 lsof 的坑,也是(可能)一直没能确定 FD 根本问题的所在。

lsof

lsof 的功能是列出打开的文件,在 man 手册的 lsof 描述中,它有一段解释:

An open file may be a regular file, a directory, a block special file, a character special file, an executing text reference, a library, a stream or a network file (Internet socket, NFS file or UNIX domain socket.)

它解释道:“一个打开的文件可以是一个普通文件、一个目录、一个块状文件、一个字符文件、一个执行中的文本引用、一个库、一个流或一个网络文件(网络套接字、NFS 文件或 UNIX 域套接字”

问题在于, lsof 列出所有打开的文件,包括不使用文件描述符的文件,所以使用 lsof |wc -l 统计出来的数目,会大于 FD 的 file-max,从而没法确定真正大量使用 FD 的进程。

这个坑,是跟着自己的片面理解和搜索引擎上的「引导」,两脚踩了进去……

实践

使用 lsof 统计(这里还 hung 住好一会):

$ lsof| wc -l
2520903

查看系统的文件描述符总的限制数:

$ cat /proc/sys/fs/file-max
1048576

这里便能看出一些问题, lsof 统计的数目明显大于 file-max ,综上,lsof 并不适合用来统计打开的文件描述符的数量。

那应该如何统计 fd 打开文件的数量呢?k8s 的守护进程 NPD 的 fd 检查脚本是一个很好的例子,它的内容如下:

$ cat check_fd.sh
#!/bin/bash
# check max fd open files
OK=0
NONOK=1
UNKNOWN=2

cd /host/proc

count=$(find -maxdepth 1 -type d -name '[0-9]*' | xargs -I {} ls {}/fd | wc -l)
max=$(cat /host/proc/sys/fs/file-max)

if [[ $count -gt $((max*80/100)) ]]; then
echo "current fd usage is $count and max is $max"
exit $NONOK
fi
echo "node has no fd pressure"
exit $OK

因为 NPD 把宿主机的 /proc 挂载到了 /host/proc/, 实际上它是遍历了宿主机 /proc 来统计 FD 数量,然后对 file-max 进行对比来断言 NodeHasFDPressure

在节点上的操作就可以直接在 /proc 目录下使用此命令统计 FD 数量了:

$ find -maxdepth 1 -type d -name '[0-9]*' | xargs -I {} ls {}/fd | wc -l
42958

统计前10个最多 fd 打开数的进程:

$ find -maxdepth 1 -type d -name '[0-9]*' 
     -exec bash -c "ls {}/fd/ | wc -l | tr '
' ' '" ; 
     -printf "fds (PID = %P), command: " 
     -exec bash -c "tr '' ' ' < {}/cmdline" ; 
     -exec echo ; | sort -rn | head

参考链接:
How Many Open Files?
Counting open files per process
fs.txt full documentation
Why file-nr and lsof count on open files differs?

原文地址:https://www.cnblogs.com/nnylee/p/14932524.html