服务器磁盘占满的排查与清理顺序

磁盘占满的表现往往不是「磁盘错误」,而是各种莫名其妙的故障:MySQL 突然起不来、日志写不进去、容器反复重启、网站报 500。本文给一条从「定位是谁占的」到「安全清理」的完整路径。

第一步:确认是哪块盘满了

# 整体使用率
df -h

1. 注意两件事:
1. 1. inode 也可能满(小文件太多时 df -h 显示有空间但写不进)
df -i

1. 2. 容器内的 /var/lib/docker 可能挂在另一块盘
df -h /var/lib/docker

df -h 有空间但写不进,八成是 inode 满了。df -i 看 IUse%,接近 100% 就是这个问题——大量小文件(如 session、日志碎片)会先耗尽 inode 而不是空间。

第二步:找出谁占的(du 的正确用法)

# 从根往下逐层找,每层只看前 10 个
du -sh /* 2>/dev/null | sort -rh | head -10

1. 锁定大目录后继续往下钻
du -sh /var/* 2>/dev/null | sort -rh | head -10
du -sh /mnt/dockerv/* 2>/dev/null | sort -rh | head -10

1. 找单文件大于 100M 的
find / -xdev -type f -size +100M -exec ls -lh {} \; 2>/dev/null | head -20

1. 只看 inode 占用最多的目录(小文件密集)
find / -xdev -type d 2>/dev/null | while read d; do
  n=$(ls -U "$d" 2>/dev/null | wc -l)
  [ "$n" -gt 1000 ] && echo "$n $d"
done | sort -rn | head -10

第三步:常见占用源(按实际频率排序)

占用源典型大小清理方式风险
Docker 镜像与构建缓存几 GB – 几十 GBdocker system prune -a会删未使用的镜像,需确认
容器日志(json-file)单容器可达 GB配 max-size 限制 + 清空低
journald 系统日志1-4 GBjournalctl --vacuum-size=200M低
MySQL binlog几 GBPURGE BINARY LOGS高:删了无法恢复到该时间点
MySQL 慢日志 / 错误日志几百 MB轮转或清空低
备份文件累积随备份策略按保留策略删旧备份高:删了没备份
过期证书归档几十 MBcertbot 自动管理,慎删中
系统包缓存几百 MBdnf clean all / apt clean低

按「风险从低到高」的顺序处理,先做无损的:

# 1. 清包管理器缓存(完全无损)
dnf clean all          # Alibaba Cloud Linux / CentOS / Fedora
1. apt clean            # Debian / Ubuntu

1. 2. 限制并清理 journald(保留最近 200M)
journalctl --vacuum-size=200M
journalctl --vacuum-time=14d

1. 3. Docker 清理(先看会删什么,加 --dry-run 不实际删)
docker system df                    # 看占用分布
docker image prune -a               # 删未使用的镜像(不删容器)
docker builder prune                # 删构建缓存
1. docker system prune -a --volumes  # 危险:连 volume 一起删,慎用

第四步:容器日志是隐形大户

Docker 默认日志驱动是 json-file,无上限。一个话多的容器几天就能写出几个 GB。

# 看各容器日志大小
du -sh /var/lib/docker/containers/*/*-json.log 2>/dev/null | sort -rh | head -10

1. 看单个容器的日志配置
docker inspect <容器名> --format '{{json .HostConfig.LogConfig}}'

1. 清空某个容器的日志(不重启容器,安全)
truncate -s 0 $(docker inspect <容器名> --format '{{.LogPath}}')

根治:在 compose 里给所有服务配日志轮转(max-size + max-file):

# compose.yaml 全局锚点,所有服务引用
x-logging: &default-logging
  driver: json-file
  options:
    max-size: "16m"
    max-file: "3"

services:
  wp:
    logging: *default-logging
  nginx:
    logging: *default-logging
  • max-size: 16m + max-file: 3 意味着单容器日志最多 48MB
  • 已经跑起来的容器需要重建才生效(up -d 会重建)
  • 不要用 driver: none:虽然省空间,但出问题时完全没有日志可查

第五步:MySQL 相关的大文件(风险最高)

-- 看 binlog 占用
SHOW BINARY LOGS;

-- 看当前正在写哪个(这个不能删)
SHOW MASTER STATUS;

-- 删除指定时间之前的 binlog(谨慎:删了就无法恢复到该时间点)
PURGE BINARY LOGS BEFORE '2026-08-01 00:00:00';

-- 或按文件删(保留最后一个)
PURGE BINARY LOGS TO 'mysql-bin.000123';

binlog 清理的三个前提:① 有近期全量备份 ② 确认不需要按时间点恢复 ③ 保留时间满足合规要求。三个都不满足就别删,改为调小 expire_logs_days 让它自动轮转。

-- 更安全的做法:设自动过期(默认 30 天,可调小)
SET GLOBAL expire_logs_days = 7;
-- 写入 my.cnf [mysqld] expire_logs_days=7

第六步:被删除但空间没释放(经典坑)

df 显示磁盘满,但 du 加起来远小于磁盘总量——说明有进程还持有已删除文件的句柄。

# 找出「已删除但被进程占用」的文件
lsof +L1 2>/dev/null | head -20
1. 或
lsof | grep deleted | awk '{print $1, $2, $7, $9}' | sort -k3 -rn | head -10

1. 典型场景:日志被 rm 了,但 nginx/apache 还拿着句柄,空间不释放
1. 处理:重启那个进程(或让它重新打开日志文件)
1.   nginx -s reopen      ← 无需重启,重新打开日志文件
1.   systemctl restart xxx
  • 永远不要用 rm 删正在被写入的日志:用 truncate -s 0,或配 logrotate
  • nginx 可以用 nginx -s reopen 重开日志,无需重启服务
  • 容器场景下 truncate 容器日志路径即可,不影响容器运行

第七步:预防(比清理更重要)

措施配置位置效果
容器日志轮转compose logging 锚点单容器日志封顶 48MB
journald 限额/etc/systemd/journald.conf系统日志封顶
binlog 自动过期my.cnf expire_logs_daysMySQL binlog 自动轮转
备份保留策略backup.sh 里删 N 天前备份不无限累积
磁盘告警cron + 阈值检查80% 时提前收到通知

最后一条最容易被忽略——等磁盘 100% 才发现就晚了,很多服务在写不进日志时才开始报错,排查方向会被误导。

# 简单的磁盘告警脚本(放进 cron,每天跑)
#!/bin/bash
THRESHOLD=80
USE=$(df / | awk 'NR==2 {gsub("%",""); print $5}')
if [ "$USE" -ge "$THRESHOLD" ]; then
    echo "磁盘使用 ${USE}%,超过 ${THRESHOLD}%" | \
      mail -s "磁盘告警 $(hostname)" you@example.com
fi

总结

  • 先看 df -i:有空间但写不进,八成是 inode 满了
  • 按风险从低到高清理:包缓存 → journald → docker 镜像 → 容器日志 → 最后才动 binlog 和备份
  • 容器日志默认无上限,必须配 max-size,这是最高频的隐性占用
  • df 满但 du 小 = 有进程持有已删文件,用 lsof +L1 找,重启进程或 reopen 日志
  • 80% 就该告警,等到 100% 时故障现象会误导排查方向
暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇