☰
Linux存储堆栈完全解读:LVM、文件系统与挂载实战
2026/10/8 8:36:07 网站建设 项目流程

在RH134这门课里,“管理存储堆栈”这一章,很多人第一次看到名字会愣一下:存储就存储,为什么要叫“堆栈”?我第一次接触这个表述时也琢磨了半天——它不是让你把磁盘一块块堆起来,而是要你把“一块物理硬盘到用户能在目录里正常读写文件”之间这一整条链路彻底搞清楚。这一章是RHCSA考试里最容易拉开分的地方,也是我后来做系统运维时天天在用的底层能力。这篇文章我按自己的理解把这章拆开讲一遍,从整体地图到具体命令,从扩容缩容到故障排错,全部结合实操经验来说。

1. 先在心里搭一张“存储堆栈”地图:磁盘到目录之间隔着什么

我见过太多初学者上来就敲mkfs和mount,结果是能跑通,但稍微换个环境就懵了。原因就是脑子里没有那张“存储地图”。

在 Linux 里,用户访问文件走的是目录路径,而数据最终落在物理磁盘上。这条路不是一步到位的,中间隔着好几层。RH134 第八章讲的管理存储堆栈,指的就是这些层级以及它们之间的合作关系。简单来说,从下往上看是这样一个链条:

物理磁盘 → 分区 → 物理卷(PV)→ 卷组(VG)→ 逻辑卷(LV)→ 文件系统 → 挂载点

如果从内核视角去看,链条还可以进一步细化:应用通过 VFS(虚拟文件系统)发请求,进入具体的文件系统层,再到块设备层,再经过 device mapper 映射,最后才到磁盘驱动和硬件设备。这也是为什么 RHEL 里 LVM 创建的设备在/dev/mapper/目录下能看到对应的映射名称,因为 LVM 本质上就是基于内核 device mapper 机制实现的虚拟块设备。

我在讲这套内容时经常打一个比方:物理磁盘是快递公司的仓库货架,分区是货架上的隔断,LVM 是在仓库里重新规划出来的虚拟仓板区,文件系统则是仓库管理员的登记簿——它知道仓库里每一件货(文件)放在哪个仓板、哪个格子。你作为用户,在.bashrc里写个快捷键也好,在/data目录下创建文件也好,数据都不是凭空跑到硬盘上的,它必须一层层“登记”“搬运”“落架”。

1.1 一个读请求的实际旅行路径

假设你执行cat /data/report.txt,系统内部大致经历这样的过程:

  • VFS 根据路径找到/data挂载点对应的文件系统实例;
  • XFS 文件系统根据文件名找到 inode,再由 inode 找到数据所在逻辑地址,这些地址是“文件系统块号”层面的地址;
  • 文件系统把这个地址请求发给底层块设备,也就是挂载时绑定的逻辑卷设备;
  • LVM 层通过 device mapper 的映射表,把逻辑卷上的地址转换成真正物理卷上的地址;
  • 最终由磁盘驱动去物理硬盘的对应扇区读取数据。

关键点在于:每一层都在向上层提供一个“抽象”接口,向下层操作另一个块设备。扩容逻辑卷时,文件系统根本感知不到底层数据被分散到多块磁盘这件事,它只知道自己的块设备变大了。这是 LVM 最值得学的核心价值——在线扩容实际改变的是映射关系,不需要搬动已有数据。

1.2 在真正的系统里一眼认出这些层

判断一台机器的存储结构,最直观的工具就是lsblk。我一般上了服务器先跑三条命令:lsblk看结构,blkid看文件系统和 UUID,df -h看实际挂载使用情况。lsblk的输出会自动缩进展示层级关系,比如:

NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS vda 252:0 0 40G 0 disk ├─vda1 252:1 0 1G 0 part /boot ├─vda2 252:2 0 39G 0 part │ └─vg_root-lv_root 253:0 0 39G 0 lvm / vdb 252:16 0 20G 0 disk └─vdb1 252:17 0 20G 0 part

注意输出里的TYPE列:disk表示物理磁盘,part表示分区,lvm表示逻辑卷。看到vda2下面挂着一个vg_root-lv_root,就说明这台机器的根文件系统是建在 LVM 上的。这一眼扫过去,整个堆栈结构就清楚了。

还有一点值得提醒:不要看见/dev/mapper/xxx就以为是特殊设备,它其实就是逻辑卷设备节点,直接对它执行mkfs、mount都没问题。真正的底层磁盘设备反而要小心操作,因为很多工具(比如parted、fdisk)一旦写错分区表,数据很难找回。

2. 裸盘到可用目录:从GPT分区到LVM文件系统的完整搭建

这一节是整章实操量最大的部分。我给你一个比较标准的练习流程:一台虚拟机里加一块 20G 的虚拟磁盘/dev/vdb,我们要把它做成一个可用的挂载点/data。整个过程分四步走:分区、建 PV/VG/LV、格式化文件系统、挂载并持久化。

2.1 第一步:分区表选型,MBR还是GPT

很多人觉得分区表无所谓的,直接fdisk一路回车就完事。但放到现在,这个习惯该改改了。

MBR(也叫 msdos 分区表)是老前辈,兼容性好,但最多只能分 4 个主分区,单分区上限 2T,而且分区表只有一份,坏了就坏一整块盘。GPT 分区表则支持 128 个分区、分区大小上限远超 2T,还自带备份分区表头,安全性更高。RHEL 8/9 在 UEFI 下默认就用 GPT,新磁盘我基本无脑选 GPT。

实操命令:

# 查看磁盘现状 lsblk # 给 /dev/vdb 打上 GPT 分区表标签 parted /dev/vdb mklabel gpt # 创建一个从盘头到盘尾的主分区 parted /dev/vdb mkpart primary xfs 0% 100% # 确认分区结果 lsblk /dev/vdb

这里有两个小细节。第一,parted的mkpart primary xfs 0% 100%中那个xfs只是给分区一个类型标识,并不会真的格式化文件系统,真正建文件系统在后面;第二,0% 100%在练习环境无所谓,但在生产环境我一般会预留一点空间,不把整块盘全分出去,以免以后遇到分区表或对齐问题没有回旋余地。

2.2 第二步:LVM 三件套,PV、VG、LV

分区完成之后,真正的 LVM 操作就开始了。LVM 的管理对象是分层递进的:

  • 物理卷 PV:底层分区或整盘设备,LVM 会在设备开头写自己的卷标元数据;
  • 卷组 VG:一个“空间池”,由多个 PV 组成;
  • 逻辑卷 LV:从 VG 里切出来的块设备,可以格式化文件系统后挂载。

命令顺序也很直观:

# 把分区初始化为物理卷 pvcreate /dev/vdb1 # 创建卷组 vg_data,把刚才的 PV 放进去 vgcreate vg_data /dev/vdb1 # 从卷组里划出逻辑卷,这里直接把全部空间给 lv_data lvcreate -n lv_data -l 100%FREE vg_data # 查看三件套的状态 pvs vgs lvs

如果你执行完lvs,会看到类似lv_data vg_data -wi-a----- 20.00g的输出。那一串-wi-a-----是 LV 的状态标记,重点不是死记每个字母,而是要知道-a表示 active,也就是正在使用中。

这里必须讲一下 VG 的默认 PE 大小。创建卷组时如果没有特别指定,默认 PE Size 是 4MiB。PE 是卷组里最小的空间分配单位,LV 的大小就是 PE 的整数倍。比如一块 20G 的盘,PE 大小为 4MiB,整块盘大约能划出 5120 个 PE。这也是为什么lvcreate既可以用-L 20G指定容量,也可以用-l 5120指定 PE 数量,两者本质是一回事。生产环境里如果想精细控制空间,理解 PE 概念会很有用。

还有一点容易被忽略:合理规划下,其实可以不对整块盘做分区,直接pvcreate /dev/vdb也行。PV 和分区本来就不是绑定关系。但我个人强烈建议走“先分区、再 PV”的路径。原因有两个:一是分区在lsblk里结构更清晰,遇到问题容易定位;二是万一这个盘要挪作他用(比如改成普通数据盘挂载),有分区表比整盘 PV 的状态更容易理解和回退。

2.3 第三步:文件系统与挂载

有了 LV 设备/dev/vg_data/lv_data,接下来才是格式化和挂载:

# 建 XFS 文件系统 mkfs.xfs /dev/vg_data/lv_data # 创建挂载点并临时挂载 mkdir /data mount /dev/vg_data/lv_data /data # 查看挂载结果 df -h /data

到这一步,/data已经可以正常读写文件了。但注意mount只是临时生效,重启之后就没了。这就是 RH134 里特别强调的持久化配置问题,我们放到第 4 节细聊。

2.4 为什么这么分层,而不是直接格式化和挂载

我经常被问:既然最终是格式化一个设备、挂载到目录,那何必绕一圈做 PV/VG/LV?答案是灵活性。直接对/dev/vdb1格式化确实简单,后续想扩容只能靠分区工具,而分区扩不了,备份重建成本很高。用了 LVM 之后,你可以随时找一块新盘,pvcreate后vgextend进卷组,再lvextend把逻辑卷放大,最后在文件系统层执行在线扩容命令。整个过程中服务不用停,数据不用动,这是生产环境最需要的特性。

3. 扩容缩容实战:LVM的灵活性为什么是RHEL的默认答案

RHEL 安装系统时默认把根文件系统建在 LVM 上,这不是没有理由的。日常运维里,“磁盘快满了,怎么办”这个问题,十次有九次是靠 LVM 解决。

3.1 先搞懂文件系统类型的分水岭:xfs能不能缩

RHEL 8/9 的默认文件系统是 XFS,RH134 课件里大量练习也是基于 XFS 的。XFS 针对大文件和大容量做了很好的设计,但它有一个特性非常关键:XFS 文件系统不能缩小。官方设计就是只允许增长,不允许 shrink。如果你建了一个 20G 的 LV 并格式化成 XFS,后面想把它缩到 10G,基本没戏。

所以在规划阶段就要想好:逻辑卷的大小不要拍脑袋,要为未来留出余量;或者你明确知道需要缩容,那就选择 ext4 文件系统。ext4 可以缩,但缩起来步骤繁琐,而且必须先卸载、检查文件系统,存在数据风险。我自己在大多数场景下更倾向 XFS + 预留充足空间,把缩容当成“下策中的下策”。

3.2 我的一次真实扩容过程

有一台文件服务器,跑着 Prometheus 数据目录/data,LVM 卷组vg_data里有一个 LVlv_data,格式是 XFS。某天df -h一看使用率到了 92%,需要临时扩展。操作是这样:

# 1. 先看卷组还有没有空间 vgs # 2. 看逻辑卷当前大小 lvs # 3. 卷组剩余空间足够,直接给 LV 增加 20G lvextend -L +20G /dev/vg_data/lv_data # 4. 重点来了:XFS 扩容要执行 growfs,不是在 LV 层停了就完事 xfs_growfs /data # 5. 确认结果 df -h /data

第 4 步是新手最容易漏掉的地方。lvextend只把逻辑卷这个“虚拟磁盘”变大了,文件系统并不知道自己可以扩展,所以必须再执行xfs_growfs让文件系统感知新空间。如果你平时用的 ext4,对应的命令则是resize2fs /dev/vg_data/lv_data。

对于新版 lvm2,lvextend带-r选项时,会自动调用适当的文件系统扩容工具。但考试也好、生产也好,我建议你仍要把两步拆开理解,因为自动化的底层逻辑就是这两件事,理解之后再图省事才不会出问题。

再补充一个细节:xfs_growfs后面接挂载点/data是最省心的写法,它自己会查到对应设备。直接传设备路径也能写,但挂载点写法更不容易记错。

3.3 缩容流程:仅限ext4,且必须先卸载

如果你真的遇到必须缩容的极端场景,用的是 ext4,完整流程是这样:

# 1. 卸下文件系统,不能在线缩容 umount /dev/vg_data/lv_dat # 2. 强制检查文件系统完整性 e2fsck -f /dev/vg_data/lv_data # 3. 把文件系统缩到目标大小(比逻辑卷目标稍大一点) resize2fs /dev/vg_data/lv_data 10G # 4. 再把逻辑卷缩到目标大小 lvreduce -L 10G /dev/vg_data/lv_data # 5. 再次检查并重新挂载 e2fsck -f /dev/vg_data/lv_data mount /dev/vg_data/lv_data /data

这里面最容易出错的是顺序:必须先缩文件系统,再缩逻辑卷,千万不能倒过来。我先resize2fs把文件系统缩到 10G,再lvreduce把逻辑卷从 20G 缩到 10G,两者之间还留了一点余量,避免文件系统边界超出逻辑卷边界导致数据损坏。我没有把缩容当成常规操作,每次做都要备份,这一点必须写在前头:任何缩容都伴随着风险,没有备份别动手。

3.4 LVM快照:最实用的隐藏技能

RH134 第八章的重点未必是快照,但运行维护两年之后你会发现 LVM 快照真的很值钱。创建快照的基本命令是:

lvcreate -L 2G -s -n lv_data_snap vg_data/lv_data

它不复制数据,而是按“copy-on-write”机制记录原始卷的数据变化。做系统升级或应用发版前,给关键逻辑卷拍个快照,出问题能秒级回滚。这个机制同样基于 device mapper,理解了存储堆栈的分层思想之后,快照就变成了很自然的能力——它就是在映射表里加了一层只读视图而已。

4. 挂载持久化、UUID与交换空间:那些重启后才会暴露的问题

挂载这件事,临时用谁都会,难点在于重启之后系统还能不能自动挂载。RH134 对这块的要求非常明确:你要能写出正确的/etc/fstab,并且理解为什么用 UUID 而不是设备名。

4.1 fstab 六字段到底在写什么

/etc/fstab每一行都有 6 个字段,空格或制表符分隔。拿我们前面搭的/data举例:

UUID=3f7c1d2e-8a4b-4f6c-9d0e-123456789abc /data xfs defaults 0 0

字段含义如下:

字段作用常见取值
1设备标识/dev/vg_data/lv_data或UUID=...,推荐 UUID
2挂载点/data、/boot、/
3文件系统类型xfs、ext4、swap
4挂载选项defaults、noatime、nodev等
5是否允许 dump 备份一般写 0
6开机 fsck 检查顺序根文件系统写 1,其他写 2,不需要检查写 0

我先解释两个最常见的坑。

第一坑:设备名漂移。/dev/sda、/dev/vdb这种设备名是内核按照发现顺序分配的,加一块新盘、换个启动顺序、调整了虚拟机的磁盘控制器后,两块盘的盘符可能对调,造成挂载错位。UUID 是文件系统创建时生成的唯一标识,跟设备盘符无关,所以必须用 UUID。

获取 UUID 我推荐blkid命令:

blkid /dev/vg_data/lv_data

输出里UUID="3f7c1d2e-..."直接复制进 fstab,尽量不要手敲。

第二坑:第六列乱写。第六列是 fsck 顺序,如果你写了一个系统里根本不存在的 UUID 对应的设备,开机阶段systemd-fsck会出现等待和失败,最后把你送进 emergency mode。这个场景太经典了,下面第五节详细说修复。

4.2 写完fstab之后,请先执行 mount -a

这是我最想让你记住的一个习惯:改完/etc/fstab,不要立刻reboot去验证,先执行:

mount -a

mount -a会按 fstab 内容把所有还没挂载的条目挂载一遍,它是检验配置最安全的方式。如果命令静默通过,再重启基本没问题;如果报错,直接改就行,不用担惊受怕。我每次改完 fstab 都养成了这个强迫症动作,已经救过我很多次。

4.3 交换空间管理:swap分区和swapfile

第八章里的交换空间也是存储堆栈的重要组成部分。当物理内存不足时,内核会把一些不常访问的内存页换出到 swap,腾出物理内存给更活跃的进程。

日常查看 swap 我有三个命令:

free -h swapon --show cat /proc/swaps

创建 swapfile 的流程也比较固定。假设我要加 2G swap 文件:

# 用 dd 创建指定大小的文件,bs 和 count 相乘等于最终大小 dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progress # 权限必须是 600,否则 mkswap 会警告,也不安全 chmod 600 /swapfile # 格式化为 swap 结构 mkswap /swapfile # 立即启用 swapon /swapfile

为什么权限要 600?swap 文件里可能会残留内存中的敏感数据,普通用户如果可读,等于把内存里的密钥、密码片段暴露出去。这个细节 RH134 教材里强调过,我自己也在生产环境见过因为权限没设对导致的安全告警。

写入 fstab 让重启之后自动启用:

/swapfile none swap defaults 0 0

这里注意第二字段写none而不是挂载点,第三字段写swap而不是文件系统类型。

关于 swap 大小,老教程常说“物理内存的两倍”,这个说法现在已经过时了。现代服务器动辄 32G、64G 内存,swap 配 64G 纯属浪费磁盘。RHEL 官方推荐的内存容量 2G 以下时设 2 倍、2G 到 8G 设与内存相同、超过 8G 反而是从 8G 起根据负载增量调整。我自己的通用策略是:普通虚拟机 2G-4G swap 足够,数据库服务器可以不配或少配,让数据库缓冲池尽量留在内存里。

还有一个内核参数值得知道:vm.swappiness,默认一般是 60,数值越大内核越倾向于使用 swap,越小越倾向于优先回收页缓存。某些对延迟敏感的场景,可以把 swappiness 调低到 10 左右,让内存尽量不被换出去:

sysctl -w vm.swappiness=10

写进/etc/sysctl.conf可持久生效。这不是 RH134 的必考内容,但绝对符合真实运维场景。

5. 存储故障排查复盘:emergency模式、inode耗尽和“消失”的空间

这一章的知识点如果只是“学过”而不去踩坑,过两个月就会忘。我挑几个自己真实遇到、也是考试和工作中常出现的故障场景,带你走一遍完整排查思路。

5.1 重启后进入emergency mode,我的完整排查链路

场景是这样的:在一台测试机上,我往/etc/fstab加了一行挂载/dev/sdc1的配置,UUID 没有认真抄,而是凭记忆敲进去的。结果重启之后系统没有正常进入登录界面,而是卡在类似这样的状态:

You are in emergency mode. After logging in, type "journalctl -xb" to view system logs.

当时心里咯噔一下,但修复思路其实很固定:

  1. 输入 root 密码进入紧急模式;
  2. 此时根文件系统通常是只读挂载,直接改不了 fstab,先重挂载为读写:
    mount -o remount,rw /
  3. 查看/etc/fstab,找到错误行。注意紧急模式下没有熟悉的编辑器也可以用sed或直接用 vim,只要能改:
    vim /etc/fstab
  4. 把错误行注释掉,保存退出;
  5. 执行reboot确认能正常启动。

排查过程中更高效的做法是执行journalctl -xb,它会列出本次启动过程中失败的单元。如果报错信息指向某个挂载点或设备,基本就是 fstab 对应的 UUID、设备名、文件系统类型三处里有一处写错了。这条链路我也带学员复盘过很多次,结论都一样:绝大多数情况不是文件系统损坏,而是/etc/fstab里的一行抄错了。

所以我的建议很明确:fstab 是 boot 流程的一部分,不是你随手编辑的普通配置文件,每次修改都值得先mount -a验证,并且备份一份再动。

5.2 df -h 显示还有空间,但文件就是写不进去

有一次业务告警“目录写入失败”,我登录上去df -h一看,空间还有 30%,第一反应是权限问题。结果权限正常,再一看df -i,inode 使用率 100%。

df -i查看的是 inode 数量,不是空间容量。每个文件或者目录至少消耗一个 inode,存储大量小文件(比如几十万个日志碎片、邮件、缓存项)时,磁盘空间可能没满,但 inode 先耗尽了。定位到元凶之后,我清了一批过期小文件,之后又写了个定时任务来清理临时目录,问题才算彻底解决。

排查命令汇总:

# 看空间 df -h # 看 inode 数量 df -i # 找哪个目录文件最多 du --inodes -d 2 /var | sort -nr | head -20

这个场景在第八章文件系统管理部分其实没有大篇幅展开,但在实际用户环境里非常高频,所以我专门提一笔。

5.3 磁盘空间凭空消失:文件被删但进程没释放

另一个诡异问题是df -h显示使用了 100%,但你去/下挨个目录du -sh,怎么加都凑不出那个数字。原因是某个进程打开了文件后又把文件删除了,文件系统里的目录项已经没了,但进程持有的文件描述符还没关闭,文件占用的块自然也不会释放。

排查方法:

# 找出所有已被删除但仍被进程占用的文件 lsof | grep deleted

或者更精确:

lsof +L1

拿到进程号之后,重启或者让应用重新打开日志即可释放空间。如果是个不该重启的数据库,那就得临时扩 LV,等维护窗口再做处理。这类问题属于“存储堆栈”上层和进程管理交叉的典型场景,我把它写进来是想提醒你:排查空间问题不要只看文件系统,还要看进程在背后做了什么。

5.4 多层存储结构下,怎么快速定位“到底哪一层出了问题”

排查 LVM 相关问题,我会按层级从上往下看:

  1. 文件系统层:df -h看空间,mount看挂载选项;
  2. LV 层:lvs看状态是否为 active;
  3. VG 层:vgs看 PE 分配情况;
  4. PV 层:pvs看物理卷是否在线;
  5. 底层设备:lsblk、dmesg | tail看驱动和硬件错误。

如果 LVM 设备消失了,优先执行pvscan和vgchange -ay尝试重新激活卷组。很多情况下只是重启后卷组没有被自动激活,并不代表数据损坏。这条经验在处理虚拟机迁移、快照恢复时特别有用。

最后说几句实在话

弄懂了存储堆栈这一章,你会发现后面学系统调优、容器存储、云镜像制作都轻松很多,因为底层概念都是同一套。我个人的建议是不要满足于把命令背下来,去找一台练习虚拟机,建一个 VG、切几个 LV、格式化、挂载、扩容、再拍快照,折腾坏了再从零来一遍。摔过几次跟头之后,分区表、LVM、UUID、挂载选项这些东西就会成为你的肌肉记忆。RHCSA 考试里存储相关的题目很多,但真正到了生产环境,这一章的价值远比考试分数大。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询