服务器磁盘满了?从告警到清理的完整排查

17 阅读1分钟

背景 凌晨收到微信告警:磁盘使用率超过 90%。

SSH 上去一看:

$ 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/

按占用从大到小逐层排查。 解决 一、先定位:哪一层目录最大

# 根目录下各目录大小
sudo du -sh /* 2>/dev/null | sort -rh | head -10

典型输出:

12G     /var
8.5G    /home
4.2G    /usr
...

再往下钻:

sudo du -sh /var/* 2>/dev/null | sort -rh | head -10
sudo du -sh /var/lib/* 2>/dev/null | sort -rh | head -10

二、Docker 占用排查

# 镜像、容器、volume、构建缓存的总体占用
docker system df

# 详细列出每个镜像
docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.Size}}'

# 列出所有 volume 及大小
docker system df -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        0B

RECLAIMABLE 那列就是可以清掉的。 三、日志占用排查

# 大日志文件排行
sudo find /var/log -type f -size +50M -exec ls -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(最安全)

# 清掉未使用的镜像、容器、网络、构建缓存
docker system prune -a

# 加上 volume(会删掉未挂载的 volume,慎用)
docker system prune -a --volumes

效果:通常能回收几百 MB 到几 GB。

注意:-a 会删掉所有没有被容器使用的镜像,包括你 pull 了但暂时没起的。想保守一点就去掉 -a。 2.清理日志(立竿见影)

# 先看有哪些 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:

# 错误:rm 后 Nginx 还拿着旧文件的句柄,空间不释放
sudo rm /var/log/nginx/access.log

# 正确:截断为空文件,Nginx 继续用同一个句柄
sudo truncate -s 0 /var/log/nginx/access.log

3清理备份(按策略保留)

# 保留最近 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 专属)

sudo apt clean
sudo apt autoremove -y

通常能回收几百 MB。

五、防止复发

  1. 配置 logrotate 创建 /etc/logrotate.d/myapp:
/var/log/myapp.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
}

copytruncate 的作用:复制一份日志然后截断原文件,进程不需要重启。

  1. 配置 Docker 日志上限 创建 /etc/docker/daemon.json:
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

重启 Docker 后,每个容器的日志最多占 30MB。

注意:只对新建的容器生效。已有容器需要重建。

  1. 加磁盘告警 用 Uptime Kuma 或脚本监控:
#!/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

验证效果 清理前后对比:

$ 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 日志文件不释放空间

# 错误
sudo rm /var/log/nginx/access.log
df -h    # 空间没变!

原因:Nginx 进程还拿着文件句柄。

正确:

sudo truncate -s 0 /var/log/nginx/access.log

或重启 Nginx:

sudo systemctl restart nginx

坑 2:docker system prune -a 删太狠 -a 会删掉所有没被运行容器使用的镜像,包括:

你 pull 了准备用的

旧版本回滚用的

构建缓存

建议:

生产环境先 docker system df 看看能回收多少

用 docker image prune(不带 -a)更保守

定期清理比一次清光好

坑 3:忘了 Docker 日志上限 默认 Docker 日志无限增长。

一个疯狂打日志的容器,几天就能吃掉几 GB。

检查当前日志大小:

sudo du -sh /var/lib/docker/containers/*/*-json.log | sort -rh | head

修复:

配置 /etc/docker/daemon.json(本文第五部分)

重建现有容器(docker compose down && docker compose up -d)