系统资源暂时不可⽤(Resource Temporarily unavaliable)的背后故事: 句柄(FD)占用

162 阅读2分钟
记录下一次排错经历,暴露出我们监控不全面的问题,但是这个问题的根因,事实上目前还没有找到。

起因: 某客户环境上的错误: 切换⽤⼾时报告 Resource Temporarily unavaliable

在本地也曾报告过,但是当时没有研究出,很快也重启了系统

在客户环境上之前出现了⼀次,没有重视

在6⽉下旬再次出现

经过3天排查,找到原因

  1. ⾸先排除cpu, 内存,硬盘等的问题

  2. 之前曾经怀疑channel-redis版本问题,实际上并不是

  3. ntp等定时任务⼀直在出错,因为在这个封闭的环境⽆法上⽹,也⽆法进⾏时间同步等,但实际上这个也不是原因

  4. 查看各类⽇志,包括系统的及⾃⾝应⽤的,也没有发现问题

  5. 新装容器,也没有lsof,到周⼆早上决定安装lsof

  6. Ulimit 结果看不出异常

  7. lsof -Ki未看出异常

60和56开头的都是grafen进程

9366 bash, 403 gossiptrans.py 9375 manage.py

  1. Lsof -n 运⾏半天后获得结果

cat lsof.txt | grep juyun > lsof_juyun.txt 结果有1600万⾏

耗时很⻓,约5-6⼩时

  1. 想重启再跑⼀次,结果出现新状况

a. 两个容器冲突

i. 旧容器是之前⼀次升级为保险留的,但是两个容器都使⽤了相同的端⼝配置,出时启动结果出问题

  1. 前⼀次的步骤没做完,导致遗留的定时炸弹,只要重启必须挂掉

  2. 最主要问题出现在ES上,两个使⽤同端⼝的ES互相⼲扰,导致出错3. 紧急情况下,删除所有旧数据了事(稳妥情况应该reindex)

  1. 第⼆次运⾏lsof -n 的结果中发现467这个数字出现的很多,查看进程是gossiptrans.py

这⾥犯了⼀个错误, 使⽤cat lsof.txt | grep 467发现有很多⾏,但是这个grep把许多其它的467算进去了,并不精确,也没有仔细排查准确的467是什么

lsof -n > lsof.txt; cat lsof.txt|grep 467| wc -l

两条命令合在⼀起执⾏

  1. 重启后发现pa-result没有⽇志问题可能是pm在之前的ES出错后没有正常⼯作,重启pm后问题解决

  2. 周四重新使⽤lsof -n查看结果

Lsof -n

列出所有的占⽤,包括⽂件,⽹络,内存中的占⽤

lsof -n > lsof_juyun.txt | awk '{count[$2]++} END {for (i in count) print i, count[i]}' lsof_juyun.txt |

sort -k2,2nr 看看结果, 把结果告诉我, 并找⼀下排名前⼏的进程号的具体信息,判断还是

gossiptrans.py,这次是进程号排序,正确性有保障周五的结果及上⼀次的结果对⽐

进程记录

33191,gossiptrans.py 重启后为66391

33691, es 这个之前观察过,基数较⼤,但是会回收,不会⼀直增⻓

464, kafka

32162, flower

32622, grafen-server (django)

32128, celery beat周四的第⼀次结果周四的第⼆次结果周五的完整结果, gossiptrans是67361结果

周五结果与临时结论因环境⽆法进⾏⻓时间跟踪,只能暂时认为lsof能观测到的fd变化表明这个问题得到解决(缺少⻓期观察结果)

总结

  1. 监控上需要有对FD的监控,不能等出问题时再调查(lsof运⾏⼀次要花数个⼩时这种现象是可以避免的)

    a. 做⼀个⾃监控应⽤,每⼩时⼀条数据,给出 cpu,mem,disk, fd的使⽤情况

    b. 最好能给出按天的diff

    c. 以及⼀些可能的业务数据监控,这个要定制

     1. ⽐如每个业务进程的历史数据(cpu,mem,fd等)这种数据会很有价值
    
         a. 有问题肯定要细查,这个数据就是细查
    
  2. 从实际情况来看,确定监控范围只能⻅招拆招,⽆法事先预测与应对,只能根据现有经验确定要监控哪些,仍然做不到预判

  3. 各种程序的⽇志需要输出合理的内容,既要有意义,⼜不要太丰富

    a. 能证明⾃⼰在正常运⾏

     i. 这是为了让任何⽤⼾都可以轻易的明了⽬标程序的状态,
    
         1. 正常还是不正常,
    
         2. 问题出在哪(有难度)
    

    b. 能证明⾃⼰在正常执⾏业务(有业务数据总结)

    c. 把正常⽇志与出错⽇志分开(防⽌回滚导致漏掉重要⽇志),出错⽇志是给开发⼈员看的

    d. 要有时间戳信息

  4. gossiptrans.py出问题的根因是什么?

    ⽬前还不能重现这个问题,我在本地通过⼤规模的修改代码解决了问题

    1. 原代码中有引⽤django项⽬的部分,实际上原版中仅⽤到了grafen中的logger功能(我认为这个是主要原因,因为lsof的结果显⽰pandas等包被多次引⼊(40+次)⽽这个引⼊的过程只会发⽣在django中)

      a. 但是也没有找到直接引⼊pandas的地⽅,另外也没有找到间接引⽤的地⽅

    2. 把⼀个循环中的random函数进⾏了优化,做成⼀个随机数池,让赋值的过程在这个池中随机取值

lsof是什么?

Resource temporarily unavailable

这句话通常是操作系统或程序返回的错误信息之⼀,表⽰请求的资源当前暂时不可⽤。这个错误信息

通常在多种情况下出现,包括但不限于以下情况:

系统资源耗尽:可能是由于系统资源(如内存、⽂件句柄、⽹络连接等)达到了上限,导致⽆法为新

请求分配⾜够的资源。

并发限制:某些操作系统或程序可能对并发请求有限制,当并发请求过多时,会返回这个错误信息。

临时性问题:资源暂时不可⽤可能是由于系统负载过⾼、瞬时的⽹络问题或其他临时性情况导致的。

系统繁忙:系统当前繁忙处理其他任务,⽆法⽴即响应新的请求,因此返回这个错误信息。

资源被锁定:资源可能被其他进程或线程锁定,导致暂时⽆法访问。

当程序收到这个错误信息时,通常会尝试在稍后的时间重新尝试请求,或者采取其他适当的措施来处

理这种情况。对于开发⼈员来说,处理这种错误信息通常需要考虑重试机制、优化资源使⽤、提⾼系

统容错性等⽅⾯。

Too many open files --> ulimit

Ulimit

ulimit 是⼀个⽤于设置和显⽰进程资源限制的命令。它⽤于控制⼀个⽤⼾会话或进程的资源使⽤情

况,包括可打开的⽂件数、最⼤进程数、最⼤内存使⽤量等。通过设置合适的资源限制,可以保护系

统免受过度资源消耗或滥⽤。

ulimit 命令通常在命令⾏终端中使⽤,并且可以由系统管理员或特权⽤⼾使⽤。它的常⻅⽤法包括:

显⽰当前的资源限制:使⽤ ulimit -a 命令可以显⽰当前会话中各种资源的限制值。设置资源限制:使⽤ ulimit -n 可以设置当前会话中允许打开的⽂件数的最⼤值。类似地,

可以使⽤其他选项来设置不同类型的资源限制。

临时修改资源限制:可以使⽤ ulimit 命令临时修改当前会话的资源限制。这些修改只在当前会话中有

效,不会影响其他⽤⼾或系统的全局设置。

需要注意的是,ulimit 命令的资源限制是基于⽤⼾会话或进程的,⽽不是全局的。不同⽤⼾或进程可

以具有不同的资源限制。此外,ulimit 命令的使⽤可能受到系统管理员设置的限制,某些资源限制可

能⽆法超过预设的最⼤值。

总⽽⾔之,ulimit 命令是⽤于管理和控制进程资源限制的⼯具,可以帮助确保系统的稳定性和安全

性。

⽂件句柄(FD)

⽂件句柄数指的是⼀个进程可以同时打开的⽂件描述符(file descriptor)的数量。在类 Unix 系统

中,⼀切皆⽂件的概念也包括了⽂件句柄。⽂件句柄是操作系统⽤来标识和访问⽂件或者其他 I/O 设备

的抽象概念。

每个打开的⽂件、套接字、管道等都会被内核分配⼀个⽂件描述符(file descriptor),这个描述符是

⼀个⾮负整数,⽤来标识这个打开的⽂件或者设备。⽂件描述符是进程访问⽂件的接⼝,通过⽂件描

述符,进程可以读取、写⼊、关闭⽂件等操作。

⽂件句柄数限制了⼀个进程可以同时打开的⽂件数量。这个限制是为了保护系统资源不被滥⽤,防⽌

⼀个进程打开过多⽂件导致系统资源耗尽。如果⼀个进程尝试打开超过其限制的⽂件数量,将会收到

"Too many open files" 的错误。

可以使⽤ ulimit -n 命令来查看当前会话的⽂件句柄限制。系统管理员可以通过调整这个限制来控制每

个⽤⼾或进程可以打开的⽂件数量,以平衡系统资源的使⽤和安全性。合理设置⽂件句柄数对于系统

的稳定性和性能是⾮常重要的。

Lsof

lsof 是⼀个⽤于列出系统中打开的⽂件的⼯具,它的名字是 "list open files" 的缩写。通过 lsof 命令,

你可以查看哪些进程打开了哪些⽂件,以及这些⽂件的详细信息。这对于系统管理员和⽤⼾来说是⾮常有⽤的,特别是在需要查找⽂件被哪些进程占⽤或者需要释放被占⽤的⽂件时。

以下是 lsof 命令的⼀些常⻅⽤途:

查看打开的⽂件列表:lsof 命令可以列出系统中当前打开的⽂件,包括普通⽂件、⽬录、设备⽂件、

⽹络 sockets 等。

查看特定⽂件的打开情况:通过指定⽂件名或路径,可以查看特定⽂件被哪些进程打开。

查看特定进程打开的⽂件:通过指定进程号(PID),可以查看特定进程打开的⽂件列表。

查看⽹络连接情况:lsof 还可以显⽰⽹络连接的信息,包括哪些进程正在使⽤⽹络连接。

查看被删除的⽂件:即使⽂件被删除,仍然可以通过 lsof 找到仍然被进程使⽤的⽂件。

查看进程使⽤的端⼝:可以通过 lsof 查看哪些进程正在使⽤哪些端⼝。

lsof 命令在 Linux 和类 Unix 系统中⾮常常⽤,可以帮助⽤⼾和管理员更好地了解系统中⽂件和进程之

间的关系,以及及时发现和解决⼀些⽂件相关的问题。

最⼤句柄数与最⼤进程数设置

最⼤句柄数 (ulimit -n)

每个进程都可以打开⼀定数量的⽂件描述符,这些⽂件描述符被称为句柄。

句柄数限制了⼀个进程可以同时打开的最⼤⽂件数。

可以使⽤ ulimit -n 命令查看和设置当前⽤⼾的最⼤句柄数。

通常情况下,最⼤句柄数默认设置为 1024。可以根据应⽤程序的需求进⾏适当调整。

最⼤进程数 (ulimit -u)

Linux 系统可以同时运⾏的最⼤进程数。可以使⽤ ulimit -u 命令查看和设置当前⽤⼾的最⼤进程数。

默认情况下,最⼤进程数通常设置为 1024 或 4096。取决于操作系统的版本和配置。

Lsof -Ki

当你运⾏ lsof -Ki 命令时,它会列出当前系统中所有的⽹络连接,并显⽰与这些连接相关的进程信息。这些信息包括进程 ID(PID)、进程名称、⽤⼾、协议、本地地址、远程地址等。这个命令常⽤于查看哪些进程正在使⽤⽹络连接,以及连接的详细信息。

Lsof -n

列出所有的占⽤,包括⽂件,⽹络,内存中的占⽤

lsof -n > lsof_juyun.txt | awk '{count[$2]++} END {for (i in count) print i, count[i]}' lsof_juyun.txt |

sort -k2,2nr