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

背景 凌晨收到微信告警:磁盘使用率超过 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。

五、防止复发

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

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

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

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

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

  1. 加磁盘告警 用 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)

相关推荐
数据库小学妹1 小时前
向量检索最小可运行实现:HNSW索引参数与召回率/延迟权衡
数据库·后端
姜鱼问生1 小时前
Docker Volume 备份与恢复实战:不停机迁移数据
后端·算法·架构
suhan1 小时前
【数据结构】三、栈和队列:5.顺序队列(循环队列)(初始化,判空判满,入队,出队,实例)
数据结构·后端·算法
看浪的路人1 小时前
第7讲:大模型后端服务化——Go 实现高并发 LLM Proxy / Router / Fallback
开发语言·后端·golang
suhan1 小时前
【数据结构】三、栈和队列:3.链栈(链栈栈的初始化,判空,进栈,出栈,读取栈顶,链栈实例)
数据结构·后端·算法
邱杰4701 小时前
数智人事系统(十一):Spring Boot + MyBatis-Plus 的 CRUD 落地
后端·github
大雄很用心1 小时前
如何理解时间复杂度与空间复杂度?| 面试刷题笔记02
算法
邱杰4701 小时前
数智人事系统(十三):考勤打卡与出勤日历
后端·github
suhan1 小时前
【数据结构】三、栈和队列:4.栈的应用(括号匹配,四则运算表达式求值,进制转换,递归)
数据结构·后端·算法