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