磁盘满了怎么办:排查三板斧

VPS磁盘突然满了别慌。小够教你三板斧:df看大盘、du找大户、日志和Docker镜像重点清理,十分钟定位元凶。跟着做就行,够用不折腾。

我是小够,只推够用的 VPS。

磁盘满是最常见的 VPS 翻车现场:网站突然打不开,SSH 登录提示"No space left on device",数据库写不进去。别急着加钱升级硬盘,十有八九是垃圾文件堆出来的。三板斧下去,十分钟找到元凶。

第一板斧:df,看大盘

先看整体情况,哪块盘满了:

df -h

输出里重点看 Use% 这一列。接近 100% 的就是出事的分区。顺带看 Mounted on,确认是系统盘 / 满了,还是数据盘满了。系统盘满了最要命,很多服务会直接罢工。

如果 df 显示还有空间,但写文件依然报错"No space left",再补一刀:

df -i

这是看 inode 用量。小文件太多(比如邮件队列爆了、session 文件堆积),inode 耗尽也会写不进去,盘看着还有空位。

第二板斧:du,找大户

知道哪块盘满了,接下来找是谁吃的空间。从根目录一层层往下挖:

du -sh /* 2>/dev/null | sort -rh | head -20

这条命令列出根目录下各目录的占用,按大小倒序。看到最大的几个,再钻进去:

du -sh /var/* 2>/dev/null | sort -rh | head -20

两三层下来,大户基本现形。根据小够的经验,元凶排行榜是这样的:

  1. /var/log:系统和应用日志。Nginx、MySQL 的日志几个月不清理,几十个 G 很轻松。
  2. Docker:/var/lib/docker,镜像、容器、volume、build 缓存,Docker 用户的头号空间杀手。
  3. 网站备份:备份脚本每天打包,却从不删旧备份,/backup 里躺着几十个历史包。
  4. MySQL binlog:开了 binlog 没设过期时间,数据库越大,binlog 涨得越快。

第三板斧:对症清理

找到大户就好办了,按类型处理:

日志文件:先看能不能直接删旧的。journalctl --disk-usage 看 systemd 日志占了多少,journalctl --vacuum-size=200M 直接砍到 200M。Nginx 日志删之前确认 logrotate 在跑,不然删完还会长回来。

Docker:一键清理三件套,按顺序来:

docker system prune -a   # 删掉没用的镜像、容器、网络
docker volume prune      # 删掉没用的 volume(小心,有数据的别乱删)

prune 之前先 docker ps -a 确认没有要留的 stopped 容器。生产环境建议加上定时任务,每周跑一次 docker system prune -f。

旧备份:保留最近 7 天的,之前的删掉或挪到异地。备份脚本里直接加上删除逻辑,一劳永逸:

find /backup -name "*.tar.gz" -mtime +7 -delete

MySQL binlog:登录 MySQL 执行 PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);,然后在配置里加上 expire_logs_days = 7,让它自动过期。

清理完还满?考虑升级

预防:让磁盘不再爆

清理是救火,预防才是正道。几个一次性配置,配完基本告别磁盘告警:

1. 配好 logrotate。大多数发行版自带 logrotate,但应用日志(Nginx、MySQL、Docker 容器日志)不一定在它的管理范围内。检查 /etc/logrotate.d/,给 Nginx 加上按天轮转、保留 14 天的配置。Docker 的话,在 /etc/docker/daemon.json 里加上日志大小限制:

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

这样单个容器日志最多 150M,永远不会无限涨。

2. Docker 定时清理。加一条 cron,每周日凌晨清理一次:

0 3 * * 0 docker system prune -af --filter "until=168h" >> /var/log/docker-prune.log 2>&1

until=168h 表示只删 7 天前没用过的,正在用的镜像和容器不受影响,生产环境也可以放心跑。

3. 备份脚本自带清理。前面给过 find -mtime +7 -delete,所有写备份脚本的人都应该加上。备份的意义是"最近的数据能恢复",不是"攒齐一年的历史"。

4. 装个磁盘告警。磁盘用到 80% 就告警,别等到 100%。简单的办法是 cron 每小时跑一次 df,超过阈值发邮件或推送。重要的机器建议上 Uptime Kuma 这类监控,磁盘、CPU、内存一起看。

5. 买机器时留余量。系统盘 20G 起步是底线,跑 Docker、数据库的直接 40G 起。硬盘是 VPS 里最便宜的资源,为几块钱差价半夜爬起来清日志,不值。

inode 耗尽专项

前面提过,df -h 看着有空间但写不进去,多半是 inode 用完了。用 df -i 确认,IUse% 接近 100% 就是它。常见元凶:

  • 邮件队列爆了:postfix 发不出邮件,队列堆了几十万封。postqueue -p 看一眼,postsuper -d ALL 清空(确认都是垃圾再删)。
  • PHP session 文件:/var/lib/php/sessions 里堆了几十万个小文件,配个定时清理或改用 redis 存 session。
  • 爬虫/日志按条落盘:某些应用把每条日志写成一个小文件,赶紧改成按天写文件。

inode 问题清完文件还不够,文件系统建的时候 inode 总数是固定的,ext4 默认一个 inode 对应 16KB 空间,小文件太多就是会爆。根治办法是应用层别再产生海量小文件。

如果三板斧下来,确实都是正经数据占的空间——网站图片、数据库、用户上传——那说明不是垃圾问题,是盘真的不够用了。这时候再考虑加硬盘或换大盘套餐,这钱花得才不冤。

另外提醒一句:买机器的时候,系统盘别买太小。10G 的盘看着便宜,装完系统加个 Docker 就剩不下多少了。个人小站建议 20G 起步,省那几块钱,半夜被告警短信吵醒不值当。

磁盘满不可怕,可怕的是不知道满在哪。三板斧收好,下次告警来了,十分钟搞定。