Linux 资源耗尽故障排查:CPU、内存与磁盘空间

使用实用命令、更安全的清理步骤和根本原因检查,排查 Linux CPU、内存和磁盘耗尽问题。

Linux 资源耗尽故障排查:CPU、内存与磁盘空间

当 Linux 服务器耗尽 CPU、内存或磁盘空间时,最初的症状通常很模糊:网站变慢、SSH 登录后挂起、部署失败或服务不断重启。快速应对事件的方法是确定哪个资源已耗尽,然后找出背后的进程或文件系统。

先执行风险最低的检查。只读命令如 topfreedfduvmstatjournalctl 能让你了解情况而不改变机器。杀死进程和删除文件可能是必要的,但它们不是诊断手段。

识别罪魁祸首:监控系统资源

在解决资源耗尽问题之前,你需要确定哪个资源被过度使用以及哪个进程负责。Linux 为此提供了丰富的命令行工具。

CPU 使用率监控

高 CPU 使用率会使系统感觉缓慢且无响应。通常由失控进程、高需求应用或低效脚本引起。

  • top:这是一个不可或缺的实时系统监控器。它显示动态的进程列表,默认按 CPU 使用率排序。你可以查看整体 CPU 利用率、内存使用率和单个进程详情。

    top
    

    top 中,按 1 查看单个 CPU 核心使用率。按 P 按 CPU 使用率排序。查找持续消耗高百分比 CPU 的进程。

  • htop:增强的交互式 top 版本。因其用户友好、彩色输出和更易导航而常被优先使用。

    htop
    

    top 类似,htop 允许按 CPU 使用率排序并提供详细的进程信息。

  • mpstat:属于 sysstat 包,提供详细的 CPU 统计信息,包括每个处理器的使用率、中断计数和上下文切换。

    mpstat -P ALL 1
    

    此命令每秒显示所有核心的 CPU 统计信息。

同时检查平均负载与 CPU 数量的对比:

uptime
nproc

平均负载为 8 在 2 核虚拟机上与在 32 核主机上意义完全不同。负载还包括等待不可中断 I/O 的任务,因此高平均负载但低 CPU 使用率可能指向磁盘或网络存储问题。

内存使用率监控

当系统耗尽可用 RAM 和交换空间时,它会开始使用磁盘空间作为虚拟内存,这显著更慢,导致严重的性能下降。

  • free -h:显示系统中空闲和已用的物理内存与交换内存总量,以及内核使用的缓冲区和缓存。-h 标志使输出更易读(例如 MB、GB)。

    free -h
    

    注意 available 内存和已用的交换空间。高交换使用率表示 RAM 不足。

  • top / htop:两者都显示每个进程的内存使用率。查找 %MEM 值高的进程。

  • vmstat:报告虚拟内存统计信息。可显示进程、内存、分页、块 IO、陷阱和 CPU 活动信息。

    vmstat 5
    

    此命令每 5 秒报告统计信息。查看 si(换入)和 so(换出)列;高值表示严重的内存交换。

对于可能的 OOM 杀死,检查内核日志:

dmesg -T | grep -i 'killed process'
journalctl -k --since "1 hour ago" | grep -i oom

OOM 杀死会改变事件性质。立即需要确定哪个进程被杀死、为何超出可用内存,以及 systemd 或编排器是否重启了它。

磁盘空间监控

满的磁盘分区会阻止应用程序写入数据、导致错误,甚至阻止系统启动。

  • df -h:报告文件系统磁盘空间使用情况。-h 标志使输出更易读。

    df -h
    

    此命令列出所有已挂载的文件系统,显示其总大小、已用空间、可用空间和挂载点。查找使用率接近或达到 100% 的分区。

  • du -sh <directory>:估算指定目录的文件空间使用情况。-s 标志汇总,-h 使其更易读。

    du -sh /var/log/*
    

    使用此命令查找哪些子目录消耗了最多的磁盘空间。

同时检查 inode 使用情况:

df -ih

文件系统可能有空闲的 GB 空间,但如果 inode 耗尽,则无法创建文件。这种情况发生在数百万个小文件时:缓存条目、邮件队列、会话文件、构建工件或轮转不当的日志。

解决资源耗尽问题

一旦确定了有问题的资源和违规进程,你可以采取措施解决问题。

处理高 CPU 使用率

  1. 识别进程:使用 tophtop 查找消耗高 CPU 的进程 ID (PID)。
  2. 调查进程:确定该进程是什么。是用户应用程序、系统服务还是意外的东西?
    • 合法高使用率:如果合法应用程序使用大量 CPU(例如编译软件、视频编码),你可能需要等待其完成、安排在非高峰时段或升级硬件。
    • 失控进程:如果进程陷入循环或无意中消耗过多 CPU,你可以尝试重启它。如果不起作用,你可能需要终止它。
  3. 终止进程(谨慎使用!):你可以使用 kill 命令向进程发送信号。最常见的信号是:
    • SIGTERM (15):优雅地请求进程终止。
    • SIGKILL (9):立即强制终止进程。这应是最后的手段,因为它不允许进程进行清理。
    # 优雅地终止 PID 为 1234 的进程
    kill 1234
    
    # 强制终止 PID 为 1234 的进程
    kill -9 1234
    
  4. 检查日志:检查系统日志(例如 /var/log/syslog/var/log/messages、应用程序特定日志)中与有问题的进程相关的错误。
  5. 优化应用程序/脚本:如果高 CPU 使用率是由于低效的应用程序或脚本引起的,考虑优化代码或配置。

高 CPU 并不总是坏事。短时间内使用所有核心的批处理作业可能没问题。单线程进程在一个核心上持续 100% 而请求在其后排队则不同。查看持续时间、用户影响以及进程是否预期繁忙。

如果在重启服务前需要更多上下文,捕获快照:

ps -fp <pid>
sudo lsof -p <pid> | head
sudo strace -p <pid> -tt -T -f

在生产系统上谨慎使用 strace。它可能增加开销,但短样本通常能告诉你进程是否在循环、等待文件、网络调用失败或重复打开同一资源。

解决内存泄漏和耗尽

内存泄漏发生在程序未能释放不再需要的内存时,逐渐消耗所有可用 RAM。这可能导致过度交换和系统无响应。

  1. 识别进程:使用 tophtop 查找内存 (%MEM) 或常驻集大小 (RSS) 值随时间稳定增长的进程。
  2. 调查进程:确定应用程序的性质。是已知有潜在内存问题的应用程序,还是自定义的?
  3. 重启应用程序/服务:通常,只需重启应用程序或服务即可通过释放累积的内存临时解决内存泄漏。
    # 示例:重启 Apache Web 服务器
    sudo systemctl restart apache2
    
  4. 检查应用程序特定监控:许多应用程序(例如 Web 服务器、数据库)有自己的监控工具或日志,可帮助诊断内存问题。
  5. 分析核心转储:对于关键应用程序,你可能需要启用核心转储并使用调试工具(如 gdb)分析泄漏发生时的内存状态。这是高级故障排查步骤。
  6. 增加交换空间(临时措施):如果无法立即解决泄漏,可以增加交换空间以提供更多虚拟内存。但这是权宜之计,而非解决方案。
  7. 硬件升级:如果系统持续因工作负载而耗尽内存,你可能需要添加更多物理 RAM。

更好的内存调查是观察随时间的变化。一次 top 截图只能说明当前谁占用大。泄漏是一个趋势。

while true; do
  date
  ps -eo pid,comm,rss,%mem --sort=-rss | head -15
  sleep 60
done

如果同一进程在样本中稳定增长,且在流量下降后不回落,则泄漏信号更强。如果多个进程在高峰流量期间一起增长,则工作负载可能只是超出了容量或并发限制。

对于 systemd 服务,检查是否已存在内存限制:

systemctl show <service> -p MemoryCurrent -p MemoryMax

对于容器,主机级别的 free -h 可能看起来正常,而容器已达到其自身限制。检查 docker statskubectl top pod 或编排器事件中的 OOM 杀死。

管理满的磁盘分区

当磁盘分区填满时,可能导致各种系统故障。通常需要立即采取行动。

  1. 识别满的分区:使用 df -h 定位使用率达到 100% 的分区。
  2. 查找大文件/目录:使用 du -shdu -h --max-depth=1 <directory> 向下导航目录树,查找消耗空间的内容。
    # 查找根分区中最大的目录
    sudo du -h --max-depth=1 / | sort -rh
    
    常见罪魁祸首包括日志文件 (/var/log)、临时文件 (/tmp)、包缓存和用户数据。
  3. 清理日志文件:日志文件可能变得非常大。你可以安全地删除旧日志,或配置日志轮转 (logrotate) 自动管理其大小。
    • 删除旧日志:谨慎操作,确保不删除当前活动的日志。你可以使用 find 删除超过特定天数的文件。
      # 删除 /var/log/myapp 中超过 30 天的 .log 文件
      sudo find /var/log/myapp -name "*.log" -type f -mtime +30 -delete
      
    • 日志轮转:确保为所有生成日志的服务正确配置了 logrotate。它通常每天运行,处理归档和删除旧日志。
  4. 清除包管理器缓存:包管理器通常保留下载的包文件。清除这些可以释放大量空间。
    • Debian/Ubuntu (apt)
      sudo apt autoremove
      sudo apt clean
      
    • CentOS/RHEL/Fedora (yum/dnf)
      sudo yum autoremove  # 或 dnf autoremove
      sudo yum clean all   # 或 dnf clean all
      
  5. 移除未使用的包:卸载不再需要的软件。
    • Debian/Ubuntusudo apt remove <package_name>
    • CentOS/RHEL/Fedorasudo yum remove <package_name>sudo dnf remove <package_name>
  6. 检查临时目录/tmp 中的文件通常可以安全删除,尤其是在重启后,但如果应用程序正在使用它们,请小心。
  7. 清空回收站:如果你使用桌面环境,检查用户回收站。
  8. 考虑调整分区大小:如果空间问题持续存在且清理不足,你可能需要调整分区大小或添加更多存储。这是更高级的操作,可能需要卸载分区或从 live 环境启动。

小心仍被打开但已删除的文件。即使你删除了大日志文件,df 可能仍显示文件系统已满,因为运行中的进程仍持有该文件句柄。

sudo lsof +L1

如果已删除的文件仍被打开,重启或重新加载拥有该文件的服务会释放空间。有意为之;不要在事件中途重启数据库或关键服务而不了解影响。

对于 journal 日志,优先使用 journalctl 清理而不是手动删除文件:

journalctl --disk-usage
sudo journalctl --vacuum-time=14d

对于 Docker 主机,检查容器日志和未使用的镜像:

docker system df
docker ps --size

不要在生产主机上盲目运行广泛的 prune 命令。它们可能移除镜像、构建缓存、已停止的容器和网络,而这些可能是某人期望保留的。

压力下有效的分类顺序

当一切缓慢时,使用固定顺序,以免在不同理论之间跳跃。

  1. 确认主机可达且非只读:

    uptime
    date
    mount | grep ' ro,'
    
  2. 检查 CPU 和负载:

    top
    uptime
    
  3. 检查内存和交换:

    free -h
    vmstat 1 5
    
  4. 检查磁盘空间和 inode:

    df -h
    df -ih
    
  5. 检查最近的内核和服务错误:

    journalctl -p warning..alert --since "30 minutes ago"
    journalctl -k --since "30 minutes ago"
    

此顺序快速捕获常见故障:CPU 饱和、交换风暴、文件系统满、inode 耗尽、OOM 杀死和存储错误。

选择最不坏的即时修复

在中断期间,你可能需要在永久修复准备好之前进行短期修复。

对于 CPU 耗尽,优雅的服务重启可能比 kill -9 更安全,尤其是对于写入状态的软件。如果一个后台作业正在饿死用户流量,降低其优先级:

sudo renice +10 -p <pid>
sudo ionice -c2 -n7 -p <pid>

对于内存耗尽,减少并发通常比添加交换并希望其有效更安全。降低 Web 工作进程数、暂停批处理作业或临时禁用昂贵功能。交换可以争取时间,但大量交换通常将明确的故障转变为缓慢的故障。

对于磁盘耗尽,删除或轮转你了解的文件。好的候选是旧的压缩日志、包缓存、过时的构建工件和已停止作业的临时文件。坏的候选是数据库文件、活动日志、应用程序数据目录下的未知文件以及任何你无法解释的东西。

要记录的根本原因笔记

系统稳定后,记录发生了什么变化。有用的笔记是具体的:

  • 耗尽的特定文件系统或资源。
  • 涉及的进程、用户、服务、容器或 cron 作业。
  • 证明问题的命令输出。
  • 采取的即时行动。
  • 需要的永久修复。

这不是为了文书工作本身。当你知道 /var 因部署后调试日志增长而填满,或内存压力在工作进程数翻倍时开始,下一次事件会容易得多。

预防的最佳实践

  • 定期监控:使用 tophtopfreedf 和专用监控解决方案(例如 Nagios、Zabbix、Prometheus)定期监控 CPU、内存和磁盘空间。
  • 自动化日志轮转:确保为所有生成日志的服务正确配置 logrotate
  • 调整应用程序配置:优化应用程序设置以提高资源效率。例如,调整 Web 服务器工作进程数、数据库连接池等。
  • 设置警报:配置针对持续高使用率、快速增长、OOM 杀死、文件系统满、inode 耗尽和服务重启的警报。对趋势发出警报,而不仅仅是硬限制。
  • 系统更新:保持系统和应用程序更新,因为新版本通常包含性能改进和错误修复。
  • 资源限制:对于多用户系统或容器化环境,考虑设置资源限制(例如使用 ulimit 或 cgroups),以防止单个进程饿死其他进程。

资源耗尽故障排查主要是纪律性的缩小范围。找到受限资源,识别所有者,做出最小的稳定更改,然后修复发生的原因。如果你按此顺序使用基本工具并抵制在了解之前删除或杀死的冲动,大多数事件都能处理。