1. 从一次“磁盘已满”的告警说起
那天下午,我正在调试一个后台服务,突然收到监控系统的告警邮件:“服务器磁盘使用率超过95%”。登录到那台跑着CentOS 7的机器上,第一反应就是执行df -h看一眼。果不其然,根分区/已经飘红了。这几乎是每个运维和开发都会遇到的经典场景——磁盘空间告急。但问题来了,df命令显示使用了90G,可我凭经验感觉系统文件和业务日志加起来不应该有这么大。于是,我又熟练地敲下du -sh /想看看具体是哪个目录在“吃”空间,结果命令卡住了,半天没反应。这就是CentOS 7(乃至整个Linux系统)磁盘空间管理的第一个“坑”:df和du统计口径不同,以及在大目录下du可能极其缓慢。
“查看磁盘空间”这个操作,远不止一个df -h那么简单。它背后涉及文件系统原理、挂载点、已删除未释放的文件、稀疏文件、以及各种“空间黑洞”。对于CentOS 7这样一个依然在生产环境中广泛使用的稳定系统,掌握一套完整的磁盘空间分析与排查方法论,是保障系统稳定性的基本功。本文将从一个老运维的角度,不仅告诉你用什么命令,更深入解释为什么会有这些现象,以及当常规命令失效时,你该如何像侦探一样,一层层剥开迷雾,找到吞噬磁盘空间的“真凶”。
2. 基础命令:df与du的兄弟之争
几乎所有教程都会从这两个命令开始。它们是最直接的武器,但理解它们的差异,是避免误判的关键。
2.1df:文件系统层面的空间报告
df命令报告的是文件系统的磁盘空间使用情况,它的数据来源于文件系统的超级块。你可以把它理解为物业的“总电表”,它只看整个大楼用了多少电,不管每个房间具体怎么用的。
最常用的命令是df -h,-h参数代表“人类可读”,用G、M、K来显示容量,比直接看字节数友好得多。
df -h输出通常如下:
文件系统 容量 已用 可用 已用% 挂载点 /dev/vda1 50G 45G 2.8G 95% / devtmpfs 3.9G 0 3.9G 0% /dev tmpfs 3.9G 0 3.9G 0% /dev/shm tmpfs 3.9G 8.5M 3.9G 1% /run tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup /dev/vdb1 200G 30G 161G 16% /data这里有几个关键信息:
/dev/vda1:这是根分区所在的物理设备。45G已用,仅剩2.8G,使用率95%,告警由此而来。/dev/vdb1:这是额外挂载的数据盘,空间充足。tmpfs系列:这些是基于内存的临时文件系统,重启后数据会消失。它们占用的是内存而非磁盘空间,所以即使显示已用,也不必担心磁盘被占。
为什么df显示的空间使用率增长很快,但好像没存那么多文件?一个常见原因是文件被删除,但进程仍持有打开状态。假设一个服务(比如Tomcat、Nginx)在持续写一个巨大的日志文件app.log。你直接用rm app.log删除了它。在文件系统层面,这个文件的索引(inode)被标记为删除。但是,如果写入这个文件的进程没有重启,它仍然持有该文件的句柄,操作系统会认为文件仍“存在”,直到所有持有它的进程都关闭句柄。因此,df看到的已用空间不会释放,而du扫描目录时却找不到这个文件。此时,lsof | grep deleted命令可以帮你找到这些“幽灵文件”和持有它们的进程。
2.2du:目录层级的空间计算
du命令则是通过递归统计目录下所有文件的大小来计算的。它像是挨家挨户查电表的抄表员,把每个房间的用电量加起来。
常用命令是du -sh /path/to/directory,-s是汇总,-h是人类可读。
# 查看根目录总大小 du -sh / # 查看 /var/log 目录大小 du -sh /var/log # 找出当前目录下最大的10个文件或目录 du -ah /path/to/dir | sort -rh | head -n 10du和df结果对不上的核心原因:
- 已删除但未释放的文件:如上所述,这是最常见原因。
du找不到这些文件,df却还记着它们。 - 文件系统预留空间:Ext4/XFS等文件系统默认会保留约5%的空间给root用户,以防普通用户写满磁盘导致系统无法运行。这部分空间
df会计入“已用”,但du不会。 - 稀疏文件:有些文件(如虚拟机磁盘镜像、数据库文件)是稀疏文件。它们看起来很大,但实际占用的物理块可能很小。
du默认报告实际占用的块(--apparent-size参数可以看逻辑大小),而df反映的是实际占用的物理空间。例如,用dd命令创建一个1G的稀疏文件:dd if=/dev/zero of=sparse_file bs=1 count=0 seek=1G。ls -lh显示1G,du -h显示可能只有几K,df看到的空间占用也是几K。 - 文件系统元数据:
df统计的空间包括了inode表、日志等元数据占用的空间,而du只统计文件数据。
实操心得:当磁盘告警时,首先对比
df -h和du -sh /的结果。如果df的已用空间远大于du统计的根目录空间,那么极有可能存在“已删除未释放”的大文件。此时,重启相关进程(如日志服务、应用服务)是最快的释放空间方法。
3. 进阶排查:当du也束手无策时
有时候,du -sh /命令会运行得非常慢,甚至像开头那样卡住。这通常是因为目录结构极深、文件数量极多,或者有挂载了NFS等网络文件系统。这时,我们需要更精准的工具和策略。
3.1 使用ncdu:交互式磁盘使用分析器
ncdu是一个基于文本的交互式磁盘分析工具,比du更直观、更快。它先扫描目录,然后提供一个可以导航的界面,让你快速定位大目录。
# 安装 ncdu (CentOS 7 EPEL源) yum install epel-release -y yum install ncdu -y # 扫描根目录 ncdu /进入界面后,你可以用方向键导航,按d删除文件(谨慎!),它能清晰展示每个子目录的空间占比,效率远超du配合sort。
3.2 定位最大文件的N种方法
当需要快速找到“罪魁祸首”时,这些命令组合是利器:
方法一:查找大于100M的文件(从根开始,可能较慢)
find / -type f -size +100M -exec ls -lh {} \; 2>/dev/null | head -202>/dev/null是为了忽略权限拒绝产生的错误信息。
方法二:聚焦常见“肥胖”目录系统中有几个目录是空间消耗的“重灾区”,应优先检查:
/var/log: 系统及应用日志。日志轮转配置不当会导致其无限膨胀。/var/lib/docker: Docker的存储目录,包含镜像、容器数据。/tmp: 临时文件。有些程序异常退出会留下大文件。/home: 用户家目录。/opt: 第三方软件安装目录。
方法三:使用ls按时间排序,找近期的大文件
# 在可疑目录下,按文件大小降序排列 ls -lhS /var/log/ # 按修改时间降序排列,找最近被修改的大文件 ls -lht /var/log/3.3 处理“已删除未释放”的文件
这是导致空间“神秘消失”的元凶。诊断步骤如下:
- 确认问题:执行
df -h和du -sh /,确认存在显著差异。 - 查找被删除但仍被进程打开的文件:
输出会显示进程ID(PID)、命令和文件描述符。你会看到类似这样的行:lsof | grep deleted
这表示PID为12345的Java进程,仍然持有一个已删除的、约1GB大小的日志文件。java 12345 user 1w REG 8,1 1048576000 1234 /path/to/app.log (deleted) - 释放空间:
- 优雅方式:重启持有该文件的进程(如
systemctl restart application.service)。 - 强制方式:如果进程不能重启,可以清空该文件描述符(极度危险,可能导致程序异常):
echo "" > /proc/12345/fd/1。更安全的方法是向进程发送信号,让其重新打开日志文件(如kill -USR1 12345,前提是程序支持)。 - 根本解决:配置应用的日志轮转(如使用
logrotate),避免单个日志文件无限增长。
- 优雅方式:重启持有该文件的进程(如
4. 空间清理实战与扩容考量
找到问题后,清理是门技术活,乱删可能直接导致系统崩溃或服务异常。
4.1 安全清理指南
1. 日志文件清理:
- 使用
logrotate:这是管理日志的首选工具。检查/etc/logrotate.conf和/etc/logrotate.d/下的配置,确保日志能按时间或大小自动轮转、压缩和删除。 - 手动清理旧日志:对于非关键日志,可以安全删除。
# 清理7天前的日志文件 find /var/log -name "*.log" -mtime +7 -exec rm -f {} \; # 清空当前日志(注意:有些服务需要重启或发信号才能继续写入) > /var/log/some_large.log
2. 包管理缓存清理:YUM的缓存可能占用不少空间。
# 清理所有已安装软件包的缓存 yum clean all # 或者只清理过期缓存 yum clean packages3. 系统临时文件:CentOS 7 引入了systemd-tmpfiles来管理临时文件,但/tmp目录下仍可能有残留。
# 查看 /tmp 目录大小 du -sh /tmp # 重启后,/tmp目录下非系统创建的文件会被清除(取决于 /etc/tmpfiles.d/ 配置)4. 容器与虚拟机镜像:如果是Docker环境,/var/lib/docker是重点。
# 查看Docker磁盘使用 docker system df # 清理无用的镜像、容器、卷和构建缓存 docker system prune -a踩坑警告:绝对不要直接删除
/var/lib、/usr、/bin、/sbin等系统核心目录下的未知文件。特别是/lib、/lib64里的库文件,删除一个就可能让系统命令全部瘫痪。清理前务必确认文件用途。
4.2 扩容:最后的解决方案
当清理也无法满足需求时,扩容就是必选项。从热搜词“centos扩容”、“vmware ubuntu扩展磁盘空间”、“vsphere安装配置centos设置远程访问”可以看出,这是虚拟化环境下的高频需求。
物理机扩容相对复杂,涉及硬盘插槽、RAID配置等,这里不展开。
虚拟机扩容(以VMware/VirtualBox为例)通用流程:
在虚拟化管理界面扩容虚拟磁盘:关闭虚拟机,在VMware或VirtualBox设置中,将虚拟硬盘大小从例如50G增加到100G。此操作仅改变了“容器”的大小,操作系统内部还感知不到。
在CentOS 7内部扩展分区和文件系统:
- 对于使用LVM的情况(推荐,也是最常见的):
# 查看物理卷、卷组、逻辑卷 pvdisplay vgdisplay lvdisplay # 假设新空间在 /dev/sda 上,需要先创建新分区(如 /dev/sda3)并类型设置为8e (Linux LVM) # 使用 fdisk 或 parted 操作,此处略。 # 将新分区创建为物理卷 pvcreate /dev/sda3 # 将物理卷扩展到现有卷组(假设卷组名为 centos) vgextend centos /dev/sda3 # 扩展逻辑卷(假设要扩展根逻辑卷 /dev/centos/root) lvextend -l +100%FREE /dev/centos/root # 最后,调整文件系统大小(对于xfs和ext4不同) # 如果是xfs文件系统(CentOS 7默认): xfs_growfs / # 如果是ext4文件系统: resize2fs /dev/centos/root - 对于非LVM的普通分区:这非常棘手,通常需要借助第三方Live CD工具(如GParted)来移动和调整分区,风险极高,强烈建议在操作前备份所有数据。
热搜词中提到的“no volume groups found.”错误,就是在执行
vgextend时,系统找不到卷组。这通常是因为磁盘扩容后,新增的空间没有创建为物理卷,或者虚拟机配置的磁盘控制器模式(如SCSI/SATA)与系统识别的不符,导致磁盘设备名变化。解决方法是先用lsblk确认新增的磁盘设备名(如/dev/sdb),然后使用pvcreate /dev/sdb创建物理卷,再将其加入卷组。- 对于使用LVM的情况(推荐,也是最常见的):
5. 防患于未然:监控与日常维护策略
被动响应告警总是狼狈的。一个成熟的系统管理员,应该建立主动的磁盘空间监控和维护体系。
1. 配置监控告警:使用像Zabbix、Prometheus+Grafana这样的监控系统,对关键分区的使用率设置告警阈值(例如>80%警告,>90%严重)。这是第一时间发现问题的手段。
2. 实施日志管理策略:
- 为所有自研应用配置日志轮转,写入
/etc/logrotate.d/。 - 对于像Nginx、Docker这类常用服务,确保其默认的logrotate配置已启用并符合预期。
- 考虑将日志中心化收集到Elasticsearch等日志平台,本地只保留短期日志。
3. 定期清理任务(Crontab):将一些安全的清理任务写入定时任务。
# 编辑root用户的crontab crontab -e # 每周日凌晨3点清理YUM缓存和临时文件 0 3 * * 0 yum clean all >/dev/null 2>&1 0 3 * * 0 find /tmp -type f -atime +7 -delete >/dev/null 2>&14. 选择合理的初始分区方案:在新系统安装时(对应热搜词“centos安装磁盘分区教程”),就应做好规划:
- 使用LVM:这是最重要的建议。LVM提供了无与伦比的灵活性,后续扩容几乎零停机。
- 分离关键目录:将
/home、/var、/opt甚至/var/log单独分区。这样即使日志爆满,也不会影响根分区的系统运行。 - 预留足够空间:根据业务性质预估增长,为根分区和关键数据分区预留充足的余量。
磁盘空间管理,看似是简单的命令操作,实则是系统理解深度和运维经验的体现。从df和du的差异,到lsof追踪幽灵文件,再到LVM的动态扩容,每一步都需要知其然并知其所以然。下次再遇到磁盘空间报警,希望你能像侦探一样,从容地拿起这些工具,精准地找到问题根源,而不仅仅是机械地执行删除命令。毕竟,在服务器上,每一点空间都关乎着业务的稳定。