硬盘突然满了这种事,谁遇到谁懂:
- 网站开始 500
- MySQL 说写不进去
- SSH 登录都卡(因为连
~/.bash_history都写不动)
更恶心的是:你删了半天文件,df -h 还是 100%。
下面这套流程是我自己救火常用的,目标很明确:先把“空间被谁吃了”定位出来,再决定是清、是迁、还是扩。
先看容量:
df -hT
重点看三列:
Use%:是不是某个挂载点(比如/或/var)单独爆了Type:ext4 / xfs / overlay(Docker)会影响你后面怎么扩容Mounted on:别把数据盘当系统盘清,清错盘更尴尬
再看 inode(这坑很常见):
df -ih
如果容量还有空间,但 inode 100%,症状跟磁盘满一模一样。常见原因:缓存目录、临时目录、小文件爆炸(比如某些日志按秒切)。
我一般从根目录往下扫,先找“最大的一层”。
# 统计 / 下一级目录占用(可按需换成 /var、/home、/opt)
sudo du -xh --max-depth=1 / | sort -h | tail -n 20
如果你想省时间,直接用 ncdu(强烈建议装):
# Debian/Ubuntu
sudo apt-get update && sudo apt-get install -y ncdu
# 交互式查看
sudo ncdu /
ncdu 的好处是:你不用猜“是不是这个目录”,它会把最大文件/目录直接摆你面前。
典型场景:
- 你删了一个巨大的 log
- 服务还在写那个 log(文件句柄没关)
df看起来完全没变化
用这条找:
sudo lsof +L1
输出里如果看到 deleted 但文件大小巨大,基本就是它。
处理方式很简单:
- 最稳:重启对应服务(Nginx / PHP-FPM / MySQL / 你的应用)
- 不想重启:让进程重新打开文件(比如某些服务支持
logrotate的copytruncate或发USR1信号)
大多数时候,重启服务就是最快的救火。
如果你用 Docker,先看一眼:
docker system df
清理(先从“安全的”开始):
# 删除没用的构建缓存
docker builder prune -f
# 删除没用的镜像/容器/网络(不会删正在跑的容器)
docker system prune -af
# 如果你确定不需要旧 volume(慎用)
docker volume prune -f
顺便提一句:很多人磁盘满不是镜像,是容器日志。
# 看看某个容器日志有多大
sudo du -sh /var/lib/docker/containers/*/*-json.log | sort -h | tail -n 20
如果日志确实爆了,建议给 Docker 配 log rotation(长期方案)。
# 看 journal 占用
sudo journalctl --disk-usage
# 只保留 7 天(示例)
sudo journalctl --vacuum-time=7d
# 或者限制到 1G
sudo journalctl --vacuum-size=1G
最常见的两个目录:
/var/log/nginx//var/log/apache2/
快速找最大的:
sudo du -sh /var/log/* | sort -h | tail -n 20
如果你只是救火,先把历史 log 压缩/删掉(别动正在写的文件,避免第 3 节那个坑)。
# 清缓存
sudo apt-get clean
# 删没用的依赖
sudo apt-get autoremove --purge -y
sudo du -sh /tmp/* 2>/dev/null | sort -h | tail -n 20
遇到“某个任务跑崩了”把临时文件堆满,这条基本一眼就能看出来。
先找小文件集中区:
# 找出 /var 下面小文件数量最多的目录
sudo find /var -xdev -type f 2>/dev/null | awk -F/ '{print $1"/"$2"/"$3"/"$4}' | sort | uniq -c | sort -nr | head
常见罪魁祸首:
- 某个服务疯狂写小日志
- 缓存目录没有 TTL
- 临时文件没清理
清理思路很简单:删“可再生”的(cache/tmp),保“不可再生”的(数据/上传/数据库)。
扩容前先确认:你是在云后台把磁盘扩了,还是只是“想扩”。
lsblk -f
# 也可以看分区表
sudo fdisk -l
你需要搞清楚:
- 是不是同一块盘(比如
/dev/vda或/dev/sda) - 你的文件系统挂在哪个分区(比如
/dev/vda1) - 有没有 LVM(如果看到
lvm2_member,那是另一条路)
# 安装 growpart(Debian/Ubuntu)
sudo apt-get update && sudo apt-get install -y cloud-guest-utils
# 把第 1 号分区扩到整块盘(示例:/dev/vda1)
sudo growpart /dev/vda 1
# 扩文件系统(ext4)
sudo resize2fs /dev/vda1
# 验证
df -hT
XFS 不能用 resize2fs,要这样:
# 先 growpart 扩分区
sudo growpart /dev/vda 1
# 然后对挂载点扩(假设挂载点是 /)
sudo xfs_growfs /
df -hT
# 扩物理卷
sudo pvresize /dev/vda1
# 看逻辑卷路径
sudo lvdisplay
# 扩逻辑卷(示例:把所有空闲都加给 root)
sudo lvextend -l +100%FREE /dev/mapper/ubuntu--vg-ubuntu--lv
# ext4
sudo resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv
# xfs(如果是 xfs)
# sudo xfs_growfs /
扩容这块如果你不熟,建议你先把 lsblk -f 的输出存下来再动手,别在救火的时候把分区表搞乱了。
最朴素的方式:cron + 阈值报警。
cat >/usr/local/bin/disk-alert.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
THRESHOLD=85
MOUNT=${1:-/}
usep=$(df -P "$MOUNT" | awk 'NR==2 {gsub(/%/,"",$5); print $5}')
if [ "$usep" -ge "$THRESHOLD" ]; then
echo "[disk-alert] $MOUNT usage=${usep}%" >&2
fi
EOF
chmod +x /usr/local/bin/disk-alert.sh
- Nginx/Apache:确认 logrotate 在跑
- 应用日志:别无限往一个文件写,按天切、保留 7-14 天
- Docker:启用日志滚动(别让 json log 变成几 GB)
df -hT/df -ih:确认是容量满还是 inode 满du -xh --max-depth=1或ncdu:找到最大目录lsof +L1:排除“删了但没释放”- 重点查
/var/log、Docker、journal、缓存 - 清完不够:扩容 +
growpart+resize2fs/xfs_growfs
你按这个顺序做,基本不会绕远路。
