Ubuntu 磁盘占用异常排查实录df 显示 1.5TB,但 du 只有 749GB,最终定位 soffice.bin“幽灵文件”

一、背景

在一台 Ubuntu 服务器上遇到磁盘占用困惑:

  • 业务侧感觉磁盘"莫名其妙"变大
  • du 统计目录大小时,看起来"没那么多"
  • df 却显示根分区已经实打实占用了 TB 级空间

这类问题非常典型,尤其常见于文档转换服务(LibreOffice/unoconv)、日志系统、长时间运行的 Java/Go 服务

本文记录一次真实排查过程,从现象到最终根因定位,并给出"终极治理方案"。

二、现象

1)用 find 找大文件

bash 复制代码
sudo find / -type f -size +1G -printf "%s\t%p\n" 2>/dev/null | sort -nr | head -50

输出中出现了:

  • /proc/kcore 巨大
  • Ollama 模型 blobs
  • NLLB/MT5 模型
  • /swapfile

2)用 du 看一级目录

bash 复制代码
sudo du -h -d 1 / 2>/dev/null | sort -hr

你得到类似:

复制代码
749G    /
665G    /data
68G     /usr
8.0G    /var
...

看起来合计合理,似乎没有 1.5TB 的量级。

3)用 df 看真实占用

bash 复制代码
df -h

关键行:

复制代码
/dev/nvme0n1p2  3.7T  1.5T  2.1T   42% /

疑点出现:

  • du 汇总感知 ~749G
  • df 真实已用却是 1.5T
  • 差额接近 700G+

三、关键认知:du 与 df 统计口径不同

  • du:统计目录树能"看到"的文件大小
  • df:统计文件系统真实占用空间

当出现差额巨大时,最常见原因就是:

文件被删除了,但仍被某个进程打开(句柄未释放)。

这类文件对 du 来说不可见,但对 df 来说仍占空间。

典型表现为 "幽灵占用"

四、排查第一坑:/proc/kcore

find 里常见的"吓人第一名":

复制代码
/proc/kcore

它是内核虚拟内存映像:

  • 看起来 TB 级
  • 不占真实磁盘
  • 无需处理

以后找大文件建议排除:

bash 复制代码
sudo find / -path /proc -prune -o -type f -size +1G -printf "%s\t%p\n" 2>/dev/null | sort -nr | head -50

五、真正的对账姿势:du 对齐 df

为了避免统计跨文件系统导致误差,使用 -x

bash 复制代码
sudo du -xhd 1 / 2>/dev/null | sort -hr

-x 意味着:

  • 只统计根分区同一文件系统
  • 便于和 df -h / 直接对应

六、核心排查命令:lsof +L1

当怀疑"幽灵文件"时,直接上:

bash 复制代码
sudo lsof +L1

这会列出:

  • link count = 0
  • 但仍被进程打开的文件

七、关键发现:soffice.bin 持有超大 deleted 临时文件

实际输出中出现大量:

复制代码
soffice.b ... /tmp/lo_job_xxx/luXXXX.tmp (deleted)

并且大小惊人,例如:

  • 298464378880(约 278GB)
  • 93539921920(约 87GB)
  • 55G+53G+ 多个

这些加总:

完全足以解释 df 与 du 的 TB 级差额。

此时可以下结论:

✅ 根因不是磁盘"乱涨",而是LibreOffice headless 转换进程异常残留导致的句柄占用。

八、现场止血方案(立刻见效)

这类 (deleted) 文件不用 rm,删不掉也没意义。

唯一有效动作:结束持有它的进程

1)温和结束 specific PID

bash 复制代码
sudo kill -TERM <pid1> <pid2> ...

2)一键结束当前用户的 soffice

bash 复制代码
pkill -u tanji -f soffice.bin

3)必要时强制

bash 复制代码
sudo kill -KILL <pid>

4)验证

bash 复制代码
df -h /
sudo lsof +L1 | head -50

一般会看到 根分区已用空间明显下降

九、为什么会发生?

在文档转换链路中,常见模式是:

  • 服务调用 unoconvlibreoffice --headless
  • 高并发或异常超时
  • 产生大量 /tmp/lo_job_* 临时文件
  • 清理逻辑提前删除文件
  • soffice 仍持有句柄
  • 导致磁盘"隐形被吃掉"

尤其当你做:

  • 邮件附件解析
  • Office → PDF
  • 批量归档/索引

这种问题概率很高。

十、终极治理方案(建议你直接落地)

目标是把 LibreOffice 转换从:

"随手起进程 + 默认 /tmp + 无超时"

升级为:

"受控 Worker + 强制超时 + 独立 TMPDIR + 独立 UserInstallation + 并发闸门 + 兜底清场"

方案 1:每个任务使用独立 TMPDIR + 独立 Profile + timeout

推荐模板:

bash 复制代码
JOB_TMP=/data/tmp/lo_job_$(date +%s)_$$
mkdir -p "$JOB_TMP"

timeout 120s \
  env TMPDIR="$JOB_TMP" \
  libreoffice --headless --nologo --nofirststartwizard --norestore \
  -env:UserInstallation="file://$JOB_TMP/profile" \
  --convert-to pdf --outdir "$JOB_TMP/out" "/path/to/input.docx"

rm -rf "$JOB_TMP"

三剑合璧:

  • timeout:超时必杀
  • TMPDIR:远离系统 /tmp
  • UserInstallation:隔离 profile,避免锁与缓存膨胀

方案 2:限制并发(强烈建议)

无论 Go/Java:

  • 全局 semaphore 限制 2~4 并发
  • 避免同时起几十个 soffice

方案 3:兜底清场脚本 + 定时任务

/usr/local/bin/lo-janitor.sh(示例)

bash 复制代码
#!/usr/bin/env bash
set -euo pipefail

THRESH=$((5*1024*1024*1024)) # 5GB

sudo lsof -nP +L1 2>/dev/null \
| awk '
  $1 ~ /soffice/ && $7 == 0 {
    size=$5; pid=$2; name=$9;
    if (size ~ /^[0-9]+$/) print pid, size, name
  }
' \
| while read -r pid size name; do
    if [ "$size" -gt "$THRESH" ]; then
      echo "Killing soffice pid=$pid size=$size name=$name"
      sudo kill -TERM "$pid" || true
      sleep 2
      sudo kill -KILL "$pid" || true
    fi
  done

赋权:

bash 复制代码
sudo chmod +x /usr/local/bin/lo-janitor.sh

用 cron 每 5 分钟跑:

bash 复制代码
sudo crontab -e

加入:

bash 复制代码
*/5 * * * * /usr/local/bin/lo-janitor.sh >/var/log/lo-janitor.log 2>&1

十一、经验总结

1)遇到"df 大、du 小"

优先怀疑:

  • deleted-but-open 文件
  • 快照(btrfs/zfs)
  • 容器层(docker overlay)

2)三板斧顺序

bash 复制代码
df -h /
sudo du -xhd 1 / 2>/dev/null | sort -hr
sudo lsof +L1

90% 的"神秘磁盘膨胀"都能快速定位。

3)文档转换服务的正确姿势

  • 独立任务临时目录
  • 独立 LibreOffice profile
  • 强制超时
  • 并发控制
  • 守护清场

十二、结语

这次排查的本质不是"磁盘真的莫名其妙变大",

而是soffice.bin 持有已删除的大型临时文件,形成了典型的"幽灵占用"。

只要把转换链路工程化治理:

你就能从"频繁救火"进入"长期稳定"。

相关推荐
Dragon online2 小时前
数据分析师成长之路--从SQL恐惧到数据掌控者的蜕变
数据库·sql
Joren的学习记录2 小时前
【Linux运维进阶知识】Nginx负载均衡
linux·运维·nginx
用户2190326527352 小时前
Java后端必须的Docker 部署 Redis 集群完整指南
linux·后端
胡先生不姓胡2 小时前
如何获取跨系统调用的函数调用栈
linux
VX:Fegn08952 小时前
计算机毕业设计|基于springboot + vue音乐管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
一招定胜负3 小时前
navicat连接数据库&mysql常见语句及操作
数据库·mysql
热心市民蟹不肉3 小时前
黑盒漏洞扫描(三)
数据库·redis·安全·缓存
chian_ocean3 小时前
openEuler集群 Chrony 时间同步实战:从零构建高精度分布式时钟体系
数据库
Databend3 小时前
构建海量记忆:基于 Databend 的 2C Agent 平台 | 沉浸式翻译 @ Databend meetup 上海站回顾及思考
数据库
αSIM0V4 小时前
数据库期末重点
数据库·软件工程