概述

完成用户与权限管理之后,Linux 系统管理进入一个更贴近日常运维现场的主题:文件系统管理。服务器上的应用、日志、数据库、备份、容器镜像、临时文件和用户数据,最终都会落到某个文件系统里。磁盘空间满了、inode 用尽、挂载参数错误、/etc/fstab 写错导致系统无法启动、日志目录占满根分区、业务数据卷扩容后没有扩展文件系统,这些问题都不是“磁盘管理”一个命令能解决的,而是需要理解块设备、分区、文件系统、挂载点、权限、容量、健康检查和恢复流程之间的关系。

本文是 Linux 系统管理系列第 4 篇,承接上一篇《用户与权限管理》。上一篇关注“谁能访问文件”,本文进一步关注“文件如何组织、挂载、扩容、检查和排障”。在实际生产环境中,文件系统管理的目标不是记住某个命令,而是建立一套稳定的操作路径:先识别设备和挂载关系,再判断文件系统类型和使用状态,然后执行创建、挂载、扩容、清理、修复或回滚动作。

为什么文件系统管理容易出事故

文件系统事故通常有几个共同特征:

  1. 影响面直接:业务文件、数据库数据、日志、容器层和配置文件都依赖文件系统。挂载失败或只读后,应用通常会立即报错。
  2. 误操作难恢复mkfs、错误的 rsync --delete、误删目录、错误覆盖 /etc/fstab 都可能直接破坏数据。
  3. 现象容易混淆:空间满、inode 满、权限不足、只读挂载、上级目录缺少执行权限、SELinux/AppArmor 限制,都可能表现为“无法写入”。
  4. 启动链路敏感:根分区、/boot/var、网络文件系统和加密卷的挂载问题,可能导致主机启动缓慢、进入 emergency mode,甚至无法远程登录。
  5. 扩容有层次顺序:云盘扩容、分区扩容、LVM 扩容和文件系统扩容不是同一层。只扩了云盘但没有扩文件系统,业务仍然看不到可用空间。

学习目标

通过本文的学习,您将能够:

  1. 理解 Linux 文件层级、块设备、分区、文件系统和挂载点之间的关系。
  2. 使用 lsblkblkidfindmntdfdustatlsof 等命令判断文件系统状态。
  3. 正确创建 ext4、XFS 等常见文件系统,并通过 UUID 写入 /etc/fstab
  4. 处理磁盘空间满、inode 用尽、文件删除后空间不释放、挂载失败、文件系统只读等常见问题。
  5. 掌握 ext4 与 XFS 的在线扩容路径,理解扩容前检查和回滚边界。
  6. 建立生产环境文件系统变更、巡检和故障响应清单。

先建立正确的心智模型

心智模型

Linux 的文件系统视图是一棵从 / 开始的目录树。用户看到的是路径,例如 /var/log/nginx/access.log;内核和存储层看到的是块设备、分区、文件系统超级块、inode、目录项和挂载关系。一个路径属于哪个设备,不取决于路径长什么样,而取决于这个路径所在的挂载点。

这张图的关键是层次顺序。排查文件系统问题时,不要直接从应用报错跳到“磁盘坏了”;应先确认路径属于哪个挂载点,再往下追踪文件系统、设备、分区和底层存储。

文件系统层级与 FHS

Linux 目录树遵循一定约定。不同发行版细节会有差异,但多数服务器环境中可以按以下方式理解:

路径 常见用途 文件系统管理关注点
/ 根文件系统,系统运行基础 不应被日志、缓存、业务数据长期占满
/boot 内核、initramfs、引导文件 空间较小,内核更新后要清理旧版本
/etc 系统和服务配置 需要备份,避免单独放在不稳定挂载上
/var 可变数据,日志、缓存、队列、数据库默认目录 增长快,生产环境常单独规划
/var/log 系统和应用日志 防止日志占满根分区
/home 用户家目录 多用户服务器可单独挂载和配额
/srv 站点或业务服务数据 适合放内部应用数据
/opt 第三方软件 商业软件或独立包常用
/tmp 临时文件 可用 tmpfs 或单独分区,并设置清理策略
/mnt / /media 临时挂载点 不建议作为长期业务路径

生产服务器的一个基本原则是:根分区承载系统启动和基础服务,业务数据、日志、备份、镜像缓存应尽量放到明确的数据分区或逻辑卷。这样即使业务数据增长过快,也不至于直接拖垮系统启动和 SSH 登录。

块设备、分区、逻辑卷和文件系统

常见路径含义如下:

对象 示例 说明
磁盘设备 /dev/sda/dev/nvme0n1/dev/vda 整块磁盘或虚拟磁盘
分区 /dev/sda1/dev/nvme0n1p1/dev/vda2 磁盘上的分区
LVM 物理卷 /dev/sdb1 被 LVM 接管的底层设备
LVM 逻辑卷 /dev/mapper/vg_data-lv_app 可动态扩展的逻辑块设备
文件系统 ext4、XFS、Btrfs 管理文件、目录、元数据和空间分配
挂载点 /data/srv/app 文件系统接入目录树的位置

很多事故来自层次混淆。例如云平台控制台把数据盘从 200 GB 扩到 500 GB,只代表底层块设备变大;如果上面有分区,还要扩分区;如果上面是 LVM,还要扩 PV/LV;最后还要扩文件系统。df -h 只显示文件系统层,不代表底层磁盘没有变大。

inode 与目录项

文件系统不仅存储文件内容,还要存储元数据。inode 保存文件类型、权限、owner、时间戳、块指针等信息;目录项把文件名映射到 inode。理解 inode 有助于解释两个常见现象:

  1. df -h 显示空间没满,但应用无法创建新文件,可能是 inode 用尽。
  2. 删除大文件后空间没有释放,可能是文件名已删除,但仍有进程打开该 inode。

查看 inode 使用情况:

bash
df -i
df -ih /var
stat /var/log/syslog

查看某个文件所属文件系统:

bash
findmnt --target /var/log/syslog
df -hT /var/log/syslog

findmnt --target 在排障中非常有用,因为它能从任意路径反查实际挂载点,而不是让管理员凭目录名猜测。


常用文件系统如何选择

不同文件系统的能力边界不同。对普通服务器而言,ext4 和 XFS 是最常见的选择;Btrfs、ZFS、NFS、CIFS、tmpfs 等也会出现在特定场景中。

文件系统 典型优势 常见限制 适合场景
ext4 成熟、通用、工具丰富,支持在线扩容 不支持在线缩小;超大规模场景不如 XFS 通用应用、系统盘、小中型数据盘
XFS 高性能、适合大文件和并行 I/O,RHEL 9 默认文件系统 通常只能扩容,不能原地缩小 日志、对象文件、数据库、大容量数据盘
Btrfs 子卷、快照、校验、压缩 运维模型与传统文件系统不同,需要专门掌握 桌面、开发、需要快照回滚的场景
tmpfs 内存文件系统,速度快,重启丢失 占用内存和 swap,不适合持久数据 /run、临时缓存、短生命周期文件
NFS 多主机共享文件 依赖网络和服务端可用性,权限模型要谨慎 共享目录、批处理、备份中转
CIFS/SMB 与 Windows 文件共享集成 凭据、字符集、权限映射复杂 跨 Windows/Linux 文件共享

ext4 与 XFS 的取舍

ext4 的优势是成熟、简单、社区资料多,适合大多数普通应用。XFS 在大文件、高并发元数据操作和大容量文件系统上表现稳健,很多企业发行版将它作为默认选择。选择时可以按以下原则:

判断问题 更倾向 ext4 更倾向 XFS
是否是通用系统盘或小型数据盘 也可,但没有必要复杂化
是否经常存放大文件、镜像、备份、日志归档 可用 更合适
是否需要在线扩容 支持 支持且常用
是否可能需要缩小文件系统 离线缩小有工具支持,但仍需谨慎 不应规划为原地缩小
团队熟悉度 多数团队熟悉 RHEL/Rocky 团队通常熟悉

不要把文件系统选择神化。比文件系统类型更重要的是容量规划、备份恢复、监控告警、挂载参数、变更前快照和故障演练。

挂载参数的安全与稳定性

常见挂载参数包括:

参数 含义 适用建议
defaults 使用默认挂载选项 普通数据盘可作为起点
noatime 不更新访问时间 可减少读密集场景元数据写入,但要确认应用不依赖 atime
relatime 相对访问时间更新 多数 Linux 默认更平衡
nodev 不解析设备文件 /tmp、共享目录等可考虑
nosuid 忽略 setuid/setgid 位 临时目录、用户上传目录可考虑
noexec 禁止直接执行文件 /tmp、上传目录可考虑,但可能影响安装脚本
nofail 启动时挂载失败不阻断启动 非关键数据盘、可选网络盘可用
x-systemd.automount systemd 按需挂载 网络文件系统或冷数据目录可考虑

安全参数要结合业务验证。例如给 /tmpnoexec 可以降低某些脚本落地执行风险,但也可能影响安装器、编译器或某些服务启动流程。生产变更前应在同类环境验证。


基础巡检:先看清楚当前状态

文件系统巡检路径

巡检路径

文件系统操作前,第一步永远是看清楚现状。不要在未知设备上直接 mkfs,不要在未知挂载点上直接 rm -rf,不要在没有确认 UUID 的情况下修改 /etc/fstab

查看设备与挂载关系

bash
# 块设备、文件系统类型、UUID、挂载点
lsblk -f

# 更适合脚本的列输出
lsblk -o NAME,TYPE,SIZE,FSTYPE,UUID,MOUNTPOINTS

# 文件系统使用量
df -hT

# inode 使用量
df -ih

# 当前挂载树
findmnt

# 指定路径属于哪个文件系统
findmnt --target /var/log
df -hT /var/log

lsblk 看块设备层,df 看已挂载文件系统层,findmnt 看挂载关系。三个命令的视角不同,排障时应组合使用。

查看 UUID 和文件系统标签

bash
sudo blkid
ls -l /dev/disk/by-uuid/
ls -l /dev/disk/by-label/ 2>/dev/null

/etc/fstab 中更推荐使用 UUID=<uuid>,而不是 /dev/sdb1 这类设备名。设备名可能因控制器、启动顺序、云平台热插拔而变化;UUID 更稳定。写入前仍要确认 UUID 唯一,尤其是克隆磁盘、恢复镜像或复制虚拟机之后。

查看目录空间消耗

bash
# 当前文件系统总体使用
df -hT /
df -hT /var

# 查找 /var 下一级目录占用
sudo du -xhd1 /var | sort -h

# 查找大文件
sudo find /var -xdev -type f -size +1G -printf '%s %p\n' 2>/dev/null | sort -n

# 查找最近增长的日志
sudo find /var/log -xdev -type f -mtime -3 -printf '%TY-%Tm-%Td %TH:%TM %s %p\n' 2>/dev/null | sort

du 建议配合 -x,避免跨文件系统统计。例如 /var/lib/docker/var/lib/kubelet/var/log 可能是单独挂载点,不加 -x 容易把其他文件系统的内容算进来。

查看打开但已删除的文件

bash
sudo lsof +L1
sudo lsof | grep deleted

如果日志文件被删除但进程仍然打开,目录里看不到文件,du 也可能统计不到,但 df 显示空间没有释放。此时不要急着重启整台机器,先定位进程,再决定重启服务、让服务重新打开日志,或使用日志轮转的正确信号。


创建并挂载一个数据文件系统

下面以新增数据盘 /dev/sdb 为例,演示创建文件系统并挂载到 /data/app。生产环境中请先确认设备名,云服务器、虚拟机、NVMe 设备的命名可能不同。

操作流程图

这个流程的重点是“先验证再持久化”。/etc/fstab 写错可能影响启动,写入后必须验证语法和可挂载性。

1. 确认新设备

bash
lsblk -o NAME,TYPE,SIZE,FSTYPE,UUID,MOUNTPOINTS
sudo blkid /dev/sdb || true

如果 /dev/sdb 已经有 FSTYPEUUID,不要直接格式化。应先确认它是不是历史数据盘、镜像盘、备份盘或错误识别的系统盘。

可以查看磁盘是否有分区表:

bash
sudo fdisk -l /dev/sdb
sudo parted /dev/sdb print

2. 创建分区

对于普通数据盘,可以使用 GPT 分区表并创建一个主分区:

bash
sudo parted /dev/sdb --script mklabel gpt
sudo parted /dev/sdb --script mkpart primary 1MiB 100%
sudo partprobe /dev/sdb
lsblk -f /dev/sdb

如果设备路径是 NVMe,分区名通常类似 /dev/nvme0n1p1;如果是 virtio 磁盘,可能是 /dev/vdb1。后续命令要按实际路径替换。

3. 创建文件系统

创建 ext4:

bash
sudo mkfs.ext4 -L appdata /dev/sdb1

创建 XFS:

bash
sudo mkfs.xfs -L appdata /dev/sdb1

mkfs 会清空目标设备上的原有文件系统元数据。生产操作前建议至少做三次确认:

bash
lsblk -f /dev/sdb
sudo blkid /dev/sdb /dev/sdb1 || true
readlink -f /dev/disk/by-id/*

4. 临时挂载并验证

bash
sudo mkdir -p /data/app
sudo mount /dev/sdb1 /data/app

findmnt /data/app
df -hT /data/app
sudo touch /data/app/.write-test
ls -la /data/app/.write-test
sudo rm -f /data/app/.write-test

如果业务需要特定 owner 和权限,再设置目录:

bash
sudo groupadd --system appdata 2>/dev/null || true
sudo chown root:appdata /data/app
sudo chmod 2770 /data/app

这里的 setgid 位用于让目录中新建文件继承 appdata 组,便于团队或服务账号协作。是否使用 setgid,应结合上一篇《用户与权限管理》中的组规划。

5. 写入 /etc/fstab

获取 UUID:

bash
sudo blkid /dev/sdb1

示例 fstab 行:

fstab
UUID=11111111-2222-3333-4444-555555555555 /data/app xfs defaults,noatime 0 0

如果是 ext4:

fstab
UUID=11111111-2222-3333-4444-555555555555 /data/app ext4 defaults,noatime 0 2

第 6 列 fsck 顺序需要理解:

含义
0 启动时不由 fsck 自动检查,XFS 通常使用 0
1 根文件系统优先检查
2 其他本地文件系统后续检查,ext4 数据盘可用

写入后验证:

bash
sudo findmnt --verify
sudo mount -a
findmnt /data/app

util-linux 的 mount 手册明确提醒,不建议把 mount -a 当作 fstab 检查工具;更合适的方式是先用 findmnt --verify 验证,再决定是否执行 mount -a。这能减少把错误配置直接应用到当前系统的风险。


/etc/fstab 管理要点

/etc/fstab 是文件系统持久挂载配置。它看起来只是几列文本,但对启动过程影响很大。

基本格式:

fstab
<source> <mountpoint> <fstype> <options> <dump> <pass>

示例:

fstab
UUID=8a2b... /data/app xfs defaults,noatime 0 0
UUID=9c3d... /var/log ext4 defaults,nodev,nosuid 0 2
tmpfs /tmp tmpfs defaults,nodev,nosuid,noexec,mode=1777,size=2G 0 0

修改 fstab 的安全流程

fstab 安全修改流程

安全修改

修改前备份:

bash
sudo cp -a /etc/fstab /etc/fstab.$(date +%F-%H%M%S).bak

验证时重点看:

bash
sudo findmnt --verify
sudo systemctl daemon-reload
sudo mount -a
findmnt --fstab --evaluate

如果挂载点是业务关键路径,应额外检查:

bash
findmnt /data/app
df -hT /data/app
stat -f /data/app
sudo -u appuser test -w /data/app && echo writable

网络文件系统与启动阻塞

NFS、CIFS 等网络文件系统在启动阶段容易因为网络、DNS、认证、服务端不可用而阻塞。非关键网络盘可以考虑:

fstab
server:/export/app /mnt/app nfs defaults,nofail,_netdev,x-systemd.automount 0 0

其中:

参数 作用
_netdev 表示依赖网络
nofail 失败时不阻断启动
x-systemd.automount 访问时再触发挂载

关键业务挂载不应简单依赖 nofail 掩盖问题。应配合监控、服务依赖和应用启动前检查,避免应用在空目录里写入数据,造成“挂载恢复后数据消失”的错觉。所谓消失,其实是挂载前写到了底层目录,挂载后被上层文件系统遮住。


空间满与 inode 满排障

空间满与 inode 满排障路径

空间排障

文件系统最常见的告警是空间使用率过高。处理这类问题不要只会删除日志。正确顺序是:确认影响范围,找增长来源,判断是否可清理,处理打开但已删除文件,最后再考虑扩容和长期治理。

排障决策树

1. 确认是哪个文件系统满了

bash
df -hT
df -ih
findmnt --target /path/reported/by/app

应用报错路径很重要。/var/log 满和 /data 满是两个问题;容器宿主机 /var/lib/containerd 满和业务数据盘满也不是同一个处理流程。

2. 找到大目录

bash
sudo du -xhd1 /var | sort -h
sudo du -xhd1 /var/log | sort -h
sudo du -xhd1 /var/lib | sort -h

常见增长源:

目录 可能原因 处理方向
/var/log 日志轮转失败、debug 日志打开、异常重复报错 修正 logrotate 或应用日志级别
/var/lib/docker 镜像、容器层、volume、build cache 堆积 使用容器工具清理,不要手工删除 overlay2
/var/lib/containerd 镜像和快照堆积 使用 crictl/nerdctl/kubelet 机制处理
/var/cache 包缓存、应用缓存 按包管理器或应用文档清理
/tmp 临时文件未清理 使用 systemd-tmpfiles 或应用修复
数据库目录 数据增长、归档未清理 按数据库备份和归档策略处理

3. 处理已删除但未释放的文件

bash
sudo lsof +L1

示例处理思路:

bash
# 找到持有 deleted 日志文件的服务
sudo lsof +L1 | awk 'NR==1 || /deleted/'

# 如果是 nginx,可优先重新打开日志
sudo nginx -s reopen

# 如果是 systemd 管理的应用服务
sudo systemctl restart demo.service

不要直接杀掉数据库、消息队列或核心业务进程。对状态型服务,应先确认主从、备份、业务窗口和重启影响。

4. inode 用尽

bash
df -ih
sudo find /var -xdev -type d -printf '%h\n' 2>/dev/null | sort | uniq -c | sort -n | tail
sudo find /var -xdev -type f | sed 's#/[^/]*$##' | sort | uniq -c | sort -n | tail

inode 用尽通常来自海量小文件,例如会话文件、缓存碎片、小日志、失败任务队列、邮件队列、监控临时文件。处理方向不是扩大单个文件,而是减少文件数量、调整保留策略、改用数据库或对象存储、清理异常队列。

5. 清理要遵循工具边界

不同目录有不同清理方式:

bash
# Debian/Ubuntu 清理包缓存
sudo apt clean
sudo apt autoremove

# RHEL/Rocky 清理 DNF 缓存
sudo dnf clean all

# journald 限制日志占用
sudo journalctl --disk-usage
sudo journalctl --vacuum-time=14d
sudo journalctl --vacuum-size=2G

容器目录不要手工删除底层存储驱动目录。应优先使用容器运行时提供的命令,并理解它会删除哪些对象:

bash
docker system df
docker image prune
docker container prune
docker builder prune

生产环境不要无脑执行 docker system prune -a --volumes,它可能删除未运行容器、未使用镜像、构建缓存和 volume,造成回滚镜像或持久数据丢失。


文件系统扩容

文件系统扩容与修复边界

扩容边界

扩容要分清层次。一个完整路径可能是:

text
云盘/虚拟磁盘 -> 分区 -> LVM PV -> VG -> LV -> 文件系统 -> 挂载点

每一层都可能需要扩展。下面分别说明非 LVM 分区和 LVM 逻辑卷的常见路径。

扩容前检查

bash
lsblk -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINTS
findmnt /data/app
df -hT /data/app
sudo blkid

记录变更前状态:

bash
date
lsblk -f
df -hT
df -ih
sudo cp -a /etc/fstab /etc/fstab.$(date +%F-%H%M%S).bak

对有数据的文件系统,扩容前应确认最近备份或快照。在线扩容虽然常见,但不是备份的替代品。

ext4 在线扩容

如果 ext4 文件系统所在的块设备已经变大,可以使用:

bash
sudo resize2fs /dev/sdb1
df -hT /data/app

如果前面还有分区,需要先扩分区。云环境常用 growpart

bash
sudo growpart /dev/sdb 1
sudo resize2fs /dev/sdb1

growpart /dev/sdb 1 的含义是扩展 /dev/sdb 上的第 1 个分区,中间有空格,不是 /dev/sdb1

XFS 在线扩容

XFS 使用挂载点扩容:

bash
sudo xfs_growfs /data/app
df -hT /data/app

如果底层分区还没有扩展,同样需要先扩分区:

bash
sudo growpart /dev/sdb 1
sudo xfs_growfs /data/app

XFS 的一个重要特点是支持在线扩容,但不按传统方式原地缩小。因此容量规划时不要把“后续缩回去”作为常规回滚手段。回滚通常依赖备份恢复、快照回滚或新建较小文件系统后迁移数据。

LVM 扩容

如果 /data/app 位于 LVM:

bash
lsblk
sudo pvs
sudo vgs
sudo lvs
findmnt /data/app

典型扩容步骤:

bash
# 如果底层磁盘分区已扩大,先扩 PV
sudo pvresize /dev/sdb1

# 扩展逻辑卷,使用所有剩余空间
sudo lvextend -l +100%FREE /dev/vg_data/lv_app

# 按文件系统类型扩容
df -hT /data/app
sudo resize2fs /dev/vg_data/lv_app     # ext4
sudo xfs_growfs /data/app              # XFS

也可以让 lvextend 调用合适的文件系统扩展工具:

bash
sudo lvextend -r -l +100%FREE /dev/vg_data/lv_app

-r 很方便,但生产环境仍建议先确认文件系统类型、备份状态和维护窗口。遇到复杂 LVM、加密卷、multipath、数据库数据盘时,应把每一层状态记录清楚。


文件系统只读、损坏与修复

当内核检测到底层 I/O 错误或文件系统元数据异常时,可能把文件系统重新挂载为只读,以避免进一步损坏。应用层通常会看到写入失败、数据库报错、日志无法追加。

识别只读挂载

bash
findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS /data/app
mount | grep ' /data/app '
dmesg -T | tail -n 100
journalctl -k -p warning..alert --since "1 hour ago"

如果看到 ro 选项、I/O error、EXT4-fs error、XFS metadata I/O error 等信息,不要急着 mount -o remount,rw。先判断底层设备是否健康,否则强行写入可能扩大损坏。

修复原则

场景 建议动作
临时误挂载为只读,底层无错误 确认原因后可尝试 remount
ext4 元数据错误 停止业务、卸载文件系统、使用 fsck 检查
XFS 元数据错误 停止业务、卸载文件系统,使用 xfs_repair
根分区损坏 进入救援模式或从 Live ISO 启动修复
底层磁盘 I/O 错误 优先更换磁盘、迁移数据、从备份恢复

ext4 示例:

bash
sudo systemctl stop demo.service
sudo umount /data/app
sudo fsck -f /dev/sdb1
sudo mount /data/app

XFS 示例:

bash
sudo systemctl stop demo.service
sudo umount /data/app
sudo xfs_repair /dev/sdb1
sudo mount /data/app

如果文件系统无法卸载,先查占用:

bash
sudo fuser -vm /data/app
sudo lsof +f -- /data/app

不要在未理解影响的情况下使用 lazy umount:

bash
sudo umount -l /data/app

lazy umount 会把挂载点从目录树中分离,但仍可能有进程持有旧引用。它适合某些卡死网络挂载的止血场景,不适合作为常规修复手段。


权限、挂载和应用写入问题

应用报 Permission denied 不一定是 chmod 问题。它可能来自 owner/group/mode、ACL、上级目录执行权限、挂载只读、noexec、磁盘配额、安全模块或服务沙箱。

排查顺序

bash
# 1. 路径和挂载
findmnt --target /srv/app/data
findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS --target /srv/app/data

# 2. 文件和上级目录权限
namei -l /srv/app/data/file.txt
stat /srv/app/data /srv/app/data/file.txt
getfacl /srv/app/data /srv/app/data/file.txt

# 3. 以服务用户实际测试
sudo -u appuser test -r /srv/app/data/file.txt && echo readable
sudo -u appuser test -w /srv/app/data && echo writable

# 4. systemd 沙箱
systemctl show demo.service -p User -p Group -p UMask -p ReadWritePaths -p ProtectSystem -p ProtectHome

namei -l 对路径权限排查很有价值。它会逐级展示每一级目录权限,能快速发现上级目录缺少 x 权限的问题。

挂载参数导致的误判

现象 可能原因 验证命令
脚本有执行权限但无法执行 文件系统挂载了 noexec findmnt -o OPTIONS --target script.sh
setuid 程序不生效 挂载了 nosuid findmnt -o OPTIONS --target file
设备文件不可用 挂载了 nodev findmnt -o OPTIONS --target path
应用写入失败 文件系统只读或 systemd 限制 findmntsystemctl show

安全参数不是越多越好,而是要放在合适的位置。上传目录、临时目录、低信任共享目录适合更严格;数据库目录、运行时目录、安装目录则需要逐项验证。


文件系统监控与日常运维

文件系统问题越早发现,处理成本越低。监控不应只看 df -h,还要覆盖 inode、增长速度、只读状态、挂载存在性、关键目录写入能力和日志轮转状态。

建议监控项

监控项 建议阈值 说明
空间使用率 80% warning,90% critical 不同业务可按增长速度调整
inode 使用率 80% warning,90% critical 小文件密集目录尤其重要
挂载点存在性 必须存在 防止业务写入未挂载的空目录
文件系统只读 出现即告警 多数是严重信号
增长速度 按小时或天计算 比单点阈值更早发现异常
/boot 空间 70% warning 内核更新前尤其要看
日志轮转状态 logrotate 失败告警 防止日志无限增长

巡检脚本示例

以下脚本用于人工巡检或接入自动化平台前的基础版本:

bash
#!/usr/bin/env bash
set -euo pipefail

echo "== filesystem usage =="
df -hT

echo
echo "== inode usage =="
df -ih

echo
echo "== readonly mounts =="
findmnt -rn -o TARGET,OPTIONS | awk '$2 ~ /(^|,)ro(,|$)/ {print}'

echo
echo "== fstab verification =="
findmnt --verify

生产接入时应把输出结构化,例如输出 JSON 或 Prometheus textfile collector 格式,并排除只读的伪文件系统,如 /proc/sys/run 中的部分挂载。

日志轮转与保留策略

日志目录是空间事故高发区。建议检查:

bash
sudo logrotate -d /etc/logrotate.conf
sudo journalctl --disk-usage
grep -R "rotate\\|size\\|daily\\|weekly" /etc/logrotate.conf /etc/logrotate.d 2>/dev/null

常见策略:

日志类型 建议
系统日志 使用 journald 限制总量,保留足够排障窗口
Web 访问日志 按天轮转,压缩,按合规要求保留
应用 debug 日志 默认关闭或短期保留
审计日志 单独规划容量,严格保留和归档
容器日志 配置运行时日志大小和文件数量限制

日志不能只靠删除。删除只解决一次空间问题,轮转和保留策略才解决持续增长问题。


容量规划与目录拆分策略

文件系统管理不只是故障发生后的处理,也包括故障发生前的容量规划。很多系统在刚上线时只有一块根盘,所有数据都放在 / 下:应用放在 /opt,日志放在 /var/log,数据库放在 /var/lib,上传文件放在 /srv,备份也放在本机某个目录。短期看这样最省事,长期看会把不同增长曲线、不同安全要求、不同恢复目标的数据混在一起。一旦某个应用日志暴涨,根分区被打满,SSH 登录、包管理、系统日志、监控代理和业务进程都会同时受到影响。

一个更稳妥的做法,是按“数据类型”和“故障影响”拆分文件系统,而不是单纯按目录美观拆分。拆分过细会增加维护成本,拆分过少又会扩大故障半径。生产环境可以从以下几个问题开始评估:

评估问题 规划含义
这个目录增长速度是否可预测 增长不可控的目录更适合单独挂载和监控
这个目录满了是否会影响系统启动 会影响启动的目录需要更保守的阈值
这里的数据是否需要独立备份 需要独立备份的数据应有明确挂载点和标签
是否存在大量小文件 需要关注 inode、目录性能和清理策略
是否由单个应用独占 可以按应用设置 owner、quota、监控和恢复流程
是否需要加密、审计或特殊挂载参数 安全要求不同的路径不宜混放

常见服务器的拆分建议

对于普通 Web 或 API 服务器,可以采用相对简单的方案:

text
/             系统根分区,保留操作系统和基础工具
/var/log      日志单独挂载,避免日志占满根分区
/srv/app      业务应用数据或上传文件
/backup       本地临时备份或备份中转,不替代异地备份

如果服务器运行数据库,则应进一步区分数据、日志和备份:

text
/var/lib/postgresql    数据库数据目录
/var/log/postgresql    数据库日志目录
/backup/postgresql     本地备份暂存目录

数据库目录不建议和普通应用上传目录混用同一个文件系统。数据库对延迟、刷盘、快照一致性和恢复流程的要求更高;普通文件上传则更关注容量、对象数量和清理策略。两者混放会让监控阈值和恢复动作变得模糊。

对于容器宿主机,重点关注容器运行时目录:

text
/var/lib/docker
/var/lib/containerd
/var/lib/kubelet

这些目录增长很快,但不能用传统“看到大目录就删除”的方式处理。镜像层、快照、volume、pod 目录和运行时元数据之间有引用关系,手工删除可能破坏运行时状态。更好的规划是:上线前就确定容器数据根目录所在文件系统,配置日志大小限制,定期清理无用镜像和构建缓存,并把业务持久化数据放在明确的 volume 或外部存储中。

容量估算方法

容量估算不能只问“现在用了多少”。应按增长模型评估:

text
预计容量 = 初始数据量 + 日增长量 × 保留天数 + 峰值缓冲 + 运维缓冲

例如某应用每天产生 20 GB 原始日志,压缩后约 5 GB,要求本机保留 14 天,异常高峰可能达到平时 2 倍,则日志文件系统至少应考虑:

text
5 GB × 14 天 × 2 + 额外运维缓冲

如果日志在压缩前会短时间同时保留原始文件和压缩文件,还要为轮转窗口预留空间。很多日志目录并不是因为长期保留太多才满,而是在某个小时内异常日志暴涨,尚未进入轮转压缩周期就把空间打满。因此监控增长速度和设置应用侧限流同样重要。

数据库容量估算还要考虑索引、WAL/binlog、临时文件、备份、VACUUM 或 compaction 过程中的额外空间。文件服务器和对象缓存则要考虑小文件数量、目录层级、元数据开销和清理任务是否能跟上写入速度。

何时使用 LVM

LVM 的价值在于把底层磁盘和上层文件系统解耦,让扩容、迁移和组合多个磁盘更灵活。适合使用 LVM 的场景包括:

  1. 虚拟机或物理机上的多个业务数据卷需要后续扩容。
  2. 需要把不同磁盘加入同一个卷组,再按逻辑卷分配给应用。
  3. 希望使用统一命名表达用途,例如 vg_data/lv_logsvg_data/lv_app
  4. 需要结合快照做短时间变更保护或备份一致性辅助。

不适合为了“看起来专业”而在所有环境强行使用 LVM。云平台本身已经提供弹性云盘、快照、在线扩容和多盘管理时,LVM 仍然有价值,但会增加一层排障复杂度。团队必须能看懂 pvsvgslvslvextendpvresize 的输出,否则事故时容易卡在层次判断上。

文件系统标签和命名规范

建议为数据文件系统设置可读标签,并在文档中记录用途:

bash
# ext4 设置标签
sudo e2label /dev/sdb1 appdata

# XFS 设置标签
sudo xfs_admin -L appdata /dev/sdb1

lsblk -f

标签不能替代 UUID 的稳定性,但它能帮助人工巡检。命名上可以使用业务含义,而不是抽象的 data1data2

不推荐 推荐
/data1 /srv/app
/disk2 /var/log/nginx
lv01 lv_app_uploads
newdisk lv_pg_wal

路径、逻辑卷名、文件系统标签、监控项和备份策略最好能互相对应。这样当告警出现 lv_pg_wal 90% 时,值班人员能立即知道这是 PostgreSQL WAL 目录,而不是再去猜这个卷属于哪个业务。

配额是否值得启用

多用户服务器、共享开发机、文件上传平台和实验环境可以考虑启用 quota,防止单个用户或项目占满整个文件系统。配额适合治理“谁占用了空间”,不适合替代容量规划。对数据库、容器运行时和复杂应用目录,配额可能引入额外问题,应谨慎验证。

在启用配额前要明确:

  1. 配额按用户、组还是项目维度管理。
  2. 超额后应用会如何报错,是否会造成数据不一致。
  3. 是否有告警提前通知,而不是等写入失败后才发现。
  4. 备份、迁移、恢复时是否保留配额信息。

文件系统容量规划的成熟标志,不是永远不告警,而是告警能够提前、准确、可解释,并且处理动作不会依赖临场猜测。


生产变更清单

文件系统变更必须比普通配置变更更谨慎,因为误操作可能直接破坏数据。以下清单可用于创建、挂载、扩容、迁移和修复场景。

变更前

检查项 命令或动作
确认目标路径 findmnt --target <path>
确认设备层次 lsblk -fblkid
确认文件系统类型 df -hT <path>
备份 fstab cp -a /etc/fstab /etc/fstab.<timestamp>.bak
确认备份或快照 云快照、备份系统、数据库备份
确认业务影响 是否需要停服务、是否有写入窗口
准备回滚方案 快照回滚、恢复 fstab、恢复挂载、服务重启

变更中

检查项 命令或动作
每一步后查看状态 lsblkdf -hTfindmnt
修改 fstab 后验证 findmnt --verify
应用挂载配置 mount -a 或明确挂载单个目标
验证权限 sudo -u <user> test -w <path>
验证应用 启动、写入、读取、日志检查

变更后

检查项 命令或动作
记录最终状态 lsblk -fdf -hT
更新监控阈值 空间、inode、挂载点
更新文档 路径用途、owner、容量、备份策略
观察增长 至少覆盖一个业务高峰

常见错误与反模式

1. 在错误设备上执行 mkfs

这是最严重的误操作之一。避免方法:

bash
lsblk -f
sudo blkid
findmnt

确认目标设备没有挂载、没有文件系统、不是系统盘、不是备份盘,再执行 mkfs。在多人协作环境中,建议把设备 WWN、云盘 ID、业务用途写入变更单。

2. 用 /dev/sdb1 写 fstab

设备名可能变化。优先使用:

fstab
UUID=<uuid> /data/app xfs defaults 0 0

克隆磁盘后要检查 UUID 是否重复:

bash
lsblk -o NAME,FSTYPE,UUID,MOUNTPOINTS

3. 挂载失败后业务写入空目录

如果 /data/app 没有成功挂载,但目录本身存在,应用可能写到根分区上的 /data/app。挂载恢复后,这些文件会被新挂载遮住。预防方法:

  1. 服务启动前检查 findmnt /data/app
  2. 对关键挂载不要随意使用 nofail
  3. 在应用 systemd unit 中增加挂载依赖。

示例:

ini
[Unit]
RequiresMountsFor=/data/app

4. 只看 du 不看 df

du 统计目录可见文件,df 统计文件系统块使用。两者差异很大时,常见原因是已删除但仍打开的文件、挂载遮挡或权限导致统计不完整。

5. 用 chmod -R 777 解决写入问题

这会把权限问题变成安全问题。正确做法是确认服务用户、组、ACL、umask、上级目录权限和挂载选项,再给出最小权限。

6. 把备份放在同一个文件系统

如果备份目录和原始数据在同一文件系统,空间满、文件系统损坏、误删、勒索软件都可能同时影响原始数据和备份。至少应把关键备份放到独立文件系统、独立主机或对象存储,并定期恢复验证。


实战场景:/var 快满时的处理

假设监控告警 /var 使用率达到 92%,应用仍在运行,但日志写入变慢。处理路径如下。

1. 确认影响范围

bash
df -hT /var
df -ih /var
findmnt /var

如果 inode 没满,继续查空间:

bash
sudo du -xhd1 /var | sort -h

2. 定位增长目录

假设 /var/log 最大:

bash
sudo du -xhd1 /var/log | sort -h
sudo find /var/log -xdev -type f -size +500M -printf '%s %p\n' | sort -n

如果发现某应用 debug 日志异常增长,先降低日志级别或修复异常循环,再处理历史文件。否则清理后还会继续增长。

3. 检查已删除文件

bash
sudo lsof +L1

如果存在 deleted 日志被进程持有,重启或通知服务重新打开日志:

bash
sudo systemctl reload rsyslog 2>/dev/null || true
sudo systemctl restart demo.service

4. 安全清理

对 journald:

bash
sudo journalctl --disk-usage
sudo journalctl --vacuum-size=2G

对普通应用日志,优先确认是否已被 logrotate 管理:

bash
grep -R "/var/log/demo" /etc/logrotate.d /etc/logrotate.conf 2>/dev/null
sudo logrotate -d /etc/logrotate.conf

必要时可压缩或迁移旧日志:

bash
sudo gzip /var/log/demo/*.log.2026-05-*.old

不要删除当前正在写入的日志文件。更好的方式是让应用重新打开日志,或通过 logrotate 的 copytruncatepostrotate 等机制处理。copytruncate 也有丢日志窗口,关键系统应优先支持 reopen 信号。

5. 复查与预防

bash
df -hT /var
sudo du -xhd1 /var | sort -h
sudo journalctl --disk-usage

预防动作包括:

  1. /var/log 单独规划文件系统。
  2. 配置 journald 和 logrotate 上限。
  3. 为应用日志设置级别、轮转、压缩和保留天数。
  4. 监控增长速率,而不是只看 90% 阈值。

总结

文件系统管理是 Linux 运维从“能装好系统”走向“能稳定运行系统”的关键能力。本文从文件系统层次模型、文件系统选择、基础巡检、创建挂载、fstab 管理、空间与 inode 排障、扩容、只读修复、权限联动和生产变更清单几个角度,构建了一套可落地的操作方法。

关键要点回顾:

  1. 路径问题要先用 findmnt --target 反查挂载点,再分析文件系统和设备层。
  2. lsblkblkiddfdufindmntlsof 是文件系统排障的基础工具组合。
  3. 创建文件系统前必须确认目标设备,mkfs 是破坏性操作。
  4. /etc/fstab 应优先使用 UUID,写入后使用 findmnt --verify 验证。
  5. 空间满要同时检查 blocks、inode、已删除但打开的文件和异常增长来源。
  6. 扩容要按底层设备、分区、LVM、文件系统的层次逐步处理。
  7. 文件系统只读或损坏时,先看内核日志和底层设备健康,不要盲目 remount。
  8. 文件系统变更必须有备份、验证和回滚思路。

下一步学习: 掌握文件系统管理之后,建议继续学习《软件包管理》。软件包安装、升级、卸载、仓库配置和安全补丁管理,会频繁写入系统目录、缓存目录和服务文件;理解文件系统后,再学习包管理会更容易判断变更影响和回滚边界。


参考资料



本文最后更新于 2026-05-22