磁盘满不是「删点东西」就完事。先分清是空间满了还是 inode 满了,再找出真正占地方的东西,最后把会持续增长的部分限额,否则过几天还会满。

第 0 步:确认是哪一种「满」

df -h
df -i

df -h 看空间。df 的手册说明它显示的是「文件系统剩余空间」,-i 则是:

list inode information instead of block usage

如果空间还有、但写入报 No space left on device,基本就是 inode 用完了,常见于大量小文件。

第 1 步:找大目录

du -xh --max-depth=1 / | sort -h

进入最大的目录继续看,通常落在这几处:

  • /var/log:系统与应用日志;
  • /var/lib:数据库、Docker、软件数据;
  • /var/cache:包缓存;
  • /home 或 /root:下载的文件、备份包;
  • Docker 的 overlay 目录(通常在 /var/lib/docker)。

du 的手册提醒:它估算的是磁盘占用,与文件「表观大小」可能不同(稀疏文件、内部碎片等)。如果数字对不上,别急着怀疑工具。

第 2 步:找大文件

find / -xdev -type f -size +200M -exec ls -lh {} \; 2>/dev/null

-xdev 不跨文件系统,避免扫到挂载的其他磁盘。重点看:旧备份包、导出的 SQL、核心转储(core)、日志归档。

第 3 步:查「已删除但仍被占用」的文件

这是最容易被忽略的一类。现象是 df 显示快满,但 du 加起来远小于已用空间。

lsof +L1 2>/dev/null | head -50

找到对应进程后,按顺序处理:

  1. 能重启的服务直接重启;
  2. 不能重启的,用 truncate -s 0 <文件路径>(对日志类文件)或写空 /proc/<pid>/fd/<fd>;
  3. 处理完再看 df -h。

注意不要用 rm 去删一个被进程占用的日志:文件从目录消失,但空间依然被占着。

第 4 步:分类清理

系统日志

journalctl --disk-usage
journalctl --vacuum-size=200M
journalctl --vacuum-time=7d

journald.conf 手册对 SystemMaxUse 的定义是:控制 journal 最多能占多少磁盘;默认值按文件系统大小的 10%、上限 4G。可以直接设置上限:

# /etc/systemd/journald.conf
SystemMaxUse=200M
systemctl restart systemd-journald

传统日志

/var/log 下的 *.log、*.gz 按时间清,别用 rm -rf /var/log/*。正确的做法是让 logrotate 管。

包缓存

apt clean
apt autoremove --purge

Docker

docker system df
docker builder prune
docker image prune
docker image prune -a

顺序是:构建缓存 → 悬空镜像 → 未使用镜像。docker volume prune 要慎用,命名卷里可能是数据库数据。

数据库与备份

  • 二进制日志、慢查询日志、旧备份包;
  • 备份文件不要长期放本机,按保留策略移到另一台机器或对象存储。

第 5 步:防复发

给 journal 设上限

见上面的 SystemMaxUse。

给应用日志配 logrotate

logrotate 的官方说明是:轮转、压缩、删除与邮寄日志,可按天/周/月或按大小触发。新建 /etc/logrotate.d/myapp:

/var/log/myapp/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
}

copytruncate 适合那些不会自己重开日志文件的程序:先复制再清空原文件,避免重启服务。

手动测试配置(不实际轮转,只校验语法并输出过程):

logrotate -d /etc/logrotate.d/myapp

给 Docker 日志设上限

/etc/docker/daemon.json:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

改完重启 Docker;已有容器要重建才生效。

监控阈值

对磁盘使用率和 inode 使用率都设告警,阈值建议 80%。只盯空间不盯 inode,仍会突然写不进去。

常见问题对照

现象 优先检查
df 满但 du 不大 lsof +L1 找已删除仍被占用的文件
有空间但写不进去 df -i 看 inode
删了日志空间没回来 进程仍持有句柄,重启或 truncate
日志反复撑满 没配 logrotate,或 logrotate 条件不匹配
Docker 目录越来越大 构建缓存 + 未限制日志大小
数据库目录异常增长 binlog、慢日志、未清理的旧表

一句话总结

先 df -h 与 df -i 分清空间还是 inode → du 找目录 → find 找大文件 → lsof +L1 查幽灵占用 → 分类清理 → 用 journald、logrotate、Docker 日志上限防复发。