当前位置:首页>Linux>Linux 运维排障终极指南:磁盘管理与实战案例

Linux 运维排障终极指南:磁盘管理与实战案例

  • 2026-09-10 09:35:40
Linux 运维排障终极指南:磁盘管理与实战案例

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 核心区别

命令
数据来源
统计范围
特点
df
文件系统元数据 (superblock)
整个文件系统的块使用情况
站在文件系统角度,反映真实磁盘占用
du
遍历目录树,累加文件大小
指定路径下的所有文件
站在文件角度,反映可见文件总大小

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已使用
  • • inode表:连续的inode结构数组
  • • 数据块:真正存放文件内容的块

3.3 inode 耗尽故障

当文件系统中有大量小文件时,可能 inode 用完而数据块仍有剩余,导致无法创建新文件。

查看 inode 使用情况:

df -i

解决方案:

  • • 删除大量小文件
  • • 若无法删除,备份后重新格式化文件系统,创建时指定更多的 inode(通过 -i bytes-per-inode 或 -N 选项)

四、Docker 磁盘爆炸事故

4.1 典型场景

Docker 默认将数据存储在 /var/lib/docker,长时间运行后磁盘可能被以下内容占满:

  • • 悬空镜像(dangling images)
  • • 未使用的容器( exited 状态的容器)
  • • 匿名卷
  • • 容器日志(json-file 驱动,未轮转)
  • • overlay2 层累积

4.2 排查步骤

  1. 1. 查看磁盘占用概况
    df -h
    du
     -sh /var/lib/docker
  2. 2. 深入分析 Docker 目录
    du -sh /var/lib/docker/* | sort -hr

    重点关注:containers(每个容器的日志、配置)、overlay2(镜像层)、volumes、image。

  3. 3. 使用 Docker 内置命令检查资源
    docker system df          # 显示 docker 磁盘使用情况
    docker system df -v       # 更详细信息,包括每个镜像、容器的大小

4.3 清理方案

4.3.1 自动清理

docker system prune -a -f --volumes
  • • -a:删除所有未使用的镜像(不仅仅是悬空)
  • • --volumes:删除未使用的卷
  • • -f:强制,无需确认

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 日志轮转
  • • 使用单独的磁盘或分区存储 Docker 数据(可挂载到 /var/lib/docker 的大分区)

五、Kubernetes 磁盘清理方案

5.1 问题来源

K8s 节点磁盘压力主要来自:

  • • 容器镜像(旧版本、悬空)
  • • 停止的容器(含退出状态的容器)
  • • 容器日志(未轮转/未清理)
  • • emptyDir 卷
  • • 系统组件日志(kubelet、容器运行时)
  • • 可驱逐的节点压力(磁盘使用率超阈值)

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 永久解决方案

  1. 1. 节点磁盘扩容:增加磁盘容量。
  2. 2. 使用单独的磁盘存放容器数据:挂载大容量磁盘到 /var/lib/docker 或 /var/lib/containerd。
  3. 3. 启用镜像回收策略:设置合理的垃圾回收阈值。
  4. 4. 定期执行清理任务:编写 cronjob 或使用 Operator 自动清理。
  5. 5. 使用分布式文件系统或云存储:将日志、数据卷持久化到外部存储,减少节点本地磁盘压力。
  6. 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
  • • 了解系统日志位置:/var/log/
  • • 具备 root 权限或 sudo 权限

7.2 场景一:根分区 / 使用率 100%

现象:系统报错“No space left on device”,服务异常。

排查过程:

  1. 1. 确认分区占用:
    df -h /
  2. 2. 找出根下的大目录:
    cd /
    du
     -sh --exclude=/proc --exclude=/sys --exclude=/dev * 2>/dev/null | sort -hr | head -20
  3. 3. 发现 /var/log 占用巨大,进入 /var/log:
    cd /var/log
    du
     -sh * | sort -hr | head -10
  4. 4. 发现 messages 和 syslog 文件过大,查看是否被进程占用:
    lsof | grep messages
  5. 5. 清空日志文件:
    > messages
    > syslog
  6. 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%。

排查:

  1. 1. 确认哪个分区 inode 耗尽:df -i
  2. 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. 3. 发现 /var/spool/postfix/maildrop 有数百万小文件(常见于邮件队列积压)。
  4. 4. 清空该目录:
    rm -rf /var/spool/postfix/maildrop/*
    # 若文件太多,使用 find 删除:

    find /var/spool/postfix/maildrop -type f -delete
  5. 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. 1. 登录节点,检查磁盘使用:
    df -h
    df
     -i
  2. 2. 分析容器运行时占用:
    # 如果是 docker
    docker system df
    # 如果是 containerd

    crictl images
  3. 3. 清理节点:
    • • 清理退出的容器:docker container prune -f 或 crictl rm ...
    • • 清理未使用的镜像:docker image prune -a -f 或 crictl rmi --prune
    • • 清理容器日志(见上文)
  4. 4. 如果清理后仍然 DiskPressure,检查 kubelet 垃圾回收配置,适当降低阈值(如 --image-gc-high-threshold=80)。
  5. 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 排查工具

工具
作用
iostat
查看磁盘 IO
iotop
查看哪个进程在写磁盘
vmstat
查看 IO wait
dstat
综合系统资源
pidstat
进程级 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%

指标解释:

指标
含义
r/s
每秒读取次数
w/s
每秒写入次数
await
IO等待时间
svctm
IO服务时间
%util
磁盘利用率

判断标准:

%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

参数:

值
含义
1
清理 PageCache
2
清理 inode/dentry
3
全部

⚠️ 生产环境 不建议频繁执行


十一、Linux I/O 调度器

Linux 使用 IO Scheduler 来优化磁盘读写。

查看:

cat /sys/block/sda/queue/scheduler

示例:

[mq-deadline] kyber bfq none

11.1 常见调度器

调度器
特点
noop
简单队列
deadline
低延迟
cfq
公平调度
mq-deadline
多队列
bfq
桌面优化

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 快

十三、企业级磁盘监控体系

生产环境必须监控:

指标
告警
磁盘使用率
80%
inode 使用率
80%
IO util
90%
IO await
30ms

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% 来自以下几类:

问题
占比
日志无限增长
40%
Docker 数据
25%
inode 耗尽
10%
deleted 文件
10%
IO 打满
10%
其他
5%

Linux 运维黄金排障流程

磁盘满

df -h
df -i

↓
du -sh

↓
lsof deleted

↓
docker system df

↓
iostat
iotop

最新文章

随机文章