1. 项目概述:当服务器磁盘亮起红灯
干运维的兄弟,十有八九都见过这个让人心头一紧的场景:监控告警突然狂响,或者一个简单的df -h命令后,屏幕上赫然显示某个分区的使用率是醒目的 100%。尤其是在那些跑着 CentOS 7 的服务器上,这套经典、稳定但“年事已高”的系统,经过长时间运行,各种日志、缓存、临时文件、残留的安装包,就像房间角落里悄悄堆积的灰尘,不知不觉就把磁盘空间给“吃”光了。磁盘爆满绝非小事,它轻则导致应用无法写入新日志而报错,重则引发数据库崩溃、服务进程被系统 OOM Killer 直接“杀掉”,业务中断的后果谁都担不起。
所以,“服务器磁盘爆满如何清理”不是一个临时抱佛脚的问题,而是一个必须掌握的核心运维技能。这不仅仅是执行几个rm -rf命令那么简单,它要求我们像侦探一样,精准定位“元凶”,像外科医生一样,安全地切除“病灶”,同时还要像规划师一样,建立长效机制防止问题复发。本文将基于 CentOS 7 这一仍在广泛使用的环境,系统性地拆解磁盘清理的完整流程、各种工具的使用心法,以及那些只有踩过坑才知道的注意事项,让你下次面对红色警报时,能够从容不迫,手到病除。
2. 清理前的核心准备工作:诊断与备份
在动手删除任何文件之前,鲁莽的操作是最大的风险。我们的首要任务是搞清楚:空间到底被谁占用了?以及,万一删错了,我们有没有后悔药?
2.1 精准定位空间消耗源头
使用df -h命令可以快速查看所有磁盘分区的使用情况,锁定是/、/home还是/var等分区满了。但知道哪个分区满只是第一步,关键是要找到这个分区里哪些目录或文件是“大胃王”。
这里首推ncdu(NCurses Disk Usage) 工具。它比常用的du -sh *命令直观得多。首先安装它:
yum install -y ncdu然后,切换到疑似有问题的分区或目录,例如根目录:
cd / ncdu运行后,你会看到一个交互式界面,直接按文件/目录大小降序排列。你可以用方向键导航,按d键删除选中的项(非常谨慎!),或者按r重新扫描。它能让你快速、直观地定位到占用空间最大的“罪魁祸首”,比如是/var/log下的日志文件,还是/home/user下的某个大文件包。
如果服务器没有网络或你不想安装新软件,可以用组合命令来替代:
# 查找当前目录下最大的10个文件或目录 du -sh * | sort -rh | head -10 # 或者,在整个系统范围查找大于100M的文件(从根目录开始,耗时较长) find / -type f -size +100M -exec ls -lh {} \; 2>/dev/null | sort -k5 -hr | head -20注意:使用
find从根目录扫描时,会遇到大量“Permission denied”的报错,所以用2>/dev/null过滤了错误信息。同时,扫描整个文件系统可能非常耗时且消耗I/O,在业务高峰期需谨慎。
2.2 必须执行的备份与保护措施
在清理,尤其是清理系统目录(如/var、/usr)下的文件前,备份是生命线。
关键配置文件备份:至少备份你即将清理的目录中,任何你可能修改过的配置文件。例如,如果你要清理
/etc下的临时文件(虽然通常不大),或者计划清理日志但想保留日志配置。# 示例:备份重要的配置文件目录 tar -czf /tmp/backup_etc_$(date +%Y%m%d).tar.gz /etc 2>/dev/null # 将备份文件传到远程服务器或本地其他硬盘 # scp /tmp/backup_*.tar.gz user@backup-server:/path/数据库备份(如果服务器运行数据库):这是重中之重。磁盘满很可能影响数据库运行。在清理前,务必对 MySQL、PostgreSQL 等数据库进行完整备份。对于 MySQL/MariaDB:
mysqldump -u root -p --all-databases > /tmp/full_db_backup_$(date +%Y%m%d).sql确保这个备份文件被存放到另一个有足够空间的磁盘或远程。
设置“只读”保护(可选但推荐):对于生产环境,在诊断和制定清理方案期间,如果情况允许,可以将关键业务应用置于维护模式或停止写入,防止在空间不足的情况下继续写入导致更严重的数据损坏。这不是必须步骤,但体现了操作的严谨性。
3. 系统性清理策略与实操详解
定位问题后,我们需要分门别类地进行清理。盲目删除/tmp下的文件可能没事,但动/lib或/usr就可能让系统崩溃。下面按目录和文件类型,给出安全的清理策略。
3.1 清理临时文件与缓存
这是最安全、也最常见能释放空间的地方。
/tmp和/var/tmp目录:这两个目录本就是存放临时文件的。但注意,有些应用可能会把正在使用的临时文件放在这里。一个相对安全的做法是,删除超过一定天数的文件。# 删除 /tmp 下超过10天未访问的文件 find /tmp -type f -atime +10 -delete # 清理 /var/tmp 同理 find /var/tmp -type f -atime +10 -delete实操心得:使用
-delete参数要格外小心,最好先只用find /tmp -type f -atime +10列出文件确认无误。对于非常繁忙的生产系统,有些临时文件可能生命周期很短但很重要,建议将天数设置得保守一些(比如30天),或者安排在业务低峰期操作。YUM/DNF 缓存:CentOS 7 使用 YUM 包管理器,它会缓存下载的 RPM 包和元数据,长期积累会占用几个G甚至更多空间。
# 清理缓存的软件包 yum clean packages # 清理所有缓存(元数据、软件包等) yum clean all # 查看缓存占用空间 du -sh /var/cache/yum执行
yum clean all是安全的,下次执行yum install时会重新下载元数据。系统内存缓存(PageCache, Dentries and Inodes):Linux 会利用空闲内存缓存磁盘数据以提高性能。这部分内存在系统需要时会自动释放,但有时我们希望手动释放来观察磁盘空间变化(注意,这释放的是内存缓冲的磁盘数据,不直接增加可用磁盘空间,但能缓解因缓存占满导致I/O慢的问题)。可以通过修改
/proc/sys/vm/drop_caches来实现。# 释放 pagecache echo 1 > /proc/sys/vm/drop_caches # 释放 dentries 和 inodes echo 2 > /proc/sys/vm/drop_caches # 释放 pagecache, dentries 和 inodes echo 3 > /proc/sys/vm/drop_caches重要警告:在生产环境执行此操作需谨慎!这会导致缓存清空,紧接着的磁盘读取操作会变慢,可能引起短暂的服务性能波动。建议仅在诊断或测试时使用,并且优先考虑重启非关键服务来释放缓存,而非直接操作此参数。
3.2 管理日志文件
日志是磁盘空间的“头号杀手”,尤其是对于运行了Web服务器(如Nginx/Apache)、数据库和应用服务的系统。
日志轮转(Log Rotation):这是治本之策。CentOS 7 使用
logrotate服务来管理日志。检查/etc/logrotate.conf和/etc/logrotate.d/下的配置文件。确保你的应用日志被正确配置轮转。一个典型的 Nginx 日志轮转配置 (/etc/logrotate.d/nginx) 如下:/var/log/nginx/*.log { daily missingok rotate 52 compress delaycompress notifempty create 640 nginx adm sharedscripts postrotate if [ -f /var/run/nginx.pid ]; then kill -USR1 `cat /var/run/nginx.pid` fi endscript }这个配置表示:每天轮转,保留52个备份,压缩旧日志,轮转后通知 Nginx 重新打开日志文件。
手动清理历史日志:如果日志轮转未生效或积累太多,可以手动清理。
# 清理 /var/log 下所有 .gz 的压缩旧日志(通常是 logrotate 生成的) find /var/log -name "*.gz" -type f -mtime +30 -delete # 清理特定的大日志文件,例如 messages, secure 的旧备份 # 可以先查看大小 ls -lh /var/log/messages-* # 谨慎删除,比如保留最近7天的 find /var/log -name "messages-*" -mtime +7 -delete find /var/log -name "secure-*" -mtime +7 -delete绝对禁忌:不要直接
rm /var/log/messages或类似当前正在写入的日志文件。这不会释放磁盘空间,因为文件句柄还被进程持有。正确做法是清空文件内容:cat /dev/null > /var/log/some_big.log或者truncate -s 0 /var/log/some_big.log。但更好的方式是重启相关服务或使用logrotate强制轮转。应用特定日志清理:检查你的应用日志目录,比如
/var/log/nginx/,/var/log/httpd/,/var/log/mysql/, 以及自定义的应用日志路径。使用ncdu或du找到最大的文件进行处理。
3.3 清理未使用的内核与软件包
系统升级后,旧内核会保留,以防新内核启动失败。但保留太多旧内核会占用/boot分区空间(如果/boot是独立分区的话)。
查看已安装内核:
rpm -qa | grep kernel你会看到类似
kernel-3.10.0-1160.el7.x86_64的列表。删除旧内核:务必确保当前运行的内核不要删除!使用
uname -r查看当前运行内核版本。# 删除除当前运行内核外的所有旧内核 yum remove $(rpm -qa | grep kernel | grep -v $(uname -r))或者使用
package-cleanup工具(来自yum-utils)更安全:yum install -y yum-utils package-cleanup --oldkernels --count=2这个命令会保留最新的2个内核(包括当前运行的),删除更旧的。
清理孤儿包和残留:有时卸载软件会有残留。
# 清理无用的依赖包 yum autoremove # 使用 yum-utils 的 package-cleanup 检查问题 package-cleanup --problems package-cleanup --leaves
3.4 查找并处理特定的大文件与垃圾文件
除了系统目录,用户目录、应用目录也可能藏有“巨无霸”。
核心转储文件(Core Dumps):应用程序崩溃时可能会生成 core 文件,体积巨大(几个G甚至更大)。它们通常位于应用程序的工作目录或根目录下,文件名通常包含
core或形如core.1234。# 在全盘查找 core 文件 find / -name "core" -o -name "core.*" -type f 2>/dev/null | xargs ls -lh找到后,确认其不再需要用于调试后,可以删除。同时,应该调查为何会产生 core dump,从根源上解决问题。可以通过
ulimit -c查看 core 文件大小限制,设置为0可以禁止生成(但不推荐,不利于调试)。Docker/容器相关垃圾:如果服务器上运行了 Docker,它的镜像、容器、卷和网络缓存会占用大量空间。
# 查看 Docker 磁盘使用情况 docker system df # 删除所有未被使用的镜像、容器、卷和网络(谨慎!确保没有需要的数据) docker system prune -a # 更精确的清理:删除所有停止的容器 docker container prune # 删除所有未被任何容器引用的镜像 docker image prune -a # 删除未被使用的卷(特别小心,卷里可能有持久化数据!) docker volume prune踩坑警告:
docker system prune -a和docker image prune -a会删除所有未被容器使用的镜像,包括那些有标签但未运行的。务必确认这些镜像可以重新从仓库拉取,或者你已备份。docker volume prune是最高风险操作,一旦删除,卷内数据永久丢失,执行前必须百分百确认卷内无重要数据。其他应用缓存:例如,如果你安装了 Jenkins,其工作空间 (
/var/lib/jenkins/workspace) 可能很大;如果运行了 CI/CD 工具,检查其构建产物和缓存目录。
4. 高级诊断与根因分析工具
当常规清理后空间释放不明显,或者空间被神秘占用时(du和df显示不一致),需要更高级的工具。
4.1 处理“文件已删除但空间未释放”问题
这是经典问题。当一个文件被进程打开时,即使你用rm删除了它,只要进程不关闭文件句柄,磁盘空间就不会被释放。df显示空间仍被占用,但du找不到这个文件。
使用
lsof命令查找被删除但未释放的文件:lsof | grep deleted这条命令会列出所有已被删除(状态为
deleted)但仍有进程打开的文件。输出会显示进程 PID 和文件大小。解决方案:
- 重启持有该文件句柄的进程:这是最直接的方法。找到对应的 PID,然后
kill -9 PID或优雅地重启相关服务。重启后,空间立即释放。 - 清空文件:如果无法重启进程(比如是数据库的重要进程),可以尝试通过进程的文件描述符来清空文件。首先通过
lsof找到文件的描述符编号(比如/proc/1234/fd/15),然后执行cat /dev/null > /proc/1234/fd/15。此操作风险极高,可能导致数据丢失或进程异常,非万不得已不要使用。
- 重启持有该文件句柄的进程:这是最直接的方法。找到对应的 PID,然后
4.2 使用ncdu进行交互式深度分析
前面提到过ncdu,这里再强调其高级用法。在ncdu界面中:
- 按
n键:按文件名排序。 - 按
s键:按文件大小排序(默认)。 - 按
C键:按项目数排序。 - 按
r键:重新扫描当前目录。 - 按
g键:用图形条显示大小比例。 - 进入一个目录后,可以清晰地看到子目录的占比,非常适合层层深入定位问题。
4.3 检查磁盘 Inode 是否耗尽
有时候,df -h显示磁盘空间还有剩余,但系统却报“No space left on device”。这很可能是 Inode 用尽了。Inode 存储文件的元信息,大量的小文件(比如邮件、缓存的小图片)会快速消耗 Inode。
# 查看 Inode 使用情况 df -i如果IUse%达到或接近 100%,就需要清理文件来释放 Inode。清理策略和清理大文件类似,但重点应放在包含海量小文件的目录上,例如会话文件目录 (/tmp/sess_*)、某些应用的缓存目录等。使用find命令可以统计和删除大量小文件:
# 查找某个目录下文件数量最多的子目录(帮助定位) find /path/to/dir -type f | cut -d/ -f2 | sort | uniq -c | sort -rn | head -20 # 删除某个目录下超过一定天数的小文件 find /path/to/dir -type f -mtime +30 -delete5. 建立长效预防与监控机制
清理是“救火”,建立监控和预防措施才是“防火”。
配置日志轮转与日志级别:确保所有关键应用(Nginx, Apache, MySQL, 自定义应用)都配置了合理的
logrotate策略,根据磁盘空间和保留需求设置rotate count(保留份数)和size/daily等触发条件。对于调试日志,在生产环境应关闭或设置为高级别(如 WARN、ERROR),避免生成过多 INFO/DEBUG 日志。设置磁盘空间监控告警:这是运维的基本功。使用 Zabbix, Prometheus + Alertmanager, CloudWatch(如果是云服务器)等监控工具,对磁盘使用率设置告警阈值(例如 >80% 警告, >90% 严重)。这样可以在问题发生前就得到通知。
定期清理任务(Cron Job):将一些安全的清理操作写成脚本,加入定时任务。例如,每周清理一次
/tmp下超过7天的文件,每月清理一次旧的 YUM 缓存和日志备份。# 编辑 root 用户的 crontab crontab -e # 添加以下行,每周日凌晨3点清理 /tmp 0 3 * * 0 find /tmp -type f -atime +7 -delete # 每月1号凌晨4点清理旧日志包 0 4 1 * * find /var/log -name "*.gz" -mtime +90 -delete注意事项:自动化清理脚本一定要先在测试环境充分验证,并且删除操作最好先
echo或log要删除的文件列表,运行一段时间确认无误后,再换成真正的-delete或rm命令。合理规划分区:在新装系统时,就应合理规划分区。将
/home、/var、/tmp等容易增长的目录单独分区,甚至可以给/var/log单独分区。这样,即使日志爆满,也只会影响/var分区,不会导致根目录/挂掉从而让整个系统崩溃。对于/tmp,可以考虑在挂载时使用tmpfs(内存文件系统),但要注意内存大小。使用存储分析工具定期巡检:可以定期(如每月)运行
ncdu或编写脚本,生成磁盘使用报告,发送给运维人员,以便提前发现异常增长的趋势。
磁盘空间管理是服务器运维中一项看似基础却极其重要的工作。它考验的不仅是命令的熟悉程度,更是系统性思维和风险控制能力。每一次清理操作,尤其是生产环境,都要遵循“先查后删、先备后动、先小后大”的原则。通过将本文介绍的方法论和工具融入日常运维流程,你不仅能快速解决眼前的磁盘危机,更能构建一个健壮、可预测的服务器存储环境,让“磁盘爆满”的告警从此变得罕见。