Linux 磁盘占用检查与分析
从 filesystem、inode、目录、Docker、deleted-open file 到长期监控,建立一套从发现磁盘异常到定位业务 owner 的分层排查流程。
#howto
#growing
#ops
#linux
#linux
[!info] related notes
- Linux:Linux MOC、Linux 文件系统命令
- 文件系统总览:duf:文件系统与挂载点磁盘总览
- 目录下钻:gdu:交互式目录磁盘占用分析
- Docker:Docker 磁盘占用诊断:system df、builder du 与 buildx du
- 幽灵占用:lsof +L1:排查已删除但仍占用磁盘的文件
- 长期监控:Beszel、Netdata
Linux 磁盘占用检查与分析
核心思路
不要从:
磁盘满了
→ 找几个看起来大的目录
→ rm -rf
开始。
应该分层回答:
1. 哪个 filesystem 满?
2. 是 block 还是 inode?
3. 哪个目录 / 文件占空间?
4. 它属于什么业务对象?
5. 是文件、数据库、容器 layer、cache,还是 deleted-open file?
6. 哪些空间真的可回收?
7. 为什么之前没有发现增长?
一张流程图
duf / df -hT
│
├─ block usage 高
│ ↓
│ gdu -x --no-delete /
│ ↓
│ 找到大目录 / 大文件
│ ├─ Docker/containerd → docker system df / buildx du
│ ├─ DB → DB 自身工具 / retention
│ ├─ logs → journal/logrotate/app
│ ├─ cache → owner 工具自己的 cache clean
│ └─ 对不上 df → lsof +L1
│
└─ inode usage 高
↓
找海量小文件
第 1 层:确认 filesystem
系统自带:
df -hT
更适合人工阅读:
duf
必须同时看 inode:
df -ih
# 或
duf --inodes
判断
Use% 高 → block capacity 问题
IUse% 高 → inode / 小文件数量问题
两者都正常 → 可能看错挂载点或业务告警口径
第 2 层:下钻目录
推荐:
sudo gdu -x --no-delete /
其中:
-x避免跨 NFS / SSHFS / FUSE 等其他文件系统;--no-delete把分析与变更分离。
系统自带方案:
sudo du -xhd1 / | sort -h
sudo du -xhd1 /var | sort -h
sudo du -xhd1 /srv | sort -h
不断缩小范围:
/
→ /srv
→ /srv/app
→ /srv/app/data
→ 具体 DB / log / cache
第 3 层:找单个大文件
例如找大于 1 GiB 的文件:
sudo find / -xdev -type f -size +1G -printf '%s\t%p\n' \
| sort -nr \
| awk -F'\t' '{printf "%.2f GiB\t%s\n",$1/1073741824,$2}'
这一步很适合发现:
SQLite DB
日志
备份包
core dump
模型文件
临时归档
第 4 层:Docker / containerd 单独分析
如果大目录是:
/var/lib/docker
/var/lib/containerd
先运行:
docker system df
docker system df -v
docker builder du
docker buildx du
核心原则:
不要直接删除
/var/lib/docker或/var/lib/containerd内部文件。
正确做法见 Docker 磁盘占用诊断。
第 5 层:df 与 du 对不上
先怀疑 deleted-open file:
sudo lsof +L1
典型模型:
app.log 20G
→ rm app.log
→ 应用仍持有 FD
→ 路径消失
→ 20G 数据块还在
此时 du 看不到,但 df 仍然计算这些 block。
详见 lsof +L1。
第 6 层:应用数据要按 owner 清理
看到大目录后,必须先分类:
| 类型 | 例子 | 正确动作 |
|---|---|---|
| Cache | npm / pnpm / uv / Playwright | 用工具自己的 cache clean / 重建策略 |
| Database | SQLite / PostgreSQL / app DB | 先分析表、retention、VACUUM/归档策略 |
| Logs | journald / app log | retention / rotate / reopen |
| Docker | image / cache / volume | Docker CLI 判断引用 |
| Backup | snapshot / tarball | 保留策略 + 明确删除旧版本 |
| Source | repo / worktree / node_modules | 判断能否重建、是否仍在开发 |
不能因为“它大”就默认“它是垃圾”。
第 7 层:长期监控
现场排障回答:
现在空间去哪了?
长期监控回答:
从什么时候开始增长?增长速度是多少?哪台机器快到阈值?
轻量多节点:
更高粒度实时观测:
一个合理组合:
Beszel / Netdata
→ 发现 70% → 80% → 90% 的趋势
SSH
→ duf
→ gdu
→ Docker / DB / lsof
→ 根因
常见误区
误区 1:df 就能找出大目录
df 看 filesystem,不看目录树。
误区 2:du 最大的目录一定能删
它可能是数据库、volume 或生产数据。
误区 3:Docker image 显示的 SIZE 可以直接相加
layer 会共享,应看 Docker 自己的 usage / reclaimable 口径。
误区 4:磁盘没满就不用看 inode
百万级小文件可能先耗尽 inode。
误区 5:rm 一个大日志就一定释放空间
如果进程仍持有 FD,数据块不会立刻释放。
误区 6:生产服务器直接 gdu 里按 d
诊断工具和删除动作应分离。推荐 --no-delete。
快速 Checklist
# 1. 文件系统
[df / duf]
df -hT
df -ih
duf
# 2. 目录
sudo gdu -x --no-delete /
# 3. 大文件
sudo find / -xdev -type f -size +1G -printf '%s\t%p\n' | sort -nr | head
# 4. Docker
docker system df
docker system df -v
docker builder du
docker buildx du
# 5. deleted-open file
sudo lsof +L1
# 6. 日志
journalctl --disk-usage
# 7. 挂载关系
findmnt
lsblk -f
Oracle2 实战案例(2026-09-30)
一次真实排查中:
根盘 145G
清理前约 98%
清理后约 76%
继续下钻得到:
/var ~39G
/var/lib/containerd ~35G
/srv ~28G
hermes-personal ~28G
/home ~28G
随后在 Hermes Personal 中发现:
profiles/memoflow ~16G
state-snapshots ~4.1G
opencode/opencode.db ~9.1G
这个案例说明:
第一轮 Docker 清理解决“可回收垃圾”,第二轮仍必须回到业务数据生命周期本身。
掌握标准
- 能区分 filesystem、directory、file、inode 四个层级。
- 能解释
df与du不一致的常见原因。 - 能用
-x避免跨文件系统误扫。 - 能区分 Docker image、container、volume、builder cache。
- 不直接删除 containerd 内部数据。
- 能把“找到大文件”继续追到业务 owner 与 retention policy。
- 能用长期监控在磁盘接近 100% 前发现趋势。