凌晨 2 点收到告警,服务挂了。SSH 上去一看,No space left on device。df 一看,根分区 100% 了。但 du 一算,明明只用了 20G,分区有 40G。坑在于:有已删除的文件还占着空间。

问题现象

df -h
# Filesystem      Size  Used Avail Use% Mounted on
# /dev/vda1        40G   40G     0 100% /

du -sh /*
# 加起来只有 20G

df 说用了 40G,du 说只用了 20G。差的 20G 去哪了?

原因分析

说白了,就是有文件被删除了,但进程还持有文件句柄。Linux 的机制是:文件被删除后,只要还有进程在用它,磁盘空间就不会释放。

坑在于:du 只统计当前存在的文件,不统计已删除但还被占用的文件。

排查过程

第一步:找出已删除但未释放的文件

# 查找被删除但还被占用的文件
lsof +L1

# 输出类似:
# COMMAND    PID USER   FD   TYPE DEVICE SIZE/NODE NODE NAME
# java     12345 root    1w  REG  253,1 21474836480  1234567 /var/log/app.log (deleted)

lsof +L1 查找所有已删除但还被占用的文件(links < 1)。输出显示 java 进程占用了一个 20G 的已删除文件。

第二步:确认文件大小

# 查看被删除文件的大小
ls -lh /proc/12345/fd/1

# 或者
ls -l /proc/12345/fd/ | grep deleted

# 输出:
# l-wx------ 1 root root 64 Jul 20 02:00 1 -> /var/log/app.log (deleted)

确认了,/var/log/app.log 被删除了,但 java 进程还持有它的句柄,占着 20G 空间。

第三步:释放空间

有两个方案:

方案一:重启进程(推荐)

# 重启 java 进程,释放文件句柄
systemctl restart java-app

重启后,已删除的文件句柄被释放,磁盘空间回来了。

方案二:不重启,清空文件内容

# 找到被删除文件的文件描述符
ls -l /proc/12345/fd/ | grep deleted
# 输出:1 -> /var/log/app.log (deleted)

# 清空文件内容(不删除文件,只清空)
> /proc/12345/fd/1

这个方法不需要重启进程,但坑在于:只能清空当前内容,进程继续写入还是会占用空间。

验证修复

# 重启后检查
df -h
# Filesystem      Size  Used Avail Use% Mounted on
# /dev/vda1        40G   20G   18G  53% /

# 确认没有已删除未释放的文件
lsof +L1
# 无输出

空间回来了,从 100% 降到 53%。

预防措施

1. 配置日志轮转

# /etc/logrotate.d/app
/var/log/app.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
}

copytruncate 会先复制再清空,避免进程持有已删除文件的句柄。

2. 监控磁盘使用率

# 添加 crontab 定期检查
*/5 * * * * df -h / | awk 'NR==2{print $5}' | sed 's/%//' | awk '$1>80{print "磁盘使用率超过80%: "$1"%"}' | mail -s "磁盘告警" admin@example.com

3. 清理大文件

# 找出大于 100M 的文件
find / -type f -size +100M -exec ls -lh {} \; 2>/dev/null

# 找出大于 7 天的日志文件
find /var/log -type f -name "*.log" -mtime +7 -exec ls -lh {} \;

踩坑总结

  1. dfdu 结果不一致,通常是有已删除未释放的文件
  2. lsof +L1 可以找出已删除但还被占用的文件
  3. 重启进程可以释放文件句柄
  4. 配置日志轮转(logrotate)可以预防此类问题
  5. 定期监控磁盘使用率,设置告警阈值

这个问题的坑在于:df 说满了,du 说没满。理解了 Linux 的文件删除机制,排查就很清晰了。

标签: CentOS, Linux

添加新评论