日志把磁盘写满:云主机磁盘告警的六步排查法与自动巡检脚本

举报
yd_292302676 发表于 2026/10/28 09:36:58 2026/10/28
【摘要】 磁盘 100% 却找不到大文件?从 df 与 du 的差异出发,按 inode 耗尽、已删除文件被进程占用、日志轮转失效三条主线排查,附可直接落地的巡检脚本与日志体积限制配置。

上周有台跑了快两年的 ECS 突然所有接口开始返回 500,登录上去第一眼就是 /var 分区 100%。按经验先去 du 找大文件,结果最大的目录只有 2G,而 df 说 40G 已经用满。这种情况在新手看来很玄学,其实是云主机运维里最常见的一类"账对不上"的问题。这篇把排查路径和后续的自动化处理完整写一遍。

一、先分清两种"满"

看到 No space left on device,第一步不是找大文件,而是确认满的是哪一类资源:

df -h /        # 块空间
df -i /        # inode 数量

这两个指标完全独立。inode 是文件系统给每个文件分配的元数据槽位,小文件多的目录(比如 session 文件、缓存碎片、百万级小日志)很容易把 inode 先用光,此时磁盘还有一半空着,但任何写入都会失败,touch 一个新文件都报同样的错。判断依据很直接:df -h 显示还有余量,df -i 的 IUse% 到 100%,那就是 inode 耗尽,删大文件没有任何用,只能减少文件个数。

二、六步排查路径

确认是块空间满了之后,我固定按这六步走,基本能在十分钟内定位到源头。

第一步:定位到具体分区。用 df -h 看清是根分区、/var 还是数据盘满了。很多服务把日志写在 /var/log,而 /var 往往和根分区共用一个卷,日志一涨,系统日志也跟着写不进去。

第二步:从根开始逐层下钻。

du -x -h --max-depth=1 / 2>/dev/null | sort -rh | head -10

-x 这个参数别省。不加它,du 会跨进挂载在子目录上的其他盘(比如挂到 /data 的数据盘、容器 overlay 目录),统计出来的数字会把别的分区算进来,和 df 永远对不上。

第三步:对可疑目录重复第二步。通常两三轮就能收敛到某个具体目录。我遇到过的分布大致是:日志目录占六成,容器运行时目录占两成,剩下是备份文件和 core dump。

第四步:检查 inode。如果 du 死活找不到大文件,先看 df -i。要找哪个目录小文件最多:

for d in /var/*; do echo "$(find $d -xdev 2>/dev/null | wc -l) $d"; done | sort -rn | head

第五步:查已删除但仍被占用的文件。这是 du 和 df 对不上的头号原因。一个日志文件被 rm 掉了,但写它的进程还活着、文件句柄没关,这块空间就不会释放。du 遍历目录树看不到它(目录项已经没了),df 却照样把它算在已用里。

lsof +L1 2>/dev/null | head -20
# 或者不装 lsof 时直接看
ls -l /proc/*/fd 2>/dev/null | grep deleted

找到之后不要急着 kill 进程。正确做法是让进程重新打开日志:kill -USR1 <pid>(Nginx 等支持),或者干脆重启服务,空间立刻就回来了。

第六步:确认日志轮转真的在跑。很多机器的 logrotate 配置写好了,但没进 cron、或者 /etc/cron.daily 里的任务被禁用了,日志就一直往同一个文件里追加。logrotate -d /etc/logrotate.conf 可以干跑一遍看它到底会不会处理你的日志。

三、du 和 df 对不上,通常是这几种原因

现象 原因 处理方式
du 统计远小于 df 文件已删除但被进程占用 lsof +L1 找到进程,重启或发信号
du 统计远大于 df 目录被其他分区挂载覆盖 加 -x,或先 umount 再统计
df 满但 du 找不到大文件 inode 耗尽 df -i 确认,按目录清小文件
容器里看和宿主机不一致 overlay 分层,容器内看不到底层 在宿主机上统计 /var/lib/docker

四、清理只是止血,止住增长才是根治

腾出空间之后,如果不改增长来源,一周后还会满。我一般会做三件事:

限制 systemd journal 的体积。默认 journal 可以吃掉分区 10%,长期不清理能到几个 G:

journalctl --vacuum-size=500M
# /etc/systemd/journald.conf 里加
SystemMaxUse=500M

限制容器日志。Docker 默认的 json-file 驱动没有任何大小上限,一个话多的应用几天就能写满盘。全局配置写进 /etc/docker/daemon.json:

{
  "log-driver": "json-file",
  "log-opts": { "max-size": "50m", "max-file": "3" }
}

注意这个配置只对新创建的容器生效,已有容器要重建。

给关键日志配轮转。按天切、保留 7 到 14 天,并且优先用 create 而不是 copytruncate。copytruncate 在写入量大的场景下有丢日志的窗口期,它的好处只是不用重启进程。

五、把检查做成定时任务

人工排查只能救一次,真正省事的是让它自己报警。这个脚本我放在每台机器上,每十分钟跑一次,超过阈值就推消息:

#!/bin/bash
THRESHOLD=85
HOST=$(hostname)

check_disk() {
  df -P -x tmpfs -x devtmpfs | awk 'NR>1 {gsub("%","",$5); if ($5 >= '"$THRESHOLD"') print $6, $5}'
}

check_inode() {
  df -P -i -x tmpfs -x devtmpfs | awk 'NR>1 {gsub("%","",$5); if ($5 >= '"$THRESHOLD"') print $6, $5}'
}

alert=""
while read -r mnt pct; do
  [ -n "$mnt" ] && alert="${alert}磁盘 ${mnt} 使用率 ${pct}%
"
done <<< "$(check_disk)"

while read -r mnt pct; do
  [ -n "$mnt" ] && alert="${alert}inode ${mnt} 使用率 ${pct}%
"
done <<< "$(check_inode)"

if [ -n "$alert" ]; then
  echo -e "[$HOST]
$alert" | mail -s "磁盘告警 $HOST" ops@example.com
fi

脚本里两个细节值得留意:用 df -P 保证输出是单行格式,长设备名不会换行导致解析错位;用 -x tmpfs 排除内存文件系统,否则容器多的机器会被一堆小挂载点刷屏。

更省事的做法是把这套指标直接交给云监控。华为云的主机监控里可以给磁盘使用率配告警规则,超过阈值直接发短信或邮件,不用自己在机器上跑脚本——但对没有装 Agent 的机器,脚本仍然是兜底手段。

六、小结

这类故障的技术含量不高,难的是排查顺序。记住三条就够用:先 df -h 和 df -i 分清是空间还是 inode;du 一定要带 -x;对不上就查 lsof +L1。清理完之后再补上 journal、容器日志和 logrotate 三处限制,同一台机器基本不会因为同样的原因再挂第二次。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。