背景
凌晨收到微信告警:磁盘使用率超过 90%。
SSH 上去一看:
$df-hFilesystem Size Used Avail Use% Mounted on /dev/vda1 40G 36G2.1G95% /40G 的盘,只剩 2G。再涨一点,MariaDB 写不进去,Nginx 日志写不了,整机就挂了。
想做到:快速定位谁占的空间,安全清理,并防止复发。
原因
磁盘占用无非几个大户:
嫌疑对象排查表
| 嫌疑对象 | 常见位置 |
|---|---|
| Docker 镜像 / 容器 / Volume | /var/lib/docker/ |
| 日志文件 | /var/log/ |
| 数据库数据 | /var/lib/mysql/ |
| 备份文件 | /home/*/backups/ |
| 临时文件 | /tmp/、/var/tmp/ |
| 系统缓存 | /var/cache/ |
按占用从大到小逐层排查。
解决
一、先定位:哪一层目录最大
# 根目录下各目录大小sudodu-sh/*2>/dev/null|sort-rh|head-10典型输出:
12G /var 8.5G /home 4.2G /usr ...再往下钻:
sudodu-sh/var/*2>/dev/null|sort-rh|head-10sudodu-sh/var/lib/*2>/dev/null|sort-rh|head-10二、Docker 占用排查
# 镜像、容器、volume、构建缓存的总体占用dockersystemdf# 详细列出每个镜像dockerimages--format'table {{.Repository}}\t{{.Tag}}\t{{.Size}}'# 列出所有 volume 及大小dockersystemdf-v典型输出:
TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 15 5 3.2GB 2.1GB (65%) Containers 8 5 120MB 80MB (66%) Local Volumes 6 4 800MB 200MB (25%) Build Cache 0 0 0B 0BRECLAIMABLE 那列就是可以清掉的。
三、日志占用排查
# 大日志文件排行sudofind/var/log-typef-size+50M-execls-lh{}\;2>/dev/null|sort-k5-rh典型输出:
-rw-r----- 1 www-data adm 2.1G /var/log/nginx/access.log -rw-r----- 1 root adm 800M /var/log/syslog -rw-r----- 1 root adm 300M /var/log/fail2ban.log注意:access.log 2.1G 说明日志轮转没配好。
四、清理(从安全到激进)
1 清理 Docker(最安全)
# 清掉未使用的镜像、容器、网络、构建缓存dockersystem prune-a# 加上 volume(会删掉未挂载的 volume,慎用)dockersystem prune-a--volumes效果:通常能回收几百 MB 到几 GB。
注意:-a 会删掉所有没有被容器使用的镜像,包括你 pull 了但暂时没起的。想保守一点就去掉 -a。
2.清理日志(立竿见影)
# 先看有哪些 logrotate 配置ls/etc/logrotate.d/# 手动触发一次轮转sudologrotate-f/etc/logrotate.d/nginx# 直接清空(不要 rm,rm 后 Nginx 不会释放句柄)sudotruncate-s0/var/log/nginx/access.log为什么用 truncate 不用 rm:
# 错误:rm 后 Nginx 还拿着旧文件的句柄,空间不释放sudorm/var/log/nginx/access.log# 正确:截断为空文件,Nginx 继续用同一个句柄sudotruncate-s0/var/log/nginx/access.log3清理备份(按策略保留)
# 保留最近 7 天find/home/user/backups-name"*.tar.gz"-mtime+7-delete# 保留最近 10 个ls-t/home/user/backups/*.tar.gz|tail-n+11|xargs-rrm4 清理 APT 缓存(Ubuntu 专属)
sudoaptcleansudoaptautoremove-y通常能回收几百 MB。
五、防止复发
- 配置 logrotate
创建 /etc/logrotate.d/myapp:
/var/log/myapp.log { daily rotate 7 compress delaycompress missingok notifempty copytruncate }copytruncate 的作用:复制一份日志然后截断原文件,进程不需要重启。
- 配置 Docker 日志上限
创建 /etc/docker/daemon.json:
{"log-driver":"json-file","log-opts":{"max-size":"10m","max-file":"3"}}重启 Docker 后,每个容器的日志最多占 30MB。
注意:只对新建的容器生效。已有容器需要重建。
- 加磁盘告警
用 Uptime Kuma 或脚本监控:
#!/usr/bin/env bashTHRESHOLD=85USAGE=$(df/|awk'NR==2 {print $5}'|tr-d'%')if["$USAGE"-gt"$THRESHOLD"];thencurl-fsS--get"$KUMA_PUSH_URL"\--data-urlencode"status=down"\--data-urlencode"msg=磁盘使用率${USAGE}%">/dev/nullelsecurl-fsS--get"$KUMA_PUSH_URL"\--data-urlencode"status=up"\--data-urlencode"msg=磁盘${USAGE}%"\--data-urlencode"ping=$USAGE">/dev/nullfi验证效果
清理前后对比:
$df-h/ Filesystem Size Used Avail Use% Mounted on /dev/vda1 40G 36G2.1G95% /# 清理前# 清理 Docker$dockersystem prune-a-fDeleted Images:10Deleted Containers:3Total reclaimed space:2.3GB# 清空 nginx 日志$sudotruncate-s0/var/log/nginx/access.log $sudotruncate-s0/var/log/syslog# 清 APT 缓存$sudoaptclean $df-h/ Filesystem Size Used Avail Use% Mounted on /dev/vda1 40G 29G9.2G77% /# 清理后从 95% 降到 77%,回收 7GB。
三个坑
坑 1:rm 日志文件不释放空间
# 错误sudorm/var/log/nginx/access.logdf-h# 空间没变!原因:Nginx 进程还拿着文件句柄。
正确:
sudotruncate-s0/var/log/nginx/access.log或重启 Nginx:
sudosystemctl restart nginx坑 2:docker system prune -a 删太狠
-a 会删掉所有没被运行容器使用的镜像,包括:
你 pull 了准备用的
旧版本回滚用的
构建缓存
建议:
生产环境先 docker system df 看看能回收多少
用 docker image prune(不带 -a)更保守
定期清理比一次清光好
坑 3:忘了 Docker 日志上限
默认 Docker 日志无限增长。
一个疯狂打日志的容器,几天就能吃掉几 GB。
检查当前日志大小:
sudodu-sh/var/lib/docker/containers/*/*-json.log|sort-rh|head修复:
配置 /etc/docker/daemon.json(本文第五部分)
重建现有容器(docker compose down && docker compose up -d)