FAQ-11:磁盘空间不足(no space left on device)
预计阅读时间:19 分钟
📖 目录
问题速查表
| 问题 | 解决章节 |
|---|---|
| 磁盘块空间被日志、缓存等占满 | 原因分析、清理系统缓存和临时文件 |
| df -h 显示有空间但仍无法写入(inode 耗尽) | 第三步:检查 inode 耗尽情况、处理 inode 耗尽 |
| 已删除但未释放的文件占用空间 | 第四步:查找已删除但未释放的文件 |
| 多用户环境下配额限制无法写入 | 第五步:检查磁盘配额 |
| 文件系统损坏导致写入失败 | 第六步:检查文件系统是否损坏 |
| 如何快速找到大文件并清理 | 第二步:找到空间占用大户、查找并删除大文件 |
No space left on device 是 Linux 系统中最令人紧张的错误之一。当系统无法在磁盘上创建新文件或写入数据时,就会报出这个错误。它可能出现在安装软件包、写入日志、保存文件、甚至启动服务时,严重时会导致系统完全无法正常工作。
值得注意的是,磁盘空间不足不一定意味着物理磁盘真的被文件占满了。Linux 文件系统有两个关键限制:磁盘块(block)空间和 inode 数量。即使磁盘还有剩余空间,如果 inode 耗尽(通常由于存在海量小文件),系统同样无法创建新文件。此外,文件系统损坏、磁盘配额限制、以及已删除但未释放的文件(被进程持有)也是常见但容易被忽视的原因。
在生产环境中,磁盘空间不足可能导致数据库崩溃、日志写入失败、备份中断、Web 应用返回 500 错误等一系列连锁反应。对于运行中的服务器,紧急清理需要兼顾业务连续性,不能盲目删除文件。下面从排查到解决逐步展开。
原因分析
磁盘块空间耗尽是最直接的原因。日志文件持续增长、应用程序产生大量临时文件、软件包缓存未清理、Docker 镜像和容器层不断累积,都会逐渐吞噬磁盘空间。
inode 耗尽是一种更隐蔽的情况。df -h 可能显示磁盘还有大量可用空间,但 df -i 会显示 inode 已用尽。inode 是文件系统中存储文件元数据的数据结构,每个文件(包括空文件和目录)都占用一个 inode。当系统中存在大量小文件时(如邮件队列、PHP session 文件、node_modules 等),inode 可能先于磁盘块空间被耗尽。
已删除但未释放的文件是另一个常见原因。当一个文件被删除但仍有进程在使用它时,磁盘空间不会被释放。这种情况在日志文件和服务输出中尤为常见。使用 lsof 可以找到这些被占用的已删除文件。
磁盘配额限制在多用户环境中可能导致特定用户无法写入,即使磁盘整体仍有空间。使用 quota 命令可以检查当前用户的配额使用情况。
排查决策树
磁盘空间不足的排查方向取决于报错的形态,先跑三个诊断命令,再按下面的决策树分流:
写入失败:No space left on device
│
├─ ① 先看 df -h 块空间是否已满
│ ├─ Use% = 100% → 块空间耗尽 → du 逐层定位大目录
│ │ └─ 清理日志 / 缓存 / 大文件(见解决方案)
│ └─ Use% 正常(如 60%)→ 进入 ②
│
├─ ② 再看 df -i 是否 inode 耗尽
│ ├─ IUse% = 100% → 海量小文件占满 inode
│ │ └─ find 定位小文件密集目录 → 清理
│ └─ IUse% 正常 → 进入 ③
│
├─ ③ 查找已删除但未释放的文件
│ │ lsof +L1 / lsof | grep '(deleted)'
│ │ ├─ 有大文件 → 重启持有该文件的进程即可释放
│ │ └─ 无 → 进入 ④
│
└─ ④ 检查配额与文件系统状态
├─ quota -s(多用户环境是否撞配额)
├─ mount | grep " / "(是否被只读重新挂载)
└─ dumpe2fs -h | grep state(文件系统是否损坏)
排查步骤
第一步:快速确认磁盘整体使用情况
# 查看各分区空间使用率
df -h
# 关注 Use% 列,接近或达到 100% 的分区需要处理
# 同时查看 inode 使用率
df -i
# 如果 IUse% 接近 100%,即使 df -h 显示有空间也无法创建新文件
第二步:找到空间占用大户
# 查看一级目录大小(从根目录开始)
du -h --max-depth=1 / 2>/dev/null | sort -hr | head -10
# 逐层深入占用最大的目录
du -h --max-depth=1 /var | sort -hr | head -10
du -h --max-depth=1 /var/log | sort -hr | head -10
# 查找大于 100MB 的文件
find / -xdev -type f -size +100M -exec ls -lh {} \; 2>/dev/null | sort -k5 -hr | head -20
# 交互式浏览(强烈推荐)
sudo apt install ncdu
ncdu /
第三步:检查 inode 耗尽情况
# 查看 inode 使用率
df -i
# 找到 inode 使用最多的目录
# 需要逐目录检查,通常问题出在 /var/mail、/var/spool、/tmp 等
find / -xdev -printf '%h\n' 2>/dev/null | sort | uniq -c | sort -rn | head -10
第四步:查找已删除但未释放的文件
# 查找被进程占用的已删除文件
lsof 2>/dev/null | grep '(deleted)' | awk '{print $7, $9, $1, $2}' | sort -rn | head -20
# 简化写法:只看被删除文件的大小
sudo lsof +L1 2>/dev/null | awk 'NR>1{print $7, $9}' | sort -rn | head -20
如果发现有大文件被删除但仍被进程占用,重启该进程即可释放空间,无需重启整个系统。
第五步:检查磁盘配额
# 查看当前用户配额
quota -s
# 查看所有用户配额
sudo repquota -a
# 如果没有启用 quota,检查是否因为文件系统挂载选项限制
mount | grep " / "
第六步:检查文件系统是否损坏
# 检查文件系统状态
dumpe2fs -h /dev/sda1 | grep "Filesystem state"
# 正常应显示:clean
# 如果显示 filesystems with errors,需要在卸载后修复
# 注意:不能修复正在使用的根分区,需要从 Live CD 启动
解决方案
清理系统缓存和临时文件
# 清理 apt 包缓存
sudo apt clean
sudo apt autoremove
# 清理 systemd 日志(保留最近 500MB)
sudo journalctl --vacuum-size=500M
# 或者保留最近 7 天
sudo journalctl --vacuum-time=7d
# 清理 Docker 资源(镜像、容器、网络、构建缓存)
docker system prune -af
# 更彻底:同时清除 volumes
docker system prune -af --volumes
# 清理旧内核(Ubuntu/Debian)
sudo apt autoremove --purge
# 清理临时文件
sudo rm -rf /tmp/*
sudo rm -rf /var/tmp/*
# 清理用户缓存
rm -rf ~/.cache/thumbnails/*
rm -rf ~/.cache/mozilla/firefox/*/Cache*
查找并删除大文件
# 找到最大的日志文件
find /var/log -type f -name "*.log" -exec ls -lh {} \; 2>/dev/null | sort -k5 -hr | head -10
# 压缩大日志文件(而不是直接删除)
sudo gzip /var/log/syslog.1
# 清理已轮转的旧日志
sudo find /var/log -name "*.gz" -mtime +30 -delete
sudo find /var/log -name "*.[1-9]" -mtime +30 -delete
处理 inode 耗尽
# 查找小文件最多的目录
find / -xdev -printf '%h\n' 2>/dev/null | sort | uniq -c | sort -rn | head -5
# 常见问题点及清理方法
# 1. 清理邮件队列
sudo postsuper -d ALL
# 2. 清理 PHP session 文件
sudo rm -rf /var/lib/php/sessions/*
# 3. 清理 Node.js 模块缓存
find / -name "node_modules" -type d -exec rm -rf {} + 2>/dev/null
# 4. 清理系统临时文件
sudo find /tmp -type f -atime +7 -delete
sudo find /var/tmp -type f -atime +7 -delete
# 5. 清理 APT 缓存中的小文件
sudo apt clean
# 6. 清理 systemd journal 中的大量小文件
sudo journalctl --vacuum-files=10
真实案例
案例 A:日志文件被删除但进程仍持有,4.3GB 空间"隐形"占用
现象:应用开始报 No space left on device,写入全部失败:
$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 50G 36K 100% /
$ du -sh /usr /var /home /opt /tmp 2>/dev/null
4.2G /usr
2.1G /var
... # 所有目录加起来远小于 50G,明显有隐藏占用
排查过程:目录实际大小与 df 数值对不上,高度怀疑"已删除但未释放"的文件,用 lsof 确认:
$ sudo lsof +L1 | awk 'NR>1{print $7, $10, $1, $2}' | sort -rn | head -5
4379812864 deleted java 18234
512294912 deleted nginx 3561
根因:Java 应用(PID 18234)以追加模式打开了日志文件,logrotate 轮转删除该文件后进程从未关闭文件描述符,4.3GB 空间被一直占用。文件在磁盘上无目录项(所以 du 看不到),但空间直到进程退出才会释放。
修复:重启持有文件的进程即可,无需重启整个系统:
$ sudo systemctl restart myapp
$ sudo lsof +L1 | wc -l # 确认 deleted 文件已全部释放
0
$ df -h /
/dev/sda1 50G 43G 6.7G 87% /
验证:df 显示空间恢复,应用日志正常写入。后续把 logrotate 配置改为 copytruncate 模式,让 Java 这类不主动重开文件描述符的服务也能正确轮转,从根源上避免再次出现隐形占用。
案例 B:inode 耗尽——df -h 明明有空间,却一个文件都建不了
现象:应用批量报错,服务器上连临时文件都创建失败:
$ df -h /data
Filesystem Size Used Avail Use% Mounted on
/dev/sdb1 500G 120G 380G 24% /data
$ df -i /data
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sdb1 3276800 3276800 0 100% /data
排查过程:df -h 明明还有 380G 可用,但 df -i 显示 inode 100%。定位小文件密集目录:
$ find /data -xdev -printf '%h\n' 2>/dev/null | sort | uniq -c | sort -rn | head -3
2843612 /data/app/sessions
123456 /data/app/cache
根因:应用把每个用户会话存成一个独立小文件,累计 284 万个小文件提前耗尽了文件系统 inode 上限(数据量只有 120G,文件数量却已触顶)。ext4 默认每个 inode 占 256 字节,这个分区格式化时分配的 inode 数量不足以支撑这种"海量小文件"业务。
修复:先清理过期会话恢复服务,再改造存储方式:
$ find /data/app/sessions -type f -mtime +30 -delete # 清理 30 天前的会话
$ df -i /data # 确认 inode 释放
/dev/sdb1 3276800 1954321 1322479 60% /data
验证:inode 使用率回落到 60%,应用恢复写入。长期方案:会话改存 Redis、限制 session 保留天数、定期清理脚本入 cron;若业务必然产生海量小文件,则在新分区格式化时用 mkfs.ext4 -N 8000000 提高 inode 数量上限。
案例 C:Docker 镜像和构建缓存吃掉 20GB 磁盘空间
现象:CI 服务器频繁构建 Docker 镜像,半年后磁盘满了,docker system df 显示镜像和缓存占用 20GB。
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 156 12 18.5GB 16.2GB (87%)
Containers 45 3 1.2GB 980MB (81%)
Local Volumes 23 5 850MB 620MB (72%)
Build Cache 312 0 4.8GB 4.8GB (100%)
根因:CI 每次构建都生成新镜像和构建层缓存,但从未清理。悬空镜像(dangling images)和无用构建缓存累积了 20GB。
修复:docker system prune -a --volumes 清理所有未使用的镜像、停止的容器、无用网络和构建缓存。后续在 CI 流水线末尾添加自动清理步骤。
案例 D:日志轮转配置错误导致日志文件无限增长
现象:Web 服务器的访问日志持续增长,logrotate 每天轮转但磁盘空间仍然被耗尽:
$ ls -lh /var/log/nginx/
-rw-r--r-- 1 root root 45G access.log
-rw-r--r-- 1 root root 2.3G access.log.1.gz
-rw-r--r-- 1 root root 1.8G access.log.2.gz
...
-rw-r--r-- 1 root root 890M access.log.30.gz
$ cat /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
postrotate
# 错误:没有真正通知 Nginx 重新打开日志文件
kill -USR1 $(cat /var/run/nginx.pid)
endscript
}
排查过程:logrotate 确实在轮转文件,但旧日志没有被真正释放:
# 检查 Nginx 是否持有已删除的文件
$ sudo lsof +L1 | grep nginx | head -5
nginx 1234 root 5w REG 8,1 48128348160 0 /var/log/nginx/access.log (deleted)
# Nginx 没有收到 USR1 信号,仍在写入旧的文件描述符
根因:logrotate 的 postrotate 脚本使用了错误的 PID 文件路径,kill 命令没有真正执行。Nginx 仍然持有旧文件的文件描述符,导致 45GB 空间无法释放。
修复:
# 修正 logrotate 配置
/var/log/nginx/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
sharedscripts
postrotate
# 正确的信号发送方式
if [ -f /var/run/nginx.pid ]; then
kill -USR1 $(cat /var/run/nginx.pid)
fi
endscript
}
# 手动触发轮转验证
sudo logrotate -f /etc/logrotate.d/nginx
# 确认 Nginx 已切换到新日志文件
$ sudo lsof +L1 | grep nginx
# 应无输出,表示没有 deleted 文件被持有
预防:配置 logrotate 后立即用 logrotate -f 手动触发一次验证。关键服务的 postrotate 脚本添加错误检查,确保信号发送成功。
案例 E:数据库事务日志膨胀导致磁盘写满
现象:MySQL 服务器磁盘空间被耗尽,但 /var/lib/mysql 目录大小远小于 df 报告的使用量:
$ df -h /var
Filesystem Size Used Avail Use%
/dev/sda2 100G 100G 0 100%
$ du -sh /var/lib/mysql/
12G /var/lib/mysql/
# 12G 远小于 100G,大量空间被隐藏占用
$ sudo lsof +L1 | grep mysql | awk '{sum+=$7} END {print sum/1024/1024/1024 " GB"}'
87.5 GB
排查过程:MySQL 持有大量已删除的事务日志文件:
$ sudo lsof +L1 | grep mysql | head -3
mysqld 2345 mysql 45w REG 8,2 46200000000 0 /var/lib/mysql/ib_logfile0 (deleted)
mysqld 2345 mysql 46w REG 8,2 45000000000 0 /var/lib/mysql/ib_logfile1 (deleted)
根因:MySQL InnoDB 的事务日志文件被删除但进程仍持有。这通常发生在手动执行 rm 删除日志文件而非通过 MySQL 配置调整大小时。
修复:
# 方案一:重启 MySQL 释放文件锁(短暂中断服务)
sudo systemctl restart mysql
# 方案二:在 MySQL 中动态调整日志文件大小(推荐)
mysql> SET GLOBAL innodb_log_file_size = '1G';
# 这会触发 MySQL 自动轮转日志文件
# 验证空间释放
$ sudo lsof +L1 | grep mysql | wc -l
0
$ df -h /var
/dev/sda2 100G 12G 88G 12%
预防:不要手动删除 MySQL 的数据文件和日志文件。调整 InnoDB 参数时通过 MySQL 命令操作,让服务自动管理文件生命周期。
案例 F:/tmp 目录被大量临时文件占满导致服务启动失败
现象:系统重启后多个服务无法启动,报错信息包含 "No space left on device" 或 "Permission denied":
# 检查 /tmp 使用情况
$ df -h /tmp
Filesystem Size Used Avail Use%
/dev/sda1 2.0G 2.0G 0 100%
$ ls /tmp | wc -l
89234
# /tmp 被数万个小文件占满
排查过程:
# 查看 /tmp 中的文件分布
$ find /tmp -maxdepth 1 -type d | head -10
/tmp
/tmp/systemd-private-...
/tmp/vmware-root-...
# 找到占用最大的目录
$ du -sh /tmp/*/ 2>/dev/null | sort -hr | head -5
280M /tmp/php-session/
156M /tmp/node-xxx/
89M /tmp/pip-xxx/
根因:PHP 会话文件、Node.js 临时文件、pip 构建缓存累积未清理。系统重启时这些文件不会被自动删除,反复累积后占满分区。
修复:
# 清理 /tmp 中超过 7 天的文件
sudo find /tmp -type f -atime +7 -delete
sudo find /tmp -type d -empty -delete
# 清理特定应用的临时文件
sudo rm -rf /tmp/php-session/*
sudo rm -rf /tmp/node-*
# 设置 systemd-tmpfiles 自动清理
# 编辑 /etc/tmpfiles.d/local.conf
# d /tmp/php-session 1777 root root 7d
sudo systemd-tmpfiles --clean
预防:配置 systemd-tmpfiles 或 cron 任务定期清理 /tmp。考虑将 /tmp 挂载为 tmpfs(内存文件系统),避免占用磁盘空间。
预防措施
- 配置磁盘使用率监控告警(建议 80% 告警、90% 紧急),参考 3.12:系统监控与告警 监控
- 同时监控
df -i的 inode 使用率,同样设置 80%/90% 阈值,避免"有空间但建不了文件"的隐蔽故障 - 配置 logrotate 限制日志文件大小和保留天数,参考 2.7:日志与故障排查 日志管理
- 重要分区使用 LVM 便于在线扩展磁盘空间,参考 rhcsa01 磁盘分区与 LVM
- 定期使用
ncdu /进行可视化磁盘空间巡检,养成主动管理的习惯 - 配置 cron 任务定期清理系统缓存和临时文件
- 为数据库等关键服务配置独立的数据分区,避免数据增长影响系统分区
- 多用户环境启用磁盘配额(quota),限制单用户可用空间,防止一人写满全盘
- 大日志服务的 logrotate 启用
copytruncate,避免进程持有已删除文件导致空间无法释放
进阶排查技巧
使用 ncdu 进行可视化空间分析
ncdu(NCurses Disk Usage)是交互式磁盘分析的利器:
# 安装 ncdu
sudo apt install ncdu # Debian/Ubuntu
sudo yum install ncdu # CentOS/RHEL
# 分析指定目录
ncdu /var/log
# 交互操作:
# ↑/↓ 导航 Enter 进入目录
# g 切换显示模式 d 删除文件
# n 按名称排序 s 按大小排序
# q 退出
使用 inotifywait 实时监控文件增长
对于持续增长的文件,可以实时监控其大小变化:
# 安装 inotify-tools
sudo apt install inotify-tools
# 监控文件大小变化
inotifywait -m -e modify /var/log/app.log --format '%T %w%f %e' --timefmt '%Y-%m-%d %H:%M:%S'
# 监控目录中的文件创建事件
inotifywait -m -e create /var/data/ --format '%T %w%f %e' --timefmt '%Y-%m-%d %H:%M:%S'
使用 fuser 定位占用文件的进程
当需要安全删除被占用的文件时:
# 查找占用特定文件的进程
fuser -v /var/log/app.log
# 强制终止占用文件的进程(谨慎使用)
fuser -k /var/log/app.log
# 查找占用特定端口的进程
fuser -v 3306/tcp
使用 find 批量清理文件
# 删除 30 天前的日志文件
find /var/log -name "*.log.*" -mtime +30 -delete
# 删除大于 100MB 的 core dump 文件
find / -name "core.*" -size +100M -delete 2>/dev/null
# 删除 7 天前的临时文件,排除最近修改的
find /tmp -type f -mtime +7 ! -name ".X*" -delete
# 查找并删除空目录
find /var/cache -type d -empty -delete
常见误区与陷阱
- 不要盲目使用
rm -rf /或类似命令:这会删除整个文件系统,造成不可逆的灾难 - 不要直接删除被进程占用的日志文件:应该先轮转或重启进程释放文件锁
- 不要忽略 inode 耗尽:
df -h显示有空间不代表能创建文件,必须同时检查df -i - 不要在生产环境使用
rm -rf清理大量文件:可能导致 inode 释放缓慢,使用find -delete更安全 - 不要忽略 Docker 的存储增长:容器镜像和构建缓存会持续累积,必须定期清理
故障复现与验证清单
修复磁盘空间问题后,使用以下清单验证:
# 1. 验证磁盘空间释放
df -h
df -i
# 2. 验证大文件已清理
find / -xdev -type f -size +1G -exec ls -lh {} \; 2>/dev/null
# 3. 验证无 deleted 文件占用
lsof +L1 2>/dev/null | wc -l
# 4. 验证服务正常运行
systemctl status 服务名
# 5. 验证日志正常写入
tail -f /var/log/syslog
最佳实践
- 分区策略:
/var、/home、/tmp单独分区,避免日志或用户文件占满根分区导致系统无法启动 - 日志管理:所有服务日志都应接入 logrotate,设置合理的轮转周期和压缩策略,避免单个日志文件无限增长
- Docker 定期清理:容器化环境中,
docker system prune应加入定期维护计划,避免镜像和构建缓存无限累积 - inode 规划:格式化文件系统时根据预期文件数量合理设置 inode 大小(
-N参数),尤其是存储大量小文件的场景 - 容量规划:新部署服务前评估其磁盘需求,预留 20% 以上的安全余量
延伸阅读
- rhcsa01 磁盘分区与 LVM——分区、LVM、fstab
- 2.7:日志与故障排查 日志与故障排查——logrotate、journalctl 清理
- faq07 FAQ:磁盘与文件系统
- inode 详解与文件系统选择指南,参考 rhcsa01 磁盘分区与 LVM
- 3.12:系统监控与告警 系统监控——磁盘使用率告警配置
- Docker 存储驱动与磁盘管理,参考 3.16:Samba/NFS 文件共享 容器技术
- 文件系统类型对比(ext4 vs xfs vs btrfs),参考 2.5:时间管理与日志 磁盘与存储管理
- LVM 在线扩容磁盘空间,参考 2.5:时间管理与日志 磁盘与存储管理
- 系统日志清理与日志轮转配置,参考 2.7:日志与故障排查 日志管理
附录:磁盘空间清理命令速查表
| 清理目标 | 命令 | 预计释放空间 |
|---|---|---|
| APT 包缓存 | sudo apt clean | 数百 MB ~ 数 GB |
| systemd 日志 | sudo journalctl --vacuum-size=500M | 按配置 |
| Docker 未使用资源 | docker system prune -a --volumes | 数 GB ~ 数十 GB |
| 临时文件 | sudo rm -rf /tmp/* | 不定 |
| 旧内核 | sudo apt autoremove --purge | 数百 MB |
| 用户缓存 | rm -rf ~/.cache/* | 数百 MB |
| 邮件队列 | sudo postsuper -d ALL | 不定 |
| Node 模块 | find / -name node_modules -exec rm -rf {} + | 数 GB |
附录:inode 使用率分析命令
# 查看各分区 inode 使用率
df -i
# 找到 inode 使用最多的目录
find / -xdev -printf '%h\n' 2>/dev/null | sort | uniq -c | sort -rn | head -10
# 统计特定目录的文件数量
find /var/mail -type f | wc -l
# 清理小文件密集目录
# 邮件队列
sudo find /var/mail -type f -mtime +30 -delete
# PHP session 文件
sudo find /var/lib/php/sessions -type f -atime +1 -delete
# 临时文件
sudo find /tmp -type f -atime +7 -delete
附录:磁盘监控脚本示例
#!/bin/bash
# 简单的磁盘空间监控脚本
THRESHOLD=80
LOG_FILE="/var/log/disk-monitor.log"
while true; do
# 检查各分区使用率
df -h | awk 'NR>1{print $5, $6}' | while read usage mount; do
usage_num=${usage%\%}
if [ "$usage_num" -ge "$THRESHOLD" ]; then
echo "$(date): WARNING: $mount is ${usage}% full" >> "$LOG_FILE"
# 可以添加邮件告警或 Slack 通知
fi
done
# 检查 inode 使用率
df -i | awk 'NR>1{print $5, $6}' | while read usage mount; do
usage_num=${usage%\%}
if [ "$usage_num" -ge "$THRESHOLD" ]; then
echo "$(date): WARNING: $mount inode usage is ${usage}%" >> "$LOG_FILE"
fi
done
sleep 3600 # 每小时检查一次
done
附录:LVM 磁盘扩容命令
# 查看物理卷
sudo pvs
sudo pvdisplay
# 查看卷组
sudo vgs
sudo vgdisplay
# 查看逻辑卷
sudo lvs
sudo lvdisplay
# 扩展逻辑卷(在线扩容)
sudo lvextend -L +10G /dev/mapper/vg0-root
sudo resize2fs /dev/mapper/vg0-root # ext4
sudo xfs_growfs /dev/mapper/vg0-root # xfs
# 添加新物理卷到卷组
sudo pvcreate /dev/sdb1
sudo vgextend vg0 /dev/sdb1
sudo lvextend -l +100%FREE /dev/mapper/vg0-root
sudo resize2fs /dev/mapper/vg0-root
案例 G:systemd journal 日志文件耗尽 inode
现象:服务器运行一段时间后报 No space left on device,但 df -h 显示磁盘空间充足:
$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 30G 18G 63% /
$ df -i /
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 3276800 3276800 0 100% /
排查过程:inode 100% 但块空间充足,说明存在海量小文件:
$ find / -xdev -printf '%h\n' 2>/dev/null | sort | uniq -c | sort -rn | head -3
3102456 /var/log/journal/remote
128456 /var/log/journal/abc123...
2345 /var/log
根因:systemd-journald 的持久化日志在 /var/log/journal 中为每个系统启动创建大量小文件,长期运行且未配置日志清理策略时,inode 被耗尽。
修复:
# 限制 journal 大小
sudo journalctl --vacuum-size=500M
# 或限制保留天数
sudo journalctl --vacuum-time=30d
# 永久配置:编辑 /etc/systemd/journald.conf
[Journal]
SystemMaxUse=500M
MaxRetentionSec=30day
# 重启 journald 使配置生效
sudo systemctl restart systemd-journald
预防:新系统部署时立即配置 journald.conf 的日志大小和保留策略。定期用 df -i 检查 inode 使用率,设置告警阈值。
案例 H:/var/log/btmp 无限增长耗尽磁盘空间
现象:SSH 暴力破解攻击导致 /var/log/btmp(记录失败登录)文件持续增长,最终磁盘写满:
$ ls -lh /var/log/btmp
-rw-rw---- 1 root utmp 45G Jul 15 10:00 /var/log/btmp
$ du -sh /var/log/btmp
45G /var/log/btmp
# btmp 记录了所有失败的登录尝试
$ lastb | wc -l
2847563
根因:/var/log/btmp 记录所有失败的 SSH 登录尝试。遭受暴力破解攻击时,该文件会快速增长。由于没有配置日志轮转,文件大小不受限制。
修复:
# 清空 btmp 文件
sudo truncate -s 0 /var/log/btmp
# 配置 logrotate 限制 btmp 大小
cat | sudo tee /etc/logrotate.d/btmp << 'EOF'
/var/log/btmp {
monthly
create 0660 root utmp
rotate 1
size 10M
}
EOF
# 安装 fail2ban 防止暴力破解
sudo apt install -y fail2ban
sudo systemctl enable fail2ban
预防:部署服务器时立即配置 fail2ban 或类似的入侵防御工具。将 /var/log/btmp 加入 logrotate 配置,限制文件大小。定期用 lastb | wc -l 检查失败登录数量。