跳到主内容
快讯直播
AI智模界
教程

服务器磁盘满了怎么定位与清理

这篇做完你能得到什么

你会拿到一套可复用的排查流程:先判断是"容量满"还是"inode 满",再从根目录一路收缩到具体目录和文件,最后用安全的方式清理日志、Docker 占用和僵尸文件。按顺序执行完,你能回答三个问题:谁把盘吃满了、能不能删、删完为什么空间没回来。

前置条件

  • 一台 Linux 服务器,具备 sudo 或 root 权限
  • 常用命令可用:dfdufindsorttruncate(多数发行版自带)
  • 建议装两个可选工具:ncdu(交互式目录浏览)、lsof(查看被进程占用的文件)
  • 心里有数:清理是有风险的操作,涉及数据库文件、审计日志、容器数据卷时先确认再动手

第一步:先分清是空间满还是 inode 满

很多人上来就删大文件,但如果盘满是因为 inode 耗尽(文件数量太多,但每个文件都很小),删大文件一点用都没有。

```bash

看容量

df -hT

看 inode

df -i

```

阅读方式:

  • df -hT 关注 Use% 这一列,找到接近 100% 的挂载点,记下它的挂载路径(比如 //var/data)和文件系统类型。
  • df -i 关注 IUSE%,如果它接近 100% 而 df -h 还有空间,那就是 inode 问题,直接跳到第七步。
  • 两个都满,先按容量处理,再处理 inode。

注意:df 显示的是挂载点,不是目录。比如 /var 单独挂了一块盘,那你该排查的是 /var,而不是整个 /

第二步:从根目录往下收缩,找到大目录

假设满的是 /。逐层下钻,每层只扫当前文件系统(-x 参数),避免翻进其他挂载盘拖慢速度,也用 nice/ionice 降低对线上服务的影响。

```bash

第一层:根目录下各目录占多大

sudo nice -n 19 ionice -c3 du -xhd1 / 2>/dev/null | sort -h

```

看到 /var 特别大,就继续下钻:

```bash

sudo du -xhd1 /var 2>/dev/null | sort -h

sudo du -xhd1 /var/log 2>/dev/null | sort -h

```

du -x 表示不跨文件系统,-d1 表示只统计一层,-h 是人类可读单位。sort -h 按体积排序,最大的排在最后。

如果装了 ncdu,可以直接交互式翻:

```bash

sudo ncdu -x /

```

方向键进入目录,d 删除(谨慎使用),q 退出。它比反复敲 du 直观。

常见大户:/var/log/var/lib/docker/var/cache/home/tmp/var/spool

第三步:捞出具体的大文件

目录定位到之后,直接找大文件:

```bash

找出当前文件系统里大于 500M 的文件,按大小倒序

sudo find / -xdev -type f -size +500M -printf '%s\t%p\n' 2>/dev/null | sort -nr | head -20

```

%s 换成可读格式也行:

```bash

sudo find /var -xdev -type f -size +200M -exec ls -lh {} + 2>/dev/null | sort -k5 -hr | head -20

```

几个高频目标:

```bash

老日志

sudo find /var/log -xdev -type f -mtime +30 -size +100M -exec ls -lh {} + 2>/dev/null

缓存目录(Debian/Ubuntu)

sudo du -xhd1 /var/cache/apt 2>/dev/null

崩溃转储

sudo ls -lh /var/crash /var/lib/systemd/coredump 2>/dev/null

```

确认某个文件确实没用了再删。拿不准就先挪到别的盘:

```bash

sudo mv /var/log/old-app.log /data/archive/

```

第四步:处理"删了文件但空间没回来"

这是磁盘清理里最经典的一个坑:用 rm 删掉了日志,df 却一点没变。原因是某个进程还持有这个文件的句柄,inode 没被释放。

先找出这些"幽灵文件":

```bash

方法一:lsof

sudo lsof -nP +L1 2>/dev/null | head -30

方法二:没有 lsof 时,直接看 /proc

sudo find /proc/[0-9]*/fd -lname '*deleted*' -printf '%p -> %l\n' 2>/dev/null | head -30

```

输出里的路径形如 /proc/12345/fd/7,其中 12345 是进程号。

两种处理方式:

1. 重启或重载对应服务,句柄释放,空间自动归还。比如 nginx 重新打开日志文件:

```bash

sudo nginx -s reopen

```

2. 不想重启服务,就通过 /proc 把文件截断为零:

```bash

sudo truncate -s 0 /proc/12345/fd/7

或者

sudo sh -c ': > /proc/12345/fd/7'

```

只对普通文件有效,socket、管道会报错。

顺带记住这条经验:清理正在被写入的日志,用 truncate -s 0: > file,不要用 rm 不然服务会继续往一个已经"不存在"的文件里写,空间不会释放,直到重启。

第五步:日志清理与轮转

日志通常是可以安全清理的部分,但方式要对。

systemd journal:

```bash

看占用

journalctl --disk-usage

只保留最近 500M

sudo journalctl --vacuum-size=500M

只保留最近 7 天

sudo journalctl --vacuum-time=7d

```

想让它以后自动控制,编辑 /etc/systemd/journald.conf

```ini

[Journal]

SystemMaxUse=1G

SystemMaxFileSize=100M

MaxRetentionSec=2week

```

然后重启:

```bash

sudo systemctl restart systemd-journald

```

传统日志文件: 用 logrotate,别手动删。在 /etc/logrotate.d/ 下加一个配置文件,例如:

```

/var/log/myapp/*.log {

daily

rotate 7

missingok

notifempty

compress

delaycompress

copytruncate

}

```

copytruncate 适合那些不方便重开日志文件的应用(它先复制再清空原文件);如果应用支持信号重载,用 postrotate 发信号更干净。先干跑验证语法:

```bash

sudo logrotate -d /etc/logrotate.d/myapp

```

包管理缓存:

```bash

Debian / Ubuntu

sudo apt-get clean

RHEL / CentOS / Rocky

sudo dnf clean all

sudo yum clean all

```

第六步:Docker 占用

/var/lib/docker 很容易悄悄涨到几十上百 G。先看统计:

```bash

docker system df

docker system df -v

```

输出会分开列出镜像、容器、数据卷、构建缓存各占多少。清理按需选择:

```bash

清理停止的容器、未使用的网络、悬空镜像、构建缓存

docker system prune

更彻底:连未被任何容器使用的镜像一起清

docker system prune -a

顺带清理未被使用的数据卷(危险,确认没有重要数据)

docker system prune -a --volumes

```

注意:prune 是不可逆的,-a 会删掉所有没在运行的容器正在使用的镜像,--volumes 会删掉未被挂载的数据卷。执行前先跑一次 docker system df -v 看清楚。

单个容器的日志文件如果特别大,可以直接定位:

```bash

sudo find /var/lib/docker/containers -name '*-json.log' -size +100M -exec ls -lh {} + 2>/dev/null

```

截断它(容器不用重启):

```bash

sudo truncate -s 0 /var/lib/docker/containers/<容器ID>/<容器ID>-json.log

```

要治本,配置日志驱动上限。编辑 /etc/docker/daemon.json

```json

{

"log-driver": "json-file",

"log-opts": {

"max-size": "50m",

"max-file": "5"

}

}

```

```bash

sudo systemctl restart docker

```

这个配置只对新创建的容器生效,已有的容器需要重建才会应用。

另外,不要直接 rm -rf /var/lib/docker,那会破坏 Docker 的元数据,可能导致守护进程起不来。要清就通过 docker 命令清。

第七步:inode 用尽

df -h 有空间、df -i 却 100%,说明文件数量太多。典型场景:PHP session 文件、邮件队列、缓存目录、海量小文件的数据集、node_modules、容器 overlay 层。

先找出哪个目录文件最多:

```bash

GNU coreutils 支持 --inodes,按 inode 数量排序

sudo du -x --inodes -d1 / 2>/dev/null | sort -n

不支持 --inodes 时用 find 统计

for d in /var/*; do

[ -d "$d" ] && printf '%s\t%s\n' "$(sudo find "$d" -xdev 2>/dev/null | wc -l)" "$d"

done | sort -n | tail -20

```

定位到目录后,先预览再删:

```bash

预览:删之前一定先 print 看看

sudo find /var/lib/php/sessions -type f -mtime +7 -print | head -50

确认无误后再执行

sudo find /var/lib/php/sessions -type f -mtime +7 -delete

```

Docker 场景下,/var/lib/docker/overlay2 层数多、小文件多,用 docker system prune 通常能一次性回收大量 inode。

第八步:验证与收尾

清理完复查:

```bash

df -hT

df -i

docker system df

journalctl --disk-usage

```

如果 du 加起来只有 20G,df 却显示 100G 已用,还可能是这几个原因:

  • ext4 默认给 root 预留 5% 空间,非 root 用户看到的可用空间会少一截。查看预留量:

```bash

sudo tune2fs -l /dev/sda1 | grep -i 'reserved block count'

```

数据盘可以调小(比如 1%),系统盘不建议动:

```bash

sudo tune2fs -m 1 /dev/sdb1

```

仅适用于 ext2/3/4。

  • 有目录被挂载点"盖住"了:/var/lib/mysql 挂了盘,但原目录里的文件还在下面。需要 mount --bind 到别处才能看到。
  • LVM 快照、thin pool、btrfs 快照占用了空间,du 看不到。

常见坑与排错

rm 后空间不释放。 有进程还开着文件句柄,回到第四步,用 lsof +L1/proc 处理,最省事的办法是重启对应服务。

删日志后服务报错或不再写日志。 大概率是删的时候句柄还连着,应用以为文件还在。改用 truncate -s 0 或让 logrotate 的 copytruncate 处理。

docker system prune 之后空间没降多少。 检查是不是还有运行中的容器在写、有数据卷没清、或者构建缓存没被回收。再跑一次 docker system df -v 看明细。

扫描目录扫描到一半卡住。 多半是跨挂载点扫到了网络盘或慢速盘。加 -xdu -xfind -xdev),并用 nice -n 19 降优先级。

find ... -delete 删错东西。 养成先 -print-delete 的习惯,中间隔着一次肉眼核对。通配符删除永远不要在 / 下裸跑。

inode 清了但没降。 有些文件被进程 hold 住,同样是句柄问题,重启相关服务后再看 df -i

清理完一会儿又满。 说明有东西在持续写入,常见是应用 debug 日志、容器日志没限额、journald 没配上限、定时任务输出全打到文件。这时候要去配置轮转和限额,而不是每周手动清一次。

下一步建议

1. 加上监控告警。 用 node_exporter + Prometheus 采集 node_filesystem_avail_bytesnode_filesystem_files_free,容量和 inode 各设一条阈值告警,在 80% 就触发,别等到 100%。

2. 把日志治理成常规动作。 journald 配上限、应用日志接 logrotate、容器日志驱动配 max-size,这三件事做完,日志类问题能减少很多。

3. 定期巡检脚本。df -hdf -idocker system dfjournalctl --disk-usage 打包成一个巡检脚本,配合定时任务把结果发到运维群或邮件。

4. 规划磁盘分层。 系统盘和数据盘分开,数据盘用 LVM 或云盘,方便在线扩容。云厂商的磁盘扩容方式以官方文档当前版本为准,扩完还要记得扩容文件系统本身。

5. 沉淀一份清理清单。 把每台机器上"哪些目录可以删、保留多久、删之前要确认什么"写成文档,下次半夜收到告警时照着执行,比现场判断快得多。

AI 生成本文由 AI 基于公开信息自动生成,仅供参考。