VPS报Too many open files,别只调ulimit:先分清进程、systemd和文件句柄泄漏

上限太小和句柄持续泄漏是两类问题;先看当前用量、增长趋势和服务实际限制
发布于 更新于
10

VPS出现Too many open filesEMFILE时,含义通常不是磁盘文件太多,而是某个进程无法再取得新的文件描述符。Linux用文件描述符表示普通文件、网络Socket、管道、事件通知等对象,所以故障可能表现为网站偶发502、数据库连接失败、日志无法写入,甚至新SSH会话或DNS请求异常。

最容易犯的错误是看到报错就把ulimit -n改得很大。上限太小确实可能限制正常并发,但如果程序不断打开文件或连接而不关闭,调大上限只会让故障晚一点发生。正确顺序是:确认谁报错,读取该进程的真实限制,统计当前句柄及类型,再判断是容量规划还是泄漏

先区分四个层级的限制

层级 常见查看位置 容易误判的地方
当前Shell ulimit -Snulimit -Hn 不能代表systemd服务
单个进程 /proc/PID/limits 软限制与硬限制可能不同
systemd单元 LimitNOFILE 改完配置但未重载或重启
系统文件表 file-nrfile-max 全局压力与单进程EMFILE混为一谈

应用日志若明确写出EMFILE,通常指该进程达到自己的描述符限制;若内核或多个无关服务同时异常,则还要检查系统级文件表、内存和连接压力。

第一步:定位真正报错的服务与PID

systemctl --failed
systemctl status your-service --no-pager
journalctl -u your-service --since "-30 minutes" --no-pager
systemctl show your-service -p MainPID -p LimitNOFILE

不要只搜索网页入口的Nginx日志。请求可能经过反向代理、应用、数据库和队列,任一层耗尽描述符都可能在前一层表现为502或超时。记录首次报错时间、服务名、主PID、请求量和近期版本变更。

如果服务显示运行但公网访问失败,还应先用极跃圈的VPS端口不通分层排查确认监听地址、防火墙和安全组,避免把网络入口问题误当成FD耗尽。

第二步:读取进程的真实上限与当前用量

cat /proc/<PID>/limits | grep -i "open files"
find /proc/<PID>/fd -maxdepth 1 -type l | wc -l
ls -l /proc/<PID>/fd | head

/proc/PID/limits反映正在运行进程的实际限制,比登录Shell中的ulimit更可靠。当前FD数量接近软限制,能解释为什么新文件或Socket无法打开;但还不能说明这些句柄是否合理。

进程重启后PID会变化,自动采集时应通过systemd主PID、容器ID或服务发现获取当前实例,不要长期写死一个PID。

第三步:看句柄是什么,而不只是数数量

lsof -nP -p <PID>
lsof -nP -p <PID> | awk '{print $5}' | sort | uniq -c | sort -nr
ss -s
  • 大量网络Socket:核对并发、Keep-Alive、连接池、上游超时和客户端断开;
  • 大量重复日志文件:检查轮转后应用是否仍持有旧文件;
  • 大量管道或匿名inode:检查工作进程、IPC和事件循环是否正常回收;
  • 大量相同路径:检查应用是否在请求循环中重复打开而未关闭;
  • 已删除文件:文件名消失不代表进程已经释放句柄和磁盘空间。

lsof输出可能很大,也可能包含路径、用户名或连接信息。保存工单证据时应脱敏,不要公开Token、数据库凭据或客户地址。

第四步:用趋势区分正常峰值与泄漏

一次快照只能说明当前状态。更有效的是每隔固定时间记录服务PID、FD数量、连接数、请求量和错误率:

趋势 更可能的解释 下一步
随流量上升,回落后下降 正常工作集或连接池 评估峰值余量
到固定平台后稳定 池大小或缓存上限 核对配置是否符合容量
相近负载下持续单向增长 文件、Socket或管道未关闭 在测试环境复现并做代码级分析
重启归零后按相似斜率增长 泄漏嫌疑增强 关联请求类型、版本和调用栈

若FD增长同时伴随内存持续上升,可结合极跃圈的VPS缓存、RSS与内存泄漏判断方法交叉确认,但文件描述符泄漏和内存泄漏不是同一个概念。

systemd服务应在单元层调整

确认正常峰值确实超过当前限制后,可使用systemd drop-in,而不是直接修改发行版自带单元文件:

sudo systemctl edit your-service
[Service]
LimitNOFILE=65536
sudo systemctl daemon-reload
sudo systemctl restart your-service
systemctl show your-service -p MainPID -p LimitNOFILE

65536只是示例,不是所有服务的推荐值。应根据已测峰值、每个连接的资源成本、程序自身上限和系统容量确定。数据库、反向代理、容器运行时也可能有独立配置,只有一层放大并不一定生效。

容器里的限制要从宿主机和运行时一起看

容器内执行ulimit看到的值来自容器启动配置和宿主机约束。排查时同时确认容器内进程限制、容器运行参数、systemd对容器运行时的限制,以及宿主机文件表。不要只在运行中的容器里临时修改,因为重建后通常不会保留。

系统级文件表接近上限时怎么判断

cat /proc/sys/fs/file-nr
cat /proc/sys/fs/file-max
cat /proc/sys/fs/nr_open

字段含义会随内核文档和版本表现,不能只凭一个数字机械判断。若多个服务同时无法打开文件,应结合内核日志、内存、进程FD分布和系统总量确认。调整fs.file-max前必须先找出主要消费者;全局上限变大不能修复单个服务的泄漏。

恢复服务与根因修复要分开

  1. 在明确影响和恢复路径后,受控重启故障服务以释放旧句柄;
  2. 立即验证端口、健康检查、关键请求和日志写入;
  3. 保留重启前后的FD数量与句柄类型;
  4. 为FD使用率、增长速度和EMFILE日志设置告警;
  5. 在测试环境复现泄漏,修复文件关闭、连接池或超时逻辑;
  6. 根据正常峰值设置有证据的LimitNOFILE与容量余量。

不要用定时重启长期掩盖泄漏。临时重启可以恢复业务,但必须同时跟踪再次增长时间,否则下一次故障只会换个时点出现。

结论

VPS报Too many open files时,先查看报错进程的真实限制和FD用量,再按句柄类型与时间趋势判断容量不足还是资源泄漏。Shell的ulimit、systemd的LimitNOFILE、容器限制和系统文件表属于不同层级;只有定位根因后再调整对应上限,才能避免“数值变大、故障照旧”。

常见问题(FAQ)

执行ulimit -n调大后,为什么VPS服务仍然报Too many open files?
ulimit通常只影响当前Shell及其子进程。由systemd启动的服务使用单元配置和管理器限制,应查看服务主进程的/proc/PID/limits与systemctl show结果,而不是用登录终端的数值推断。
打开文件数接近上限,就一定是程序泄漏吗?
不一定。高并发连接、日志、缓存文件和数据库文件都会合理占用描述符。应结合请求量观察FD趋势、类型与关闭情况;在负载回落后仍持续单向增长,才更值得怀疑泄漏。
可以把LimitNOFILE直接改成很大的数吗?
不建议盲目放大。上限提高会推迟报错,但不能修复泄漏,还可能让故障消耗更多内存和内核资源。应先测正常峰值、预留余量,并验证程序、systemd与系统级限制是否匹配。
重启服务后错误消失,算修好了吗?
重启只会关闭旧进程持有的描述符,可能暂时恢复容量。如果FD随后以相似速度再次增长,根因仍在。应保留重启前后的用量、句柄类型、日志和请求量趋势继续定位。

本文由作者原创/授权发布于极跃圈(jiyueip.com)未经许可,禁止转载。题图来自Unsplash,基于CC0协议。

声明:极跃圈(JIYUEIP.com)内网友所发表的所有内容及言论仅代表其本人,并不反映任何极跃圈(JIYUEIP.com)之意见及观点。

0 讨论
热门最新
总结
暂无总结
0 / 600