04-文件系统管理
完成用户与权限管理之后,Linux 系统管理进入一个更贴近日常运维现场的主题:文件系统管理。服务器上的应用、日志、数据库、备份、容器镜像、临时文件和用户数据,最终都会落到某个文件系统里。磁盘空间满了、inode 用尽、挂载参数错误、/etc/fstab 写错导致系统无法启动、日志目录占满。

概述
完成用户与权限管理之后,Linux 系统管理进入一个更贴近日常运维现场的主题:文件系统管理。服务器上的应用、日志、数据库、备份、容器镜像、临时文件和用户数据,最终都会落到某个文件系统里。磁盘空间满了、inode 用尽、挂载参数错误、/etc/fstab 写错导致系统无法启动、日志目录占满根分区、业务数据卷扩容后没有扩展文件系统,这些问题都不是“磁盘管理”一个命令能解决的,而是需要理解块设备、分区、文件系统、挂载点、权限、容量、健康检查和恢复流程之间的关系。
本文是 Linux 系统管理系列第 4 篇,承接上一篇《用户与权限管理》。上一篇关注“谁能访问文件”,本文进一步关注“文件如何组织、挂载、扩容、检查和排障”。在实际生产环境中,文件系统管理的目标不是记住某个命令,而是建立一套稳定的操作路径:先识别设备和挂载关系,再判断文件系统类型和使用状态,然后执行创建、挂载、扩容、清理、修复或回滚动作。
为什么文件系统管理容易出事故
文件系统事故通常有几个共同特征:
- 影响面直接:业务文件、数据库数据、日志、容器层和配置文件都依赖文件系统。挂载失败或只读后,应用通常会立即报错。
- 误操作难恢复:
mkfs、错误的rsync --delete、误删目录、错误覆盖/etc/fstab都可能直接破坏数据。 - 现象容易混淆:空间满、inode 满、权限不足、只读挂载、上级目录缺少执行权限、SELinux/AppArmor 限制,都可能表现为“无法写入”。
- 启动链路敏感:根分区、
/boot、/var、网络文件系统和加密卷的挂载问题,可能导致主机启动缓慢、进入 emergency mode,甚至无法远程登录。 - 扩容有层次顺序:云盘扩容、分区扩容、LVM 扩容和文件系统扩容不是同一层。只扩了云盘但没有扩文件系统,业务仍然看不到可用空间。
学习目标
通过本文的学习,您将能够:
- 理解 Linux 文件层级、块设备、分区、文件系统和挂载点之间的关系。
- 使用
lsblk、blkid、findmnt、df、du、stat、lsof等命令判断文件系统状态。 - 正确创建 ext4、XFS 等常见文件系统,并通过 UUID 写入
/etc/fstab。 - 处理磁盘空间满、inode 用尽、文件删除后空间不释放、挂载失败、文件系统只读等常见问题。
- 掌握 ext4 与 XFS 的在线扩容路径,理解扩容前检查和回滚边界。
- 建立生产环境文件系统变更、巡检和故障响应清单。
先建立正确的心智模型
心智模型
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 有助于解释两个常见现象:
df -h显示空间没满,但应用无法创建新文件,可能是 inode 用尽。- 删除大文件后空间没有释放,可能是文件名已删除,但仍有进程打开该 inode。
查看 inode 使用情况:
df -i
df -ih /var
stat /var/log/syslog
查看某个文件所属文件系统:
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 按需挂载 | 网络文件系统或冷数据目录可考虑 |
安全参数要结合业务验证。例如给 /tmp 加 noexec 可以降低某些脚本落地执行风险,但也可能影响安装器、编译器或某些服务启动流程。生产变更前应在同类环境验证。
基础巡检:先看清楚当前状态
巡检路径
文件系统操作前,第一步永远是看清楚现状。不要在未知设备上直接 mkfs,不要在未知挂载点上直接 rm -rf,不要在没有确认 UUID 的情况下修改 /etc/fstab。
查看设备与挂载关系
# 块设备、文件系统类型、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 和文件系统标签
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 唯一,尤其是克隆磁盘、恢复镜像或复制虚拟机之后。
查看目录空间消耗
# 当前文件系统总体使用
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 容易把其他文件系统的内容算进来。
查看打开但已删除的文件
sudo lsof +L1
sudo lsof | grep deleted
如果日志文件被删除但进程仍然打开,目录里看不到文件,du 也可能统计不到,但 df 显示空间没有释放。此时不要急着重启整台机器,先定位进程,再决定重启服务、让服务重新打开日志,或使用日志轮转的正确信号。
创建并挂载一个数据文件系统
下面以新增数据盘 /dev/sdb 为例,演示创建文件系统并挂载到 /data/app。生产环境中请先确认设备名,云服务器、虚拟机、NVMe 设备的命名可能不同。
操作流程图
这个流程的重点是“先验证再持久化”。/etc/fstab 写错可能影响启动,写入后必须验证语法和可挂载性。
1. 确认新设备
lsblk -o NAME,TYPE,SIZE,FSTYPE,UUID,MOUNTPOINTS
sudo blkid /dev/sdb || true
如果 /dev/sdb 已经有 FSTYPE 或 UUID,不要直接格式化。应先确认它是不是历史数据盘、镜像盘、备份盘或错误识别的系统盘。
可以查看磁盘是否有分区表:
sudo fdisk -l /dev/sdb
sudo parted /dev/sdb print
2. 创建分区
对于普通数据盘,可以使用 GPT 分区表并创建一个主分区:
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:
sudo mkfs.ext4 -L appdata /dev/sdb1
创建 XFS:
sudo mkfs.xfs -L appdata /dev/sdb1
mkfs 会清空目标设备上的原有文件系统元数据。生产操作前建议至少做三次确认:
lsblk -f /dev/sdb
sudo blkid /dev/sdb /dev/sdb1 || true
readlink -f /dev/disk/by-id/*
4. 临时挂载并验证
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 和权限,再设置目录:
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:
sudo blkid /dev/sdb1
示例 fstab 行:
UUID=11111111-2222-3333-4444-555555555555 /data/app xfs defaults,noatime 0 0
如果是 ext4:
UUID=11111111-2222-3333-4444-555555555555 /data/app ext4 defaults,noatime 0 2
第 6 列 fsck 顺序需要理解:
| 值 | 含义 |
|---|---|
0 |
启动时不由 fsck 自动检查,XFS 通常使用 0 |
1 |
根文件系统优先检查 |
2 |
其他本地文件系统后续检查,ext4 数据盘可用 |
写入后验证:
sudo findmnt --verify
sudo mount -a
findmnt /data/app
util-linux 的 mount 手册明确提醒,不建议把 mount -a 当作 fstab 检查工具;更合适的方式是先用 findmnt --verify 验证,再决定是否执行 mount -a。这能减少把错误配置直接应用到当前系统的风险。
/etc/fstab 管理要点
/etc/fstab 是文件系统持久挂载配置。它看起来只是几列文本,但对启动过程影响很大。
基本格式:
<source> <mountpoint> <fstype> <options> <dump> <pass>
示例:
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 的安全流程
安全修改
修改前备份:
sudo cp -a /etc/fstab /etc/fstab.$(date +%F-%H%M%S).bak
验证时重点看:
sudo findmnt --verify
sudo systemctl daemon-reload
sudo mount -a
findmnt --fstab --evaluate
如果挂载点是业务关键路径,应额外检查:
findmnt /data/app
df -hT /data/app
stat -f /data/app
sudo -u appuser test -w /data/app && echo writable
网络文件系统与启动阻塞
NFS、CIFS 等网络文件系统在启动阶段容易因为网络、DNS、认证、服务端不可用而阻塞。非关键网络盘可以考虑:
server:/export/app /mnt/app nfs defaults,nofail,_netdev,x-systemd.automount 0 0
其中:
| 参数 | 作用 |
|---|---|
_netdev |
表示依赖网络 |
nofail |
失败时不阻断启动 |
x-systemd.automount |
访问时再触发挂载 |
关键业务挂载不应简单依赖 nofail 掩盖问题。应配合监控、服务依赖和应用启动前检查,避免应用在空目录里写入数据,造成“挂载恢复后数据消失”的错觉。所谓消失,其实是挂载前写到了底层目录,挂载后被上层文件系统遮住。
空间满与 inode 满排障
空间排障
文件系统最常见的告警是空间使用率过高。处理这类问题不要只会删除日志。正确顺序是:确认影响范围,找增长来源,判断是否可清理,处理打开但已删除文件,最后再考虑扩容和长期治理。
排障决策树
1. 确认是哪个文件系统满了
df -hT
df -ih
findmnt --target /path/reported/by/app
应用报错路径很重要。/var/log 满和 /data 满是两个问题;容器宿主机 /var/lib/containerd 满和业务数据盘满也不是同一个处理流程。
2. 找到大目录
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. 处理已删除但未释放的文件
sudo lsof +L1
示例处理思路:
# 找到持有 deleted 日志文件的服务
sudo lsof +L1 | awk 'NR==1 || /deleted/'
# 如果是 nginx,可优先重新打开日志
sudo nginx -s reopen
# 如果是 systemd 管理的应用服务
sudo systemctl restart demo.service
不要直接杀掉数据库、消息队列或核心业务进程。对状态型服务,应先确认主从、备份、业务窗口和重启影响。
4. inode 用尽
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. 清理要遵循工具边界
不同目录有不同清理方式:
# 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
容器目录不要手工删除底层存储驱动目录。应优先使用容器运行时提供的命令,并理解它会删除哪些对象:
docker system df
docker image prune
docker container prune
docker builder prune
生产环境不要无脑执行 docker system prune -a --volumes,它可能删除未运行容器、未使用镜像、构建缓存和 volume,造成回滚镜像或持久数据丢失。
文件系统扩容
扩容边界
扩容要分清层次。一个完整路径可能是:
云盘/虚拟磁盘 -> 分区 -> LVM PV -> VG -> LV -> 文件系统 -> 挂载点
每一层都可能需要扩展。下面分别说明非 LVM 分区和 LVM 逻辑卷的常见路径。
扩容前检查
lsblk -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINTS
findmnt /data/app
df -hT /data/app
sudo blkid
记录变更前状态:
date
lsblk -f
df -hT
df -ih
sudo cp -a /etc/fstab /etc/fstab.$(date +%F-%H%M%S).bak
对有数据的文件系统,扩容前应确认最近备份或快照。在线扩容虽然常见,但不是备份的替代品。
ext4 在线扩容
如果 ext4 文件系统所在的块设备已经变大,可以使用:
sudo resize2fs /dev/sdb1
df -hT /data/app
如果前面还有分区,需要先扩分区。云环境常用 growpart:
sudo growpart /dev/sdb 1
sudo resize2fs /dev/sdb1
growpart /dev/sdb 1 的含义是扩展 /dev/sdb 上的第 1 个分区,中间有空格,不是 /dev/sdb1。
XFS 在线扩容
XFS 使用挂载点扩容:
sudo xfs_growfs /data/app
df -hT /data/app
如果底层分区还没有扩展,同样需要先扩分区:
sudo growpart /dev/sdb 1
sudo xfs_growfs /data/app
XFS 的一个重要特点是支持在线扩容,但不按传统方式原地缩小。因此容量规划时不要把“后续缩回去”作为常规回滚手段。回滚通常依赖备份恢复、快照回滚或新建较小文件系统后迁移数据。
LVM 扩容
如果 /data/app 位于 LVM:
lsblk
sudo pvs
sudo vgs
sudo lvs
findmnt /data/app
典型扩容步骤:
# 如果底层磁盘分区已扩大,先扩 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 调用合适的文件系统扩展工具:
sudo lvextend -r -l +100%FREE /dev/vg_data/lv_app
-r 很方便,但生产环境仍建议先确认文件系统类型、备份状态和维护窗口。遇到复杂 LVM、加密卷、multipath、数据库数据盘时,应把每一层状态记录清楚。
文件系统只读、损坏与修复
当内核检测到底层 I/O 错误或文件系统元数据异常时,可能把文件系统重新挂载为只读,以避免进一步损坏。应用层通常会看到写入失败、数据库报错、日志无法追加。
识别只读挂载
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 示例:
sudo systemctl stop demo.service
sudo umount /data/app
sudo fsck -f /dev/sdb1
sudo mount /data/app
XFS 示例:
sudo systemctl stop demo.service
sudo umount /data/app
sudo xfs_repair /dev/sdb1
sudo mount /data/app
如果文件系统无法卸载,先查占用:
sudo fuser -vm /data/app
sudo lsof +f -- /data/app
不要在未理解影响的情况下使用 lazy umount:
sudo umount -l /data/app
lazy umount 会把挂载点从目录树中分离,但仍可能有进程持有旧引用。它适合某些卡死网络挂载的止血场景,不适合作为常规修复手段。
权限、挂载和应用写入问题
应用报 Permission denied 不一定是 chmod 问题。它可能来自 owner/group/mode、ACL、上级目录执行权限、挂载只读、noexec、磁盘配额、安全模块或服务沙箱。
排查顺序
# 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 限制 | findmnt、systemctl show |
安全参数不是越多越好,而是要放在合适的位置。上传目录、临时目录、低信任共享目录适合更严格;数据库目录、运行时目录、安装目录则需要逐项验证。
文件系统监控与日常运维
文件系统问题越早发现,处理成本越低。监控不应只看 df -h,还要覆盖 inode、增长速度、只读状态、挂载存在性、关键目录写入能力和日志轮转状态。
建议监控项
| 监控项 | 建议阈值 | 说明 |
|---|---|---|
| 空间使用率 | 80% warning,90% critical | 不同业务可按增长速度调整 |
| inode 使用率 | 80% warning,90% critical | 小文件密集目录尤其重要 |
| 挂载点存在性 | 必须存在 | 防止业务写入未挂载的空目录 |
| 文件系统只读 | 出现即告警 | 多数是严重信号 |
| 增长速度 | 按小时或天计算 | 比单点阈值更早发现异常 |
/boot 空间 |
70% warning | 内核更新前尤其要看 |
| 日志轮转状态 | logrotate 失败告警 | 防止日志无限增长 |
巡检脚本示例
以下脚本用于人工巡检或接入自动化平台前的基础版本:
#!/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 中的部分挂载。
日志轮转与保留策略
日志目录是空间事故高发区。建议检查:
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 服务器,可以采用相对简单的方案:
/ 系统根分区,保留操作系统和基础工具
/var/log 日志单独挂载,避免日志占满根分区
/srv/app 业务应用数据或上传文件
/backup 本地临时备份或备份中转,不替代异地备份
如果服务器运行数据库,则应进一步区分数据、日志和备份:
/var/lib/postgresql 数据库数据目录
/var/log/postgresql 数据库日志目录
/backup/postgresql 本地备份暂存目录
数据库目录不建议和普通应用上传目录混用同一个文件系统。数据库对延迟、刷盘、快照一致性和恢复流程的要求更高;普通文件上传则更关注容量、对象数量和清理策略。两者混放会让监控阈值和恢复动作变得模糊。
对于容器宿主机,重点关注容器运行时目录:
/var/lib/docker
/var/lib/containerd
/var/lib/kubelet
这些目录增长很快,但不能用传统“看到大目录就删除”的方式处理。镜像层、快照、volume、pod 目录和运行时元数据之间有引用关系,手工删除可能破坏运行时状态。更好的规划是:上线前就确定容器数据根目录所在文件系统,配置日志大小限制,定期清理无用镜像和构建缓存,并把业务持久化数据放在明确的 volume 或外部存储中。
容量估算方法
容量估算不能只问“现在用了多少”。应按增长模型评估:
预计容量 = 初始数据量 + 日增长量 × 保留天数 + 峰值缓冲 + 运维缓冲
例如某应用每天产生 20 GB 原始日志,压缩后约 5 GB,要求本机保留 14 天,异常高峰可能达到平时 2 倍,则日志文件系统至少应考虑:
5 GB × 14 天 × 2 + 额外运维缓冲
如果日志在压缩前会短时间同时保留原始文件和压缩文件,还要为轮转窗口预留空间。很多日志目录并不是因为长期保留太多才满,而是在某个小时内异常日志暴涨,尚未进入轮转压缩周期就把空间打满。因此监控增长速度和设置应用侧限流同样重要。
数据库容量估算还要考虑索引、WAL/binlog、临时文件、备份、VACUUM 或 compaction 过程中的额外空间。文件服务器和对象缓存则要考虑小文件数量、目录层级、元数据开销和清理任务是否能跟上写入速度。
何时使用 LVM
LVM 的价值在于把底层磁盘和上层文件系统解耦,让扩容、迁移和组合多个磁盘更灵活。适合使用 LVM 的场景包括:
- 虚拟机或物理机上的多个业务数据卷需要后续扩容。
- 需要把不同磁盘加入同一个卷组,再按逻辑卷分配给应用。
- 希望使用统一命名表达用途,例如
vg_data/lv_logs、vg_data/lv_app。 - 需要结合快照做短时间变更保护或备份一致性辅助。
不适合为了“看起来专业”而在所有环境强行使用 LVM。云平台本身已经提供弹性云盘、快照、在线扩容和多盘管理时,LVM 仍然有价值,但会增加一层排障复杂度。团队必须能看懂 pvs、vgs、lvs、lvextend、pvresize 的输出,否则事故时容易卡在层次判断上。
文件系统标签和命名规范
建议为数据文件系统设置可读标签,并在文档中记录用途:
# ext4 设置标签
sudo e2label /dev/sdb1 appdata
# XFS 设置标签
sudo xfs_admin -L appdata /dev/sdb1
lsblk -f
标签不能替代 UUID 的稳定性,但它能帮助人工巡检。命名上可以使用业务含义,而不是抽象的 data1、data2:
| 不推荐 | 推荐 |
|---|---|
/data1 |
/srv/app |
/disk2 |
/var/log/nginx |
lv01 |
lv_app_uploads |
newdisk |
lv_pg_wal |
路径、逻辑卷名、文件系统标签、监控项和备份策略最好能互相对应。这样当告警出现 lv_pg_wal 90% 时,值班人员能立即知道这是 PostgreSQL WAL 目录,而不是再去猜这个卷属于哪个业务。
配额是否值得启用
多用户服务器、共享开发机、文件上传平台和实验环境可以考虑启用 quota,防止单个用户或项目占满整个文件系统。配额适合治理“谁占用了空间”,不适合替代容量规划。对数据库、容器运行时和复杂应用目录,配额可能引入额外问题,应谨慎验证。
在启用配额前要明确:
- 配额按用户、组还是项目维度管理。
- 超额后应用会如何报错,是否会造成数据不一致。
- 是否有告警提前通知,而不是等写入失败后才发现。
- 备份、迁移、恢复时是否保留配额信息。
文件系统容量规划的成熟标志,不是永远不告警,而是告警能够提前、准确、可解释,并且处理动作不会依赖临场猜测。
生产变更清单
文件系统变更必须比普通配置变更更谨慎,因为误操作可能直接破坏数据。以下清单可用于创建、挂载、扩容、迁移和修复场景。
变更前
| 检查项 | 命令或动作 |
|---|---|
| 确认目标路径 | findmnt --target <path> |
| 确认设备层次 | lsblk -f、blkid |
| 确认文件系统类型 | df -hT <path> |
| 备份 fstab | cp -a /etc/fstab /etc/fstab.<timestamp>.bak |
| 确认备份或快照 | 云快照、备份系统、数据库备份 |
| 确认业务影响 | 是否需要停服务、是否有写入窗口 |
| 准备回滚方案 | 快照回滚、恢复 fstab、恢复挂载、服务重启 |
变更中
| 检查项 | 命令或动作 |
|---|---|
| 每一步后查看状态 | lsblk、df -hT、findmnt |
| 修改 fstab 后验证 | findmnt --verify |
| 应用挂载配置 | mount -a 或明确挂载单个目标 |
| 验证权限 | sudo -u <user> test -w <path> |
| 验证应用 | 启动、写入、读取、日志检查 |
变更后
| 检查项 | 命令或动作 |
|---|---|
| 记录最终状态 | lsblk -f、df -hT |
| 更新监控阈值 | 空间、inode、挂载点 |
| 更新文档 | 路径用途、owner、容量、备份策略 |
| 观察增长 | 至少覆盖一个业务高峰 |
常见错误与反模式
1. 在错误设备上执行 mkfs
这是最严重的误操作之一。避免方法:
lsblk -f
sudo blkid
findmnt
确认目标设备没有挂载、没有文件系统、不是系统盘、不是备份盘,再执行 mkfs。在多人协作环境中,建议把设备 WWN、云盘 ID、业务用途写入变更单。
2. 用 /dev/sdb1 写 fstab
设备名可能变化。优先使用:
UUID=<uuid> /data/app xfs defaults 0 0
克隆磁盘后要检查 UUID 是否重复:
lsblk -o NAME,FSTYPE,UUID,MOUNTPOINTS
3. 挂载失败后业务写入空目录
如果 /data/app 没有成功挂载,但目录本身存在,应用可能写到根分区上的 /data/app。挂载恢复后,这些文件会被新挂载遮住。预防方法:
- 服务启动前检查
findmnt /data/app。 - 对关键挂载不要随意使用
nofail。 - 在应用 systemd unit 中增加挂载依赖。
示例:
[Unit]
RequiresMountsFor=/data/app
4. 只看 du 不看 df
du 统计目录可见文件,df 统计文件系统块使用。两者差异很大时,常见原因是已删除但仍打开的文件、挂载遮挡或权限导致统计不完整。
5. 用 chmod -R 777 解决写入问题
这会把权限问题变成安全问题。正确做法是确认服务用户、组、ACL、umask、上级目录权限和挂载选项,再给出最小权限。
6. 把备份放在同一个文件系统
如果备份目录和原始数据在同一文件系统,空间满、文件系统损坏、误删、勒索软件都可能同时影响原始数据和备份。至少应把关键备份放到独立文件系统、独立主机或对象存储,并定期恢复验证。
实战场景:/var 快满时的处理
假设监控告警 /var 使用率达到 92%,应用仍在运行,但日志写入变慢。处理路径如下。
1. 确认影响范围
df -hT /var
df -ih /var
findmnt /var
如果 inode 没满,继续查空间:
sudo du -xhd1 /var | sort -h
2. 定位增长目录
假设 /var/log 最大:
sudo du -xhd1 /var/log | sort -h
sudo find /var/log -xdev -type f -size +500M -printf '%s %p\n' | sort -n
如果发现某应用 debug 日志异常增长,先降低日志级别或修复异常循环,再处理历史文件。否则清理后还会继续增长。
3. 检查已删除文件
sudo lsof +L1
如果存在 deleted 日志被进程持有,重启或通知服务重新打开日志:
sudo systemctl reload rsyslog 2>/dev/null || true
sudo systemctl restart demo.service
4. 安全清理
对 journald:
sudo journalctl --disk-usage
sudo journalctl --vacuum-size=2G
对普通应用日志,优先确认是否已被 logrotate 管理:
grep -R "/var/log/demo" /etc/logrotate.d /etc/logrotate.conf 2>/dev/null
sudo logrotate -d /etc/logrotate.conf
必要时可压缩或迁移旧日志:
sudo gzip /var/log/demo/*.log.2026-05-*.old
不要删除当前正在写入的日志文件。更好的方式是让应用重新打开日志,或通过 logrotate 的 copytruncate、postrotate 等机制处理。copytruncate 也有丢日志窗口,关键系统应优先支持 reopen 信号。
5. 复查与预防
df -hT /var
sudo du -xhd1 /var | sort -h
sudo journalctl --disk-usage
预防动作包括:
- 为
/var/log单独规划文件系统。 - 配置 journald 和 logrotate 上限。
- 为应用日志设置级别、轮转、压缩和保留天数。
- 监控增长速率,而不是只看 90% 阈值。
总结
文件系统管理是 Linux 运维从“能装好系统”走向“能稳定运行系统”的关键能力。本文从文件系统层次模型、文件系统选择、基础巡检、创建挂载、fstab 管理、空间与 inode 排障、扩容、只读修复、权限联动和生产变更清单几个角度,构建了一套可落地的操作方法。
关键要点回顾:
- 路径问题要先用
findmnt --target反查挂载点,再分析文件系统和设备层。 lsblk、blkid、df、du、findmnt、lsof是文件系统排障的基础工具组合。- 创建文件系统前必须确认目标设备,
mkfs是破坏性操作。 /etc/fstab应优先使用 UUID,写入后使用findmnt --verify验证。- 空间满要同时检查 blocks、inode、已删除但打开的文件和异常增长来源。
- 扩容要按底层设备、分区、LVM、文件系统的层次逐步处理。
- 文件系统只读或损坏时,先看内核日志和底层设备健康,不要盲目 remount。
- 文件系统变更必须有备份、验证和回滚思路。
下一步学习: 掌握文件系统管理之后,建议继续学习《软件包管理》。软件包安装、升级、卸载、仓库配置和安全补丁管理,会频繁写入系统目录、缓存目录和服务文件;理解文件系统后,再学习包管理会更容易判断变更影响和回滚边界。
参考资料
- mount(8) - Linux manual page
- findmnt(8) - Linux manual page
- mke2fs(8) - Linux manual page
- mkfs.xfs(8) - Linux manual page
- Red Hat Enterprise Linux 9 - Managing file systems
- Ubuntu Server documentation - Storage
本文最后更新于 2026-05-22