网站突然打不开,SSH 登录后执行命令报 “No space left on device”,这是 VPS 用户最常见的故障之一。磁盘满了不仅写不进日志,MySQL 也可能直接拒绝写入,甚至导致数据库损坏。很多人第一反应是删文件,但删错位置不但没释放空间,还可能把业务数据一起删掉。这篇按“先定位、再清理、最后预防”的顺序讲清楚。
一、先确认哪个分区满了
df -h
重点看 Use% 和 Mounted on 两列。常见情况是根分区 100%,而数据盘还有空间。根分区满优先查 /var;数据盘满优先查网站目录、备份目录和上传目录。
顺便看一眼 inode:
df -i
如果 inode 使用率 100%,说明小文件太多,常见于 session 文件、缓存碎文件、邮件队列。这种情况删大文件没用,要按文件数量排查。
二、定位大目录:du 由浅入深
du -h --max-depth=1 / | sort -hr | head -20
找到最大的目录后逐层往下查,例如:
du -h --max-depth=1 /var | sort -hr | head
不要一上来就 du -h / 全盘扫描,IO 会飙高,网站会更卡。优先查 /var/log、/var/lib/docker、/www、/data、/root、/tmp 这几个位置,基本能覆盖九成情况。
三、最常见的几个“元凶”
- 系统日志:journalctl --disk-usage 查看,长期不清理能占几个 G。
- 网站日志:Nginx 的 access.log 在流量大或被刷时增长极快。
- Docker:镜像层、停止的容器和 json-file 日志,docker system df 一看便知。
- MySQL binlog:没设过期时间会一直累积。
- 备份文件:本地备份没做轮转,越备越多。
- 已删除但被占用的文件:rm 了空间没释放,进程还握着句柄。
四、清理日志的正确姿势
不要直接 rm 正在写入的日志,进程句柄还在,空间不会释放。正确做法是清空文件:
: > /var/log/nginx/access.log
journalctl 用自带命令限制体积更安全:
journalctl --vacuum-size=200M journalctl --vacuum-time=7d
长期方案是改 /etc/systemd/journald.conf 里的 SystemMaxUse=200M,再执行 systemctl restart systemd-journald。
五、已删除文件仍占用空间
lsof +L1 | head -20
输出里能看到被删但仍打开的文件。处理方式是重启对应进程,比如 systemctl restart nginx,空间立刻释放。这也是“删了文件 df 还是 100%”的典型原因,不查这一步很容易白忙一场。
六、Docker 占用清理
docker system df docker image prune -a docker builder prune docker system prune -a --volumes
注意最后一条会删掉未使用的数据卷,执行前确认没有业务数据在里面。生产机上更稳妥的做法是先 docker ps -a,把确实不用的容器和镜像单独删除,而不是无差别清理。
七、MySQL binlog 与备份
SHOW BINARY LOGS; PURGE BINARY LOGS BEFORE NOW() - INTERVAL 7 DAY;
更推荐在配置文件里设置 expire_logs_days=7,MySQL 8 用 binlog_expire_logs_seconds。备份文件则用脚本保留最近 7 天,旧文件自动删除,并尽量同步到对象存储,不要长期堆在系统盘上。
八、日志轮转:避免下次再满
Nginx 日志可以交给 logrotate 管理,在 /etc/logrotate.d/nginx 写入:
/var/log/nginx/*.log {
daily
rotate 14
missingok
compress
delaycompress
notifempty
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}logrotate 默认由 cron 每天执行,配置好后不用手动干预。Docker 容器日志则要在 daemon.json 里限制 max-size 和 max-file,这一项比事后清理有效得多。
九、扩容而不是一直删
如果清理完很快又满,说明容量确实不够。云厂商的云盘一般支持在线扩容,扩容后在系统里扩分区和文件系统:
growpart /dev/vda 1 resize2fs /dev/vda1 # ext4 xfs_growfs / # xfs
LVM 则先 pvresize,再 lvextend -r。操作前务必创建快照,扩容命令敲错分区名是不可逆事故。
十、常见问题
- df 和 du 结果差很多:大概率是已删除未释放的文件,用 lsof +L1 确认。
- 删了日志空间没变:进程还持有句柄,清空文件或重启服务,而不是删除文件。
- inode 满了:按目录统计文件数量,清理 session、缓存和临时文件。
- 清完又满:查是否被刷日志、是否有死循环写日志、备份是否堆积。
总结
磁盘排查有固定顺序:df 找分区,du 找目录,lsof 找幽灵文件,再按日志、Docker、数据库、备份逐项清理。清理只是止血,真正要做的是日志轮转、容器日志限制、备份轮转和容量监控。把这四件事配好,磁盘满导致宕机的概率会低很多。