登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

Linux 服务怎么限制文件描述符:LimitNOFILE、启动用户与日志检查

来源:17golang原创

时间:2026-08-26 07:34:33 493浏览 收藏

线上服务出现“Too many open files”时,先别急着把机器级限制一口气调大。systemd 启动的服务有自己的进程上下文,当前终端里执行 ulimit -n 65535,并不会自动改变已经由 systemd 拉起的进程。更稳妥的做法是给目标 unit 写入 LimitNOFILE,再用进程实际限制和日志确认配置确实生效。

要点速览
  • LimitNOFILE 是 systemd 服务级配置,适合把文件描述符上限跟着 unit 管理。
  • 改完 drop-in 后必须执行 daemon-reload 和服务重启,并用 /proc/PID/limits 复核。
  • 上限不是越大越好,启动用户、连接池、日志轮转和内核资源要一起检查。
  • 发布前保留 systemctl catsystemctl showjournalctl 的结果,回滚时只需移除 drop-in。

先区分当前 Shell 限制和服务进程限制

同一台机器上,下面两条命令可能给出不同答案:

ulimit -n
systemctl show order-api.service -p LimitNOFILE

第一条只反映当前 Shell 的资源限制;第二条查看 systemd 记录给 unit 的属性。真正处理请求的是服务进程,所以最终还要拿到 PID 检查 /proc/PID/limits。如果只改了登录用户的 shell 配置,服务重启后仍可能回到旧上限。

Linux 服务 LimitNOFILE 从默认限制到进程实际上限的对比

用 drop-in 给 order-api.service 设置上限

不要直接改发行版提供的 unit 文件,使用 drop-in 更容易审计和回滚:

sudo systemctl edit order-api.service

在打开的编辑器中写入:

[Service]
LimitNOFILE=65535

这里的数值要按连接数、日志文件、监听 socket 和程序自身打开的临时文件估算。一个服务如果有 2 万个长连接,再加上数据库连接池、文件缓存和运行时开销,65535 可能是合理的起点;如果只是小型定时任务,直接套用这个数字反而会掩盖句柄泄漏。

重载、重启后检查配置来源和实际值

配置保存后按固定顺序执行:

sudo systemctl daemon-reload
sudo systemctl restart order-api.service
systemctl is-active order-api.service
systemctl cat order-api.service
systemctl show order-api.service -p User -p LimitNOFILE -p FragmentPath

systemctl cat 用来确认 drop-in 确实被合并;systemctl show 则能看到 systemd 当前解析后的属性。这里别只看“active”,服务能启动不代表限制已经按预期传递给进程。

先取 PID,再检查进程上下文:

pid=$(systemctl show -p MainPID --value order-api.service)
ps -o pid,user,comm -p "$pid"
grep -i 'open files' "/proc/$pid/limits"
ls "/proc/$pid/fd" | wc -l

最后一条是当前打开句柄数的近似快照,不等于峰值。对长期运行的服务,建议把它和进程指标、连接池指标一起观察,避免仅凭一次检查下结论。

Linux 服务重启后通过 systemctl、proc limits 和 journal 日志复核文件描述符限制

启动用户和日志权限也要一起验收

服务的 User= 决定了它能打开哪些目录和日志文件。LimitNOFILE 配置正确,但启动用户无法访问日志目录,仍然会在启动后反复报错。发布检查至少要记录下面几项:

检查项命令通过标准
配置来源systemctl cat order-api.service能看到目标 drop-in
运行用户systemctl show ... -p User与目录权限设计一致
进程上限/proc/PID/limitsSoft/Hard 值符合预期
启动日志journalctl -u order-api.service -b无权限或句柄相关错误

查看本次启动日志时,优先使用:

journalctl -u order-api.service -b --no-pager -n 100
journalctl -u order-api.service -b -p warning..alert --no-pager

如果日志里出现 EMFILEPermission denied 或某个路径无法创建,先确认是描述符耗尽、目录权限还是应用自己的资源泄漏。不要把所有错误都归因于 LimitNOFILE。

生产环境的数值边界与回滚方法

建议先在一台实例上改动,压测或回放一段与生产接近的连接流量,观察句柄数量是否持续上涨。若句柄数接近上限,优先查连接关闭、文件句柄泄漏和日志轮转;提高上限只能延后故障。

回滚时删除 drop-in 中的两行配置,再重载并重启:

sudo systemctl revert order-api.service
sudo systemctl daemon-reload
sudo systemctl restart order-api.service
systemctl show order-api.service -p LimitNOFILE

如果 unit 里还有其他人工 drop-in,不要直接使用 revert 覆盖它们;先用 systemctl cat 确认文件来源,再删除本次创建的片段。回滚也要检查服务状态和最近一轮日志。

常见问题

改了 /etc/security/limits.conf,systemd 服务会跟着变吗?

不能直接假设会变。该文件主要影响登录会话,systemd unit 应使用自身的资源限制配置,并以进程实际值为准。

LimitNOFILE 设置成 unlimited 可以吗?

不建议把它当作默认修复。无限制会放大句柄泄漏和内核资源消耗,应该先按连接模型设定明确上限。

systemctl show 已经是 65535,为什么应用仍报 EMFILE?

可能是服务运行后确实耗尽了上限,也可能是应用或依赖进程没有正确关闭文件。继续检查当前 FD 数量、连接池和日志轮转,而不是只看 unit 属性。

重启后限制又恢复了怎么办?

systemctl cat 检查 drop-in 是否位于正确目录,再确认是否执行过 daemon-reload。如果配置由部署工具管理,还要检查下一次发布是否覆盖了该文件。

发布前的最小检查清单

systemctl catsystemctl show/proc/PID/limitsjournalctl -u ... -b 四类结果留档,再进行下一台实例的变更。这样即使问题出在应用资源泄漏,也能快速证明 systemd 的配置是否真正生效。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>