概述
凌晨三点,你被告警吵醒——线上数据库写入失败,Nginx 起不来,SSH 登录卡顿。登上服务器一查,df -h 显示根分区 100%。这种事每个运维都遇到过,磁盘满大概是生产环境最高频的故障类型之一,根据实际运维统计,磁盘相关告警占比约 15%-20%。
但磁盘满不是一个简单问题。df -h 看到的 100% 可能并不是全部真相:
- inode 耗尽:磁盘空间还有剩余,但文件系统inode用完了,照样报
No space left on device。这种问题更隐蔽,排查难度也更大。 - 已删除文件未释放:
rm删了大文件,df显示空间没变。因为还有进程持有文件描述符,数据块并没有真正释放。 - 预留空间:ext4 默认预留 5% 空间给 root,400GB 的数据盘就有 20GB 被"藏"起来了。
这篇文章把磁盘空间和 inode 管理的排查思路、清理策略、预防机制一次说透。所有命令都在 Ubuntu 22.04 和 CentOS 7.9 上实测过,能直接拿去用。
磁盘空间的两个维度:blocks 与 inode
很多人对磁盘空间的理解只停留在"还剩多少 GB"这个层面。但 Linux 文件系统管理的是两个独立资源:数据块(blocks) 和 索引节点(inode)。
打个比方:文件系统像一个停车场。blocks 是停车位,inode 是停车记录。停车场可能还有空车位(blocks 没满),但记录本写满了(inode 耗尽),新来的车也停不进去。
inode 是什么
inode(Index Node,索引节点)是文件系统里描述文件元数据的数据结构。每个文件或目录对应一个 inode,存储了以下信息:
| 元数据字段 | 说明 |
|---|---|
| 文件类型 | 普通文件、目录、符号链接等 |
| 权限信息 | rwx 权限位 |
| 所有者 | uid 和 gid |
| 文件大小 | 字节数 |
| 时间戳 | atime / mtime / ctime |
| 数据块指针 | 指向实际数据存储位置的索引 |
| 硬链接数 | 指向该 inode 的目录项数量 |
注意一个关键点:inode 不存储文件名。文件名存在目录项(dentry)中,目录项将文件名映射到 inode 编号。这也是为什么硬链接能存在——多个文件名可以指向同一个 inode。
inode 总数在文件系统创建时就固定了。ext4 默认每 16KB 空间分配一个 inode,也就是说一个 100GB 的分区大约有 655 万个 inode。对于存大文件来说足够了,但如果你的系统上堆了几百万个小文件(比如邮件队列、日志碎片、Session 文件),inode 就可能先于 blocks 耗尽。
xfs 和 ext4 的 inode 策略差异
ext4 的 inode 数量在 mkfs 时固定,后期无法调整。如果创建时没预估好,后面只能重新格式化。
xfs 的策略不同——它采用动态 inode 分配。当需要新 inode 时,xfs 会自动从空闲空间中分配。这意味着 xfs 理论上不会遇到"inode 耗尽但空间还有"的问题。但代价是 xfs 的 inode 不像 ext4 那样有固定大小,某些需要大 inode 的场景(如存储大量 ACL)可能受限。
# 查看文件系统类型
df -T
# 查看 ext4 的 inode 配置
tune2fs -l /dev/sda1 | grep -i "inode\|block count\|block size"
# 查看 xfs 的 inode 信息
xfs_info /dev/sda1 | grep -i "imax\|inodes"
实际案例:inode 耗尽但空间充足
某天收到告警,应用报 No space left on device。登上服务器:
$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 20G 30G 40% /
$ df -i /
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 3276800 3276800 0 100% /
磁盘空间剩 30GB,但 inode 100% 用完了。这种矛盾现象在 Linux 系统中并不罕见,根因往往是大量小文件堆积(参考:Linux 磁盘明明有空间,却报 No space left on device)。
排查第一步:快速确认磁盘状态
收到磁盘告警后,第一件事不是删文件,而是搞清楚到底什么满了。
同时检查 blocks 和 inode
# 查看磁盘空间使用情况
df -h
# 查看 inode 使用情况(关键!)
df -ih
df -ih 加了 -h 参数,以人类可读的格式显示 inode 使用率。如果只看 df -h 不看 df -i,很容易漏掉 inode 耗尽的问题。
典型输出:
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 100G 95G 5.0G 95% /
/dev/sdb1 500G 120G 380G 24% /data
tmpfs 7.8G 1.2M 7.8G 1% /run
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 6.5M 6.5M 0 100% /
/dev/sdb1 3.2M 125K 3.1M 1% /data
tmpfs 512K 105 512K 1% /run
上面这个输出说明根分区同时存在空间不足和 inode 耗尽两个问题。
找出空间占用最大的目录
# 从根目录逐级排查
du -h --max-depth=1 / 2>/dev/null | sort -rh | head -20
# 针对特定目录深入排查
du -h --max-depth=1 /var 2>/dev/null | sort -rh | head -20
这条命令的原理:du 统计每个一级子目录的大小,sort -rh 按人类可读格式逆序排列,head -20 只看前 20 个。通过逐级深入,通常 3-4 次就能定位到罪魁祸首。
找出 inode 占用最多的目录
# 统计各目录下的文件数量
find / -xdev -type f 2>/dev/null | \
awk -F/ '{print "/"$2}' | \
sort | uniq -c | sort -rn | head -20
或者用更直观的方式,逐级排查:
# 查看根目录下每个一级目录的文件数
for d in /*; do
[ -d "$d" ] && echo "$(find "$d" -xdev 2>/dev/null | wc -l) $d"
done | sort -rn | head -10
实际生产中,inode 杀手通常是这几类:
| 场景 | 典型目录 | 特征 |
|---|---|---|
| 日志碎片 | /var/log | 大量 KB 级小文件 |
| 邮件队列 | /var/spool/postfix | 数万到数百万个邮件文件 |
| Session 文件 | /tmp、/var/lib/php/sessions | 海量会话文件 |
| Docker 层 | /var/lib/docker/overlay2 | 容器镜像层和容器文件系统 |
| 定时任务残留 | /var/spool/cron | cron 执行残留的临时文件 |
已删除文件未释放:最隐蔽的坑
这是磁盘排查中最经典的坑之一。用 rm 删除了一个大文件,但 df 显示空间没有释放。原因是有进程还持有这个文件的文件描述符(参考:Linux 磁盘空间满了怎么办?超详细排查指南)。
原理
Linux 文件系统中,文件的数据块在所有持有它的文件描述符都关闭后才会真正释放。rm 只是从目录项中删除了文件名到 inode 的映射,但如果某个进程还在通过文件描述符读写这个文件,inode 和数据块就不会被回收。
这就像你把书架上书的标签撕了(rm),但书还放在那里(数据块还在),直到有人把它拿走(文件描述符关闭)才会真正腾出位置。
定位已删除但未释放的文件
# 列出已删除但仍被进程占用的文件
lsof +L1 2>/dev/null
# 或者用更精确的方式
lsof -nP | grep -i "(deleted)"
输出示例:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 1234 root 8w REG 8,1 10G 12345 /var/log/nginx/access.log (deleted)
java 5678 app 42w REG 8,1 5G 12346 /opt/app/logs/gc.log (deleted)
从输出可以看到,nginx 进程(PID 1234)还持有 /var/log/nginx/access.log 的写句柄,这个文件已经被删了但 10GB 的空间没释放。java 进程也类似,占着 5GB 的 GC 日志。
释放空间的三种方式
方式一:重启进程(推荐)
# 平滑重启 nginx
nginx -s reload
# 重启 java 应用
systemctl restart app
重启后进程释放文件描述符,空间立即回收。这是最安全的方式。
方式二:清空文件内容(不重启进程)
如果进程不能重启(比如生产环境的数据库),可以通过 /proc 清空文件内容:
# 通过 /proc 文件系统清空文件
cat /dev/null > /proc/1234/fd/8
其中 1234 是 PID,8 是文件描述符编号(来自 lsof 输出的 FD 列,去掉字母后缀)。
这个操作的效果等同于 > /var/log/nginx/access.log,但不需要文件名——直接通过 fd 操作。文件内容被清空,空间释放,但文件描述符保持打开状态,进程不会报错。
方式三:使用 truncate 命令
# 通过 /proc 截断文件
truncate -s 0 /proc/1234/fd/8
效果和 cat /dev/null > 一样,但语法更清晰。
注意:千万不要直接
kill -9进程来释放文件描述符。在生产环境中,强杀进程可能导致数据损坏或服务中断。优先使用优雅重启或/proc方式。
ext4 预留空间:被"藏"起来的 5%
ext4 文件系统默认预留 5% 的空间给 root 用户。这个设计是为了防止普通用户把磁盘写满后,root 用户连登录都登不了——因为登录需要写 /var/log/wtmp 等文件。在根分区上这个设计很合理,但在纯数据盘上就是浪费。
查看预留空间
# 查看预留块比例
tune2fs -l /dev/sda1 | grep "Reserved block count"
# 输出示例:
# Reserved block count: 2621440
# Block size: 4096
# 预留空间 = 2621440 * 4096 / 1024 / 1024 / 1024 ≈ 10GB
调整预留空间
# 将预留比例设为 1%(数据盘推荐)
tune2fs -m 1 /dev/sdb1
# 完全取消预留(仅用于临时盘或测试盘)
tune2fs -m 0 /dev/sdb1
# 指定预留块数(精确控制)
tune2fs -r 0 /dev/sdb1
400GB 的数据盘从 5% 调到 1%,能多出 16GB 可用空间。如果是纯日志盘或备份盘,设为 0% 也没问题——上面跑不了用户进程,不存在 root 登不了的风险。
xfs 文件系统没有这个预留机制,所以不需要调整。
日志清理:磁盘满的头号元凶
生产环境磁盘满,根因统计下来大概率是日志。Nginx access log、Java GC log、应用业务日志——这些文件持续增长,如果不管控迟早出事。
手动清理策略
# 清空日志文件(保留文件本身,只清内容)
cat /dev/null > /var/log/nginx/access.log
# 删除 7 天前的日志文件
find /var/log -name "*.log" -mtime +7 -delete
# 删除超过 100MB 的日志文件
find /var/log -name "*.log" -size +100M -delete
# 压缩 3 天前的日志
find /var/log -name "*.log" -mtime +3 -exec gzip {} \;
cat /dev/null > 和 rm 的区别:前者清空文件内容但保留文件本身,正在写入的进程不会报错;后者删除文件,进程的下次写入会触发重建(如果配置了的话)或报错。对于正在运行的服务的日志文件,优先用清空方式。
logrotate:生产级日志管理
手动清理是临时手段,长期方案是配置 logrotate。logrotate 是 Linux 自带的日志轮转工具,能自动按大小或时间切割、压缩、删除旧日志。
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily # 每天轮转
rotate 14 # 保留 14 天
compress # 压缩旧日志
delaycompress # 延迟一天压缩(防止和当前写入冲突)
missingok # 文件不存在不报错
notifempty # 空文件不轮转
create 644 nginx adm # 轮转后创建新文件
sharedscripts # 多个文件匹配时脚本只执行一次
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 $(cat /var/run/nginx.pid)
fi
endscript
}
关键参数解释:
| 参数 | 作用 | 推荐值 |
|---|---|---|
daily/weekly/monthly | 轮转频率 | 根据日志量选择 |
rotate N | 保留份数 | 7-30 天 |
compress | gzip 压缩旧日志 | 必开 |
delaycompress | 延迟一天压缩 | 防止和当前写入冲突 |
size 500M | 按大小轮转 | 大流量日志推荐 |
copytruncate | 复制后截断原文件 | 不支持 postrotate 的场景 |
copytruncate 和 create 的选择是关键。create 模式会创建新文件,需要通过 postrotate 通知进程重新打开日志(比如 nginx 的 kill -USR1)。copytruncate 模式直接把当前日志复制一份然后清空原文件,进程不需要重启也不需要信号——但代价是复制和清空之间有一个极短的时间窗口可能丢日志。
对于不能发信号的服务(比如某些 Java 应用),用 copytruncate:
# /etc/logrotate.d/myapp
/opt/app/logs/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
copytruncate
size 500M
}
测试 logrotate 配置
# 调试模式(不实际执行,只显示会做什么)
logrotate -d /etc/logrotate.d/nginx
# 强制执行
logrotate -f /etc/logrotate.d/nginx
Docker 磁盘清理
Docker 是另一个磁盘空间大户。镜像层、容器可写层、日志、Volume,每一层都可能堆积 GB 级数据。
快速诊断
# 查看 Docker 整体磁盘使用
docker system df
# 查看详细信息
docker system df -v
输出示例:
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 45 12 52.3GB 38.1GB (72%)
Containers 23 8 1.2GB 800MB (66%)
Local Volumes 15 10 8.5GB 2.3GB (27%)
Build Cache 0 0 0B 0B
RECLAIMABLE 列就是可回收的空间。上面这个例子有 38GB 镜像和 800MB 容器数据可以清理。
清理命令
# 一键清理:停止的容器 + 无用网络 + 悬空镜像 + 构建缓存
docker system prune
# 更彻底:包括未被容器引用的镜像
docker system prune -a
# 清理未被使用的 Volume(谨慎!确认没有需要保留的数据)
docker volume prune
# 清理构建缓存
docker builder prune
# 只清理悬空镜像(<none> 标签的)
docker image prune
清理容器日志
Docker 容器的日志默认存在 /var/lib/docker/containers/<id>/<id>-json.log,如果不限制大小,一个高流量容器的日志能吃掉几十 GB。
// /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
这个配置限制每个容器的日志文件最大 100MB,保留 3 个文件。修改后需要重启 Docker:
systemctl restart docker
注意:
daemon.json的日志限制只对新建的容器生效。已有容器需要重建才能应用新配置。如果急需清理某个容器的日志,可以直接清空对应文件:cat /dev/null > /var/lib/docker/containers/<container-id>/<container-id>-json.log
自动化磁盘巡检脚本
手动排查是基本功,但生产环境不能靠人肉巡检。下面是一个可以直接用的磁盘巡检脚本,配合 cron 定期执行,超过阈值就告警。
#!/bin/bash
# disk-check.sh - 磁盘空间与 inode 巡检脚本
# 用法: ./disk-check.sh [阈值百分比,默认85]
THRESHOLD=${1:-85}
HOSTNAME=$(hostname)
ALERT_LIST=""
# 检查磁盘空间
while read -r line; do
USAGE=$(echo "$line" | awk '{print $5}' | tr -d '%')
MOUNT=$(echo "$line" | awk '{print $6}')
if [ "$USAGE" -ge "$THRESHOLD" ] 2>/dev/null; then
ALERT_LIST="${ALERT_LIST}磁盘空间: ${MOUNT} 使用率 ${USAGE}%\n"
fi
done < <(df -h | grep -v "^Filesystem" | grep -v "tmpfs")
# 检查 inode 使用率
while read -r line; do
USAGE=$(echo "$line" | awk '{print $5}' | tr -d '%')
MOUNT=$(echo "$line" | awk '{print $6}')
if [ "$USAGE" -ge "$THRESHOLD" ] 2>/dev/null; then
ALERT_LIST="${ALERT_LIST}inode: ${MOUNT} 使用率 ${USAGE}%\n"
fi
done < <(df -ih | grep -v "^Filesystem" | grep -v "tmpfs")
# 检查已删除未释放的大文件
DELETED_FILES=$(lsof +L1 2>/dev/null | awk 'NR>1 && $7+0 > 104857600 {print $1, $2, $7/1024/1024 "MB", $NF}')
if [ -n "$DELETED_FILES" ]; then
ALERT_LIST="${ALERT_LIST}已删除未释放文件:\n${DELETED_FILES}\n"
fi
# 输出告警
if [ -n "$ALERT_LIST" ]; then
echo -e "[磁盘巡检告警] ${HOSTNAME}\n"
echo -e "$ALERT_LIST"
# 这里接入告警通道(企业微信/钉钉/邮件等)
# send_alert "$ALERT_LIST"
exit 1
else
echo "[磁盘巡检] ${HOSTNAME} 正常"
exit 0
fi
配合 cron 使用:
# 每天上午 9 点巡检
0 9 * * * /opt/scripts/disk-check.sh 85 >> /var/log/disk-check.log 2>&1
# 每小时检查一次(适合磁盘增长快的环境)
0 * * * * /opt/scripts/disk-check.sh 90 >> /var/log/disk-check.log 2>&1
inode 恢复:inode 耗尽后怎么办
inode 耗尽比磁盘空间满更棘手,因为不能简单删除文件——你可能连 touch 一个临时文件都做不到。
紧急恢复步骤
# 第一步:找出文件最多的目录
for d in /*; do
[ -d "$d" ] && echo "$(find "$d" -xdev 2>/dev/null | wc -l) $d"
done | sort -rn | head -10
# 第二步:深入到子目录,逐级定位
# 假设 /var 文件最多,继续往下查
for d in /var/*; do
[ -d "$d" ] && echo "$(find "$d" -xdev 2>/dev/null | wc -l) $d"
done | sort -rn | head -10
# 第三步:批量删除小文件
# 先确认文件数量和类型
find /var/log/app -type f -name "*.tmp" | wc -l
# 删除(先用 -print 确认,再换 -delete)
find /var/log/app -type f -name "*.tmp" -delete
高效批量删除大量文件
当文件数量达到百万级时,rm -rf 可能会因为参数过长而失败,或者耗时极长。更高效的方式:
# 方法一:find -delete(推荐,最快)
find /path/to/dir -type f -delete
# 方法二:find + xargs(适合需要额外处理时)
find /path/to/dir -type f -print0 | xargs -0 rm -f
# 方法三:rsync 清空目录(性能极好)
mkdir /tmp/empty_dir
rsync -a --delete /tmp/empty_dir/ /path/to/dir/
实测在 500 万文件的场景下,find -delete 比 rm -rf 快 3-5 倍,rsync --delete 更快,因为它不走 unlink 系统调用,直接在文件系统层面操作。
长期方案:调整 inode 数量
如果业务确实需要存储大量小文件,可以在创建文件系统时调整 inode 比例:
# ext4:每 4KB 空间分配一个 inode(默认 16KB)
mkfs.ext4 -i 4096 /dev/sdb1
# 或者指定每 2KB 一个 inode(更激进)
mkfs.ext4 -i 2048 /dev/sdb1
# 查看调整效果
tune2fs -l /dev/sdb1 | grep "Inode count"
代价是每个 inode 占用 256 字节,inode 数量翻倍意味着元数据开销翻倍。对于大文件存储场景不划算,需要根据实际文件大小分布权衡。
注意:ext4 的 inode 数量在
mkfs时固定,事后无法调整。如果已经格式化好的分区 inode 不够,只能重新格式化或迁移数据。xfs 支持动态 inode 分配,不存在这个问题。
监控体系:Prometheus + node_exporter
光靠巡检脚本还不够,需要一个持续监控的体系。Prometheus + node_exporter 是事实上的标准方案。
关键监控指标
| 指标 | 说明 | PromQL |
|---|---|---|
| 磁盘空间使用率 | 各挂载点空间占比 | 100 - (node_filesystem_avail_bytes / node_filesystem_size_bytes * 100) |
| inode 使用率 | 各挂载点 inode 占比 | 100 - (node_filesystem_files_free / node_filesystem_files * 100) |
| 磁盘写入速率 | 每秒写入字节数 | rate(node_disk_written_bytes_total[5m]) |
| 已删除文件大小 | 通过自定义 exporter | 需要自定义脚本 |
告警规则
# disk-alerts.yml
groups:
- name: disk_alerts
rules:
# 磁盘空间使用率 > 85%
- alert: DiskSpaceHigh
expr: |
100 - (node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"}
/ node_filesystem_size_bytes{fstype!~"tmpfs|overlay"} * 100) > 85
for: 5m
labels:
severity: warning
annotations:
summary: "磁盘空间使用率高: {{ $labels.mountpoint }}"
description: "{{ $labels.instance }} 的 {{ $labels.mountpoint }} 使用率 {{ $value }}%"
# 磁盘空间使用率 > 95%(紧急)
- alert: DiskSpaceCritical
expr: |
100 - (node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"}
/ node_filesystem_size_bytes{fstype!~"tmpfs|overlay"} * 100) > 95
for: 2m
labels:
severity: critical
annotations:
summary: "磁盘空间严重不足: {{ $labels.mountpoint }}"
description: "{{ $labels.instance }} 的 {{ $labels.mountpoint }} 使用率 {{ $value }}%"
# inode 使用率 > 85%
- alert: InodeUsageHigh
expr: |
100 - (node_filesystem_files_free{fstype!~"tmpfs|overlay"}
/ node_filesystem_files{fstype!~"tmpfs|overlay"} * 100) > 85
for: 5m
labels:
severity: warning
annotations:
summary: "inode 使用率高: {{ $labels.mountpoint }}"
description: "{{ $labels.instance }} 的 {{ $labels.mountpoint }} inode 使用率 {{ $value }}%"
实测发现,inode 使用率告警比磁盘空间告警更容易被忽略。很多团队只配了磁盘空间告警,没有配 inode 告警,等出了问题才发现监控盲区。别踩这个坑——两个维度都要监控。
常见问题速查表
把生产环境高频问题整理成速查表,遇到问题时按图索骥:
| 现象 | 可能原因 | 排查命令 | 解决方案 |
|---|---|---|---|
df -h 显示 100% | 日志/临时文件堆积 | `du -h –max-depth=1 / | sort -rh` |
df -h 有空间但报 No space | inode 耗尽 | df -ih | 清理小文件 |
rm 后空间未释放 | 进程持有文件描述符 | lsof +L1 | 重启进程或 cat /dev/null > /proc/PID/fd/N |
| 磁盘使用率突增 | Docker 镜像/日志 | docker system df | docker system prune -a |
| 根分区空间被预留 | ext4 5% 预留 | tune2fs -l /dev/sdX | grep Reserved | tune2fs -m 1 /dev/sdX |
| 应用写日志失败 | 磁盘满或 inode 耗尽 | df -h && df -ih | 清理空间或增加 inode |
| SSH 登录卡顿 | 磁盘满导致写 wtmp 失败 | df -h / | 清理根分区 |
生产环境磁盘管理清单
基于实际运维经验,整理一份生产环境磁盘管理清单,建议逐项落实:
监控层面:
- 同时监控 blocks 和 inode 使用率,阈值设为 85% 告警、95% 紧急告警
- 监控磁盘写入速率突变(可能预示日志暴涨或异常)
- 配置已删除未释放文件的定期检查
日志管理:
- 所有日志服务配置 logrotate,按天或按大小轮转
- Docker 容器配置
daemon.json日志限制(max-size + max-file) - 应用日志按天切割,压缩保留 7-30 天
清理策略:
- 定期执行
docker system prune清理无用镜像 - 临时文件目录配置 tmpwatch 或 systemd-tmpfiles 自动清理
- 数据库 binlog 配置过期时间(如 MySQL
expire_logs_days = 7)
容量规划:
- 数据盘使用 ext4 时根据文件大小分布调整 inode 比例
- 大量小文件场景优先使用 xfs(动态 inode 分配)
- 预留 15%-20% 的空间余量,不要等到满了再处理
总结
磁盘空间管理看着简单,实际坑不少。总结几条实战经验:
排查要全面。df -h 只是第一步,必须同时查 df -ih。inode 耗尽的表现和磁盘满一样,但排查思路完全不同。别只看一个维度就下结论。
删文件不等于释放空间。有进程持有的文件删了也不释放,这个坑踩过的人都印象深刻。lsof +L1 应该成为磁盘排查的标配命令。
预防胜于治疗。logrotate、Docker 日志限制、Prometheus 监控——这些配置在出事之前花十分钟搞定,能避免凌晨三点被叫起来。生产环境最大的风险不是技术难度,而是"知道该做但没做"。
inode 要提前规划。ext4 的 inode 数量创建后不可改,格式化前就要预估。大量小文件场景用 xfs 更省心。这个决策做错了,后面改起来代价很大。
最后一条:磁盘满了不要慌。先 df -h 看空间、df -ih 看 inode、lsof +L1 看幽灵文件、du 定位大目录——四步走完,问题基本就定位了。
参考资料与致谢
本文在撰写过程中参考了以下资料,感谢原作者的贡献:
- Linux 磁盘空间满了怎么办?超详细排查指南 — 腾讯云开发者社区,提供了磁盘空间排查的系统化流程和已删除文件未释放的原理分析
- Linux 磁盘明明有空间,却报 No space left on device — CSDN,详细解析了 inode 耗尽的诊断方法和 inode 机制原理
- Linux 文件系统深度解析:ext4、XFS、inode、硬链接 vs 软链接 — CSDN,提供了 ext4 块组布局和 xfs 动态 inode 分配机制的深入分析
- 记录一次 inode 耗尽造成的生产事故 — CSDN,真实生产事故复盘,展示了 inode 耗尽的定位过程和 logrotate 治理方案