Linux 运维排障终极指南:磁盘管理与实战案例
一、Linux 磁盘结构原理图
1.1 物理视角:从硬盘到文件系统
+-----------------------------------------------------------------------------------+
| 物理硬盘 |
| +-------------+-------------+-------------+-------------+-------------+ |
| | 分区表 | 分区1 | 分区2 | 分区3 | 扩展分区 | |
| | (MBR/GPT) | (sda1) | (sda2) | (sda3) | (sda4) | |
| +-------------+-------------+-------------+-------------+-------------+ |
| | | | | | |
| v v v v v |
| +----------+ +----------+ +----------+ +----------+ +------------------+ |
| | 引导扇区 | | 块组0 | | 块组1 | | 块组2 | | 逻辑卷管理(LVM) | |
| | | | (ext4) | | (ext4) | | (ext4) | | +-----+-----+-----+ | |
| +----------+ +----------+ +----------+ +----------+ | LV1 | LV2 | ... | |
| +-----+-----+-----+ |
+-----------------------------------------------------------------------------------+
↓
+-------------------+
| 文件系统层 |
| (VFS虚拟文件系统) |
+-------------------+
↓
+-------------------------------------------------------------------------------+
| inode 与数据块 |
| +-------------+ +-------------+ +-------------+ +-------------+ |
| | inode表 |---->| 直接块指针 |---->| 数据块 | | 间接块指针 | |
| | (元数据) | | 间接块指针 |---->| 数据块 | | 双重间接 | |
| | 权限/大小/ | | 双重间接 |---->| 数据块 | | 块指针 | |
| | 时间戳/链接 | | 块指针 | | | | | |
| +-------------+ +-------------+ +-------------+ +-------------+ |
+-------------------------------------------------------------------------------+
1.2 逻辑视角:进程与磁盘交互
用户进程 → 系统调用 (open/read/write) → VFS (虚拟文件系统) → 具体文件系统 (ext4/xfs)
→ 块设备层 → I/O调度 → 磁盘驱动 → 物理磁盘
二、df 与 du 原理解析
2.1 核心区别
2.2 原理详解
- • df 读取超级块中的块总数和空闲块数,计算已用块数。它统计的是文件系统层面的块占用,包括:
- • 文件系统元数据 (inode表、位图、日志等)
- • 已删除但仍有进程打开的文件(这类文件不会出现在du中,但占用的块仍被标记为使用,直到进程关闭)
- • du 通过遍历目录,对每个文件调用
stat 获取其占用的块数(或字节数),然后累加。它只统计有文件名关联的数据块。不会包含元数据占用,也不会包含已删除但未释放的文件。
2.3 常见场景:df 显示满,du 显示很小
原因:有进程打开了某个已删除的大文件,该文件占用的块尚未释放。
排查命令:
lsof | grep deleted # 查找已删除但仍在使用的文件
找到对应的进程后,重启该进程或 kill 掉,磁盘空间才会真正释放。
三、inode 工作机制图
3.1 inode 结构
inode 结构 (通常 128/256 字节)
+-----------------------------------+
| 文件所有者 (owner) |
| 文件权限 (mode) |
| 文件大小 (size) |
| 时间戳 (atime/mtime/ctime) |
| 链接计数 (link count) |
| 数据块指针数组 (15个指针) |
| - 12个直接指针 → 直接指向数据块 |
| - 1个间接指针 → 指向一个块,该块包含指向数据块的指针 |
| - 1个双重间接指针 |
| - 1个三重间接指针 |
| 扩展属性 (xattr) (可选) |
+-----------------------------------+
3.2 文件系统布局 (以 ext4 为例)
+--------------------------------------------------------------------------+
| 引导块 | 块组0 | 块组1 | ... | 块组N |
+--------------------------------------------------------------------------+
|
v
+--------------------------------------------------------------------------------+
| 超级块 | 块组描述符表 | 预留GDT | 数据块位图 | inode位图 | inode表 | 数据块 |
+--------------------------------------------------------------------------------+
- • 超级块:文件系统全局信息(块大小、总数、空闲块、inode总数等)
- • inode位图:记录块组中哪些inode已使用
3.3 inode 耗尽故障
当文件系统中有大量小文件时,可能 inode 用完而数据块仍有剩余,导致无法创建新文件。
查看 inode 使用情况:
df -i
解决方案:
- • 若无法删除,备份后重新格式化文件系统,创建时指定更多的 inode(通过
-i bytes-per-inode 或 -N 选项)
四、Docker 磁盘爆炸事故
4.1 典型场景
Docker 默认将数据存储在 /var/lib/docker,长时间运行后磁盘可能被以下内容占满:
4.2 排查步骤
- 1. 查看磁盘占用概况
df -h
du -sh /var/lib/docker
- 2. 深入分析 Docker 目录
du -sh /var/lib/docker/* | sort -hr
重点关注:containers(每个容器的日志、配置)、overlay2(镜像层)、volumes、image。
- 3. 使用 Docker 内置命令检查资源
docker system df # 显示 docker 磁盘使用情况
docker system df -v # 更详细信息,包括每个镜像、容器的大小
4.3 清理方案
4.3.1 自动清理
docker system prune -a -f --volumes
4.3.2 手动清理常见占用
- • 清理日志(容器日志默认无限增长)
# 查看容器日志大小
ls -lh /var/lib/docker/containers/*/*-json.log
# 清空日志(或设置轮转)
truncate -s 0 /var/lib/docker/containers/*/*-json.log
更优方案:配置 Docker daemon 日志轮转(/etc/docker/daemon.json):
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
- • 清理 overlay2 层(一般由
docker system prune 处理,但如果容器已删除但层未释放,可以检查 docker image ls 和 docker container ls -a) - • 清理悬空卷
docker volume ls -f dangling=true
docker volume prune
- • 限制容器日志大小(运行容器时指定)
docker run -d --log-opt max-size=10m --log-opt max-file=3 ...
4.4 预防措施
- • 定期执行
docker system prune - • 监控
/var/lib/docker 目录使用率 - • 使用单独的磁盘或分区存储 Docker 数据(可挂载到
/var/lib/docker 的大分区)
五、Kubernetes 磁盘清理方案
5.1 问题来源
K8s 节点磁盘压力主要来自:
5.2 节点级清理策略
5.2.1 Kubelet 垃圾回收机制
Kubelet 内置容器和镜像垃圾回收,可通过 kubelet 配置调整:
- • 容器垃圾回收:
--minimum-container-ttl-duration 和 --maximum-dead-containers-per-container - • 镜像垃圾回收:
--image-gc-high-threshold(默认85%)和 --image-gc-low-threshold(默认80%)
若希望手动触发,可在节点上执行:
# 清理退出的容器
docker container prune -f # 或 containerd 对应命令
# 清理悬空镜像
docker image prune -a -f
5.2.2 使用 K8s 原生工具
- • kubectl delete pods 删除 pod 时,emptyDir 会被清理,但容器和镜像可能仍需 GC 回收。
- • 节点压力驱逐:当节点磁盘不足时,kubelet 会根据服务质量驱逐 pod,优先驱逐 BestEffort 和 Burstable pod。
5.3 主动清理步骤
5.3.1 查看节点磁盘使用情况
kubectl describe node <node-name> | grep -A 5 "Conditions"
kubectl top node
登录节点执行 df -h、du -sh /var/lib/docker(或 containerd 目录)。
5.3.2 清理容器运行时
Docker 运行时:
# 清理所有停止的容器
docker container prune -f
# 清理未使用的镜像(包括 dangling 和未使用)
docker image prune -a -f
# 清理所有未使用资源(容器、网络、镜像、构建缓存)
docker system prune -a -f
containerd 运行时:
# 使用 crictl(K8s 推荐)
crictl rmi --prune # 删除未使用的镜像
crictl rm $(crictl pods -q) # 谨慎使用,通常不会直接操作
# 或使用 nerdctl(类似 docker)
nerdctl system prune -a -f
5.3.3 清理容器日志
每个容器的日志默认在 /var/log/pods 或 /var/lib/docker/containers,需要手动轮转或清理。
方案一:cronjob 定期清理
# 清空超过7天的容器日志文件
find /var/log/pods -name "*.log" -mtime +7 -delete
方案二:配置节点日志轮转(推荐)
- • 对于使用 journald 的节点,配置 journald 限制:
journalctl --vacuum-size=500M # 保留 500M
- • 对于容器日志,建议配置容器运行时日志轮转(如 docker daemon 的 max-size/max-file),或使用 logrotate 服务。
方案三:使用 DaemonSet 自动清理日志(如 fluentd 或 logrotate 容器)
5.3.4 清理 kubelet 垃圾回收遗漏的镜像
有时镜像被标记为“已使用”但实际上未被任何容器引用,可以手动删除:
# 列出所有镜像,根据 REPOSITORY 和 TAG 判断
docker images
# 删除指定镜像
docker rmi <image-id>
5.4 永久解决方案
- 2. 使用单独的磁盘存放容器数据:挂载大容量磁盘到
/var/lib/docker 或 /var/lib/containerd。 - 4. 定期执行清理任务:编写 cronjob 或使用 Operator 自动清理。
- 5. 使用分布式文件系统或云存储:将日志、数据卷持久化到外部存储,减少节点本地磁盘压力。
- 6. 监控告警:对节点磁盘使用率设置告警(如 >80% 警告,>85% 驱逐)。
六、完整运维排障流程图
以下是 Linux 磁盘空间不足时的标准排障流程图(文本描述 + 字符示意图):
[开始] 发现告警:磁盘空间不足
│
▼
[步骤1] 确认问题范围
├─ 执行 `df -h` 查看各分区使用率
├─ 执行 `df -i` 检查 inode 是否耗尽
└─ 确定是哪个分区(如 /、/var、/home)空间满
│
▼
[步骤2] 定位大文件/大目录
├─ 进入问题分区,执行 `du -sh /* 2>/dev/null | sort -hr | head -20` 找出最大目录
├─ 逐层深入,使用 `du -sh * | sort -hr` 定位具体大文件
└─ 结合 `ls -lh` 查看可疑文件(如日志、core dump、容器数据)
│
▼
[步骤3] 特殊情况排查
├─ 若 df 显示满但 du 统计很小 → 检查已删除但被进程占用的文件:`lsof | grep deleted`
├─ 若 inode 耗尽 → 检查大量小文件目录,如 `/var/spool/postfix/maildrop`、`/tmp` 等
└─ 若为 Docker 节点 → 执行 `docker system df` 分析 Docker 占用
│
▼
[步骤4] 根据原因采取清理措施
├─ 删除无用大文件:`rm -f /path/to/bigfile`
├─ 清空日志文件:`> /var/log/xxx.log` 或 `truncate -s 0`
├─ 清理已删除但被占用的文件:重启相关进程或 kill 进程
├─ 清理 inode:删除大量小文件或重新格式化
├─ Docker/K8s 清理:`docker system prune` / `crictl rmi --prune`
└─ 日志轮转配置
│
▼
[步骤5] 验证空间是否释放
├─ 再次执行 `df -h` 确认使用率下降
└─ 监控一段时间,确保未再次快速飙升
│
▼
[步骤6] 根因分析与长期措施
├─ 分析为何磁盘会满:日志未轮转?应用生成大文件?监控缺失?
├─ 配置日志轮转(logrotate)
├─ 设置磁盘配额或报警阈值
├─ 如果是容器环境,调整镜像/容器回收策略
└─ 考虑扩容或迁移数据
│
▼
[结束]
七、实战指南:从零开始排查 Linux 磁盘故障
7.1 准备工作
- • 熟练使用命令:
df、du、lsof、find、sort、head、grep
7.2 场景一:根分区 / 使用率 100%
现象:系统报错“No space left on device”,服务异常。
排查过程:
- 2. 找出根下的大目录:
cd /
du -sh --exclude=/proc --exclude=/sys --exclude=/dev * 2>/dev/null | sort -hr | head -20
- 3. 发现 /var/log 占用巨大,进入 /var/log:
cd /var/log
du -sh * | sort -hr | head -10
- 4. 发现 messages 和 syslog 文件过大,查看是否被进程占用:
lsof | grep messages
- 5. 清空日志文件:
> messages
> syslog
- 6. 配置 logrotate(如果未配置):
cat /etc/logrotate.d/syslog
# 确保配置了 daily、rotate 7、compress 等
7.3 场景二:df 显示 /var 满,但 du 统计很小
现象:df -h 显示 /var 使用率 95%,但 du -sh /var 只有 10G(实际分区 100G)。
排查:
lsof | grep deleted
输出中寻找 /var 下的文件,例如:
rsyslogd 1234 root 5w REG 253,0 8589934592 1234567 /var/log/messages (deleted)
解决:重启 rsyslog 服务
systemctl restart rsyslog
7.4 场景三:inode 耗尽
现象:df -h 显示空间还有,但无法创建文件,提示“No space left on device”。df -i 显示 inode 使用率 100%。
排查:
- 2. 查找该分区下文件数最多的目录:
find /var -xdev -type f | cut -d/ -f1-3 | sort | uniq -c | sort -nr | head -10
或使用:
for i in /var/*; do echo "$i: $(find $i -type f | wc -l)"; done | sort -nr -k2
- 3. 发现 /var/spool/postfix/maildrop 有数百万小文件(常见于邮件队列积压)。
- 4. 清空该目录:
rm -rf /var/spool/postfix/maildrop/*
# 若文件太多,使用 find 删除:
find /var/spool/postfix/maildrop -type f -delete
- 5. 解决根本原因:检查 postfix 为何产生大量邮件,或禁用 postfix。
7.5 场景四:Docker 磁盘爆炸
现象:/var/lib/docker 占满磁盘。
排查与清理:
# 查看 docker 使用概况
docker system df
# 进入 docker 目录查看具体占用
du -sh /var/lib/docker/* | sort -hr
# 常见大目录:
# - /var/lib/docker/containers: 每个容器的日志文件
# - /var/lib/docker/overlay2: 镜像层
# - /var/lib/docker/volumes: 卷数据
# 清理容器日志
find /var/lib/docker/containers -name "*-json.log" -exec truncate -s 0 {} \;
# 清理未使用的 docker 资源
docker system prune -a -f --volumes
7.6 场景五:Kubernetes 节点磁盘压力
现象:kubectl describe node 显示 DiskPressure 或 MemoryPressure。
排查:
- 1. 登录节点,检查磁盘使用:
df -h
df -i
- 2. 分析容器运行时占用:
# 如果是 docker
docker system df
# 如果是 containerd
crictl images
- • 清理退出的容器:
docker container prune -f 或 crictl rm ... - • 清理未使用的镜像:
docker image prune -a -f 或 crictl rmi --prune
- 4. 如果清理后仍然 DiskPressure,检查 kubelet 垃圾回收配置,适当降低阈值(如
--image-gc-high-threshold=80)。 - 5. 长期方案:为节点配置自动清理脚本(如 cron 执行清理命令),或部署 node-problem-detector 和 descheduler。
八、总结与最佳实践
- • 定期监控:使用 Prometheus + Grafana 监控磁盘使用率、inode 使用率,设置告警。
- • 日志轮转:配置 logrotate 或容器日志轮转,防止日志无限增长。
- • 容器环境:定期执行
docker system prune,配置 Docker 日志驱动限制大小。 - • K8s 环境:合理设置 kubelet 垃圾回收参数,使用 emptyDir 时注意容量限制,考虑使用持久卷。
- • 应急工具:熟练使用
lsof | grep deleted 处理隐藏占用,使用 ncdu 交互式分析磁盘。 - • 根因分析:每次故障后,记录根本原因,改进监控和预防措施。
九、Linux 磁盘 I/O 性能排查
很多生产事故并不是磁盘满,而是 磁盘 I/O 被打满。
例如:
磁盘空间:还有 300G
服务却极慢
原因通常是:
IO wait
9.1 Linux I/O 架构
+---------------------------------------------------+
| 用户应用 |
| (MySQL / Java / Redis) |
+---------------------------------------------------+
|
v
+---------------------------------------------------+
| VFS (虚拟文件系统) |
+---------------------------------------------------+
|
v
+---------------------------------------------------+
| Page Cache (页缓存) |
+---------------------------------------------------+
|
v
+---------------------------------------------------+
| 文件系统 (ext4 / xfs / btrfs) |
+---------------------------------------------------+
|
v
+---------------------------------------------------+
| Block Layer (块设备层) |
+---------------------------------------------------+
|
v
+---------------------------------------------------+
| I/O Scheduler (IO调度器) |
+---------------------------------------------------+
|
v
+---------------------------------------------------+
| Device Driver |
+---------------------------------------------------+
|
v
+---------------------------------------------------+
| 物理磁盘 SSD/HDD |
+---------------------------------------------------+
9.2 IO 排查工具
9.3 iostat 实战
安装:
apt install sysstat
查看 IO:
iostat -x 1
示例输出:
Device: r/s w/s await svctm %util
sda 0.5 120 80ms 5ms 99%
指标解释:
判断标准:
%util > 90% 磁盘打满
await > 50ms IO延迟高
9.4 iotop 找出罪魁祸首
运行:
iotop
示例:
PID USER DISK READ DISK WRITE COMMAND
2341 root 0.00 B/s 120.00 M/s java
说明:
Java 程序疯狂写磁盘
十、Linux Page Cache 原理
很多人会误以为:
内存被吃光
实际上是 Page Cache。
10.1 Page Cache 架构
应用程序
│
▼
Page Cache
│
▼
磁盘
读取流程:
第一次读取
磁盘 → 内存缓存
第二次读取
直接内存
性能提升:
100x+
10.2 查看缓存
free -h
示例:
Mem: 32G
used: 10G
buff/cache: 17G
说明:
17G 是缓存
不是占用
10.3 释放缓存(调试)
sync
echo 3 > /proc/sys/vm/drop_caches
参数:
⚠️ 生产环境 不建议频繁执行
十一、Linux I/O 调度器
Linux 使用 IO Scheduler 来优化磁盘读写。
查看:
cat /sys/block/sda/queue/scheduler
示例:
[mq-deadline] kyber bfq none
11.1 常见调度器
11.2 SSD 推荐配置
NVMe / SSD:
none
修改:
echo none > /sys/block/nvme0n1/queue/scheduler
十二、超大文件定位神器:ncdu
du 在超大目录非常慢。
推荐:
ncdu
安装:
apt install ncdu
运行:
ncdu /
界面:
/var
20G log
10G lib
5G cache
特点:
交互式
可删除
比 du 快
十三、企业级磁盘监控体系
生产环境必须监控:
13.1 Prometheus 监控
node_exporter 指标:
node_filesystem_size_bytes
node_filesystem_free_bytes
node_filesystem_files_free
磁盘使用率计算:
(1 - free / size) * 100
Grafana 展示:
Disk Usage %
十四、真实生产事故复盘
事故一:Docker 日志打爆磁盘
环境:
Kubernetes
SpringBoot
Docker
现象:
磁盘 500G
3小时写满
排查:
df -h
发现:
/var/lib/docker
继续:
du -sh /var/lib/docker/*
发现:
containers
继续:
ls -lh
结果:
container.log 300G
原因:
debug 日志
解决
配置 docker 日志限制:
/etc/docker/daemon.json
{
"log-driver":"json-file",
"log-opts":{
"max-size":"100m",
"max-file":"3"
}
}
十五、Linux 删除超大文件技巧
如果日志:
10GB+
直接删除:
rm big.log
可能很慢。
推荐:
方法1
truncate -s 0 big.log
方法2
: > big.log
方法3
cat /dev/null > big.log
优点:
秒级释放
十六、企业级磁盘自动清理脚本
自动清理脚本:
#!/bin/bash
echo "start clean disk"
docker system prune -af
journalctl --vacuum-time=7d
find /var/log -name "*.log" -size +500M -exec truncate -s 0 {} \;
echo "clean finished"
加入 cron:
0 3 * * * /opt/clean_disk.sh
十七、运维排障口诀
生产环境必背:
磁盘满
df 看分区
du 找目录
du 不准
lsof | grep deleted
文件建不了
df -i
容器节点
docker system df
IO 很慢
iostat + iotop
找大文件
ncdu
十八、企业级磁盘架构设计
大型系统通常这样设计磁盘:
服务器
NVMe SSD
├── /
├── /var
└── /var/lib/docker
SSD
└── /data
对象存储
└── 日志归档
优点:
系统盘
数据盘
日志分离
最终总结
Linux 磁盘问题 80% 来自以下几类:
Linux 运维黄金排障流程
磁盘满
df -h
df -i
↓
du -sh
↓
lsof deleted
↓
docker system df
↓
iostat
iotop