前言
生产环境的 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?