做运维的人应该都有过这种经历:某个业务目录空间告急,df一看已经 97%,头开始大。如果这套环境用的是传统分区,那接下来的剧本往往是“停机、加磁盘、迁移数据、重新挂载”;如果当初用了 LVM 逻辑卷,情况就会从容很多——因为有了PV、VG、LV这套三层抽象,你可以在线把空间“补”上去,甚至跨物理盘调整。我看到最近还有人在搜“pv pvc storageclass 这三者关系是什么”,再加上另一个热搜“lv内核下载”,说明不少同学在概念和内核层面都有疑问。这篇文章就把 LVM 的 PV/VG/LV 讲透,顺带把 K8s 那套 PV/PVC/StorageClass 的关系也捋清楚,最后补几个我实际运维中踩过的坑。
1. 分区困境:为什么业务系统最终会选择 LVM 这套抽象
1.1 传统分区方案的三个硬伤
很多新手会觉得“磁盘管理”不就是fdisk分个区、mkfs格式一下、然后挂载吗?维护个两年,你就能体会到传统分区的痛:
- 空间固定,调整成本高。一个分区是 100G,业务涨到 120G,除非旁边有未分配空间,否则你只能加新盘、重新建分区、拷贝数据,再卸载旧分区。这中间必然产生停机窗口。
- 跨盘能力为零。两块盘各 500G,想弄出一个 800G 的“单目录”,传统分区做不到,只能靠 RAID 或者更上层的文件系统去拼。
- 盘和目录绑定太死。在传统模式下,
/dev/sdb1就是sdb这块盘上的分区,你没法在保持文件系统不变的情况下,把它背后的物理载体悄悄换掉。
这就是为什么大一点的服务器、数据库主机、虚拟化宿主机,几乎默认都会上 LVM。LVM 的全称是 Logical Volume Manager,它要解决的核心问题就是:不要把业务看到的存储形态,直接锁死在物理磁盘的固定布局上。
1.2 LVM 的解题思路:把磁盘拆成“积木”
LVM 的抽象并不复杂,一共三层:
- PV(Physical Volume,物理卷):把一块物理磁盘或分区初始化成 LVM 认识的“标准原料”。
- VG(Volume Group,卷组):把多块 PV 汇总成一个大的“资源池”。
- LV(Logical Volume,逻辑卷):从 VG 这个池子里切出的一块块“业务卷”,相当于传统概念里的分区。
你可以这样类比:PV 是“一袋袋水泥”,VG 是“把所有水泥倒进一个大搅拌池”,LV 是从池子中按需取出来的“预制板”。业务看到的是预制板,不用关心水泥来自哪个袋。
这个抽象带来一个天然好处:LV 不再和某一块物理盘绑定。它可能跨了两块盘,或者只用了某块盘的一部分。后续任何一块盘空间不够,只要 VG 里还有余量,LV 就能在线“长个”;VG 也不够时,再塞一块新盘进去做 PV,扩容就完成了。
1.3 LVM 带来的三大红利:聚合、在线调整、快照
具体收益,我用三条来总结:
- 聚合:多块小盘汇成一个大池子。比如四块 2T 盘组成一个 VG,就可以分出一个 6T 的 LV 给备份目录,而不用管那 6T 到底落在哪块盘上。
- 在线调整:大多数场景下,
lvextend扩 LV、vgextend扩 VG 都能在系统运行中完成,配合resize2fs或xfs_growfs,业务几乎无感。 - 快照:LVM 支持创建 LV 快照,对数据库备份、虚拟机磁盘备份非常有用。快照利用的是 copy-on-write 机制,创建速度极快,不会长期占用与源卷等量的空间。
如果你还不清楚这三个红利在实际运维里意味着什么,后面第 4 节的实操会带你完整走一遍。
2. 解开 LVM 三层模型:PV、VG、LV 的职责、限制与命令对照
2.1 PV:把物理磁盘改造成“可入库原料”
PV 是最底层的基础设施。执行pvcreate /dev/sdb /dev/sdc之后,LVM 会在盘头写入一段元数据,并把这个盘纳入自己的管辖范围。从这以后,这块盘不再是一个“裸分区”,而是一块有 LVM 身份的物理卷。
执行后,我们可以用pvs或pvdisplay查看。常见输出类似:
$ pvs PV VG Fmt Attr PSize PFree /dev/sdb vg_data lvm2 a-- 500.00g 500.00g /dev/sdc vg_data lvm2 a-- 500.00g 500.00gPV 内部是被划分成一个个PE(Physical Extent,物理扩展块)的。默认 PE 大小是 4MB,也就是 4MB 的整数倍来“切”磁盘。PE 可以理解为 LVM 分配磁盘空间的最小“筹码”,后面 LV 的大小和 VG 的容量统计,都和 PE 有关。
一个常被忽略的限制:创建 PV 时,最好明确整块盘是否已有重要数据。pvcreate会把磁盘头部的元数据区域覆盖掉,如果你拿一块有旧分区的盘直接做 PV,旧数据会变得不可见,属于高风险操作。
2.2 VG:把原料汇成资源池
VG 是把多个 PV 合并后的“空间蓄水池”。执行vgcreate vg_data /dev/sdb /dev/sdc,系统就把这两个 PV 划入同一个卷组。VG 里有空闲空间,才能继续创建 LV。
VG 的元数据会记录在它包含的 PV 上,并不是存在某个全局数据库中。所以当你把一组 PV 全部拔掉再插回同一台机器,LVM 也能通过vgscan重新发现 VG。
VG 需要特别注意的点:
- 一个 PV只能属于一个 VG。如果你想让某块盘离开当前组加入另一个组,得先
vgextract或pvmove再pvchange。 - VG 名字在系统内必须唯一,否则激活时会混乱。
- VG 的可用空间是所有 PV 剩余 PE 的总和,所以
vgs里的VFree才是你真正能继续分给 LV 的余量。
用vgs查看:
$ vgs VG #PV #LV #SN Attr VSize VFree vg_data 2 1 0 wz--n- 999.99g 800.00g2.3 LV:从池子里切出“业务分区”
LV 是真正挂载给业务使用的块设备。它由若干LE(Logical Extent,逻辑扩展块)组成,每个 LE 会映射到某个 PE。因为有了这层映射,LV 可以跨 PV,并且在扩容时,新 PE 只需追加到映射关系里就好,对文件系统透明。
创建命令很直接:
lvcreate -L 200G -n lv_www vg_data-L指定大小,-n指定逻辑卷名。创建完成后,设备路径通常是/dev/vg_data/lv_www,同时也会在/dev/mapper/vg_data-lv_www下出现一个映射设备。
我们来看小例子:
$ lvs LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert lv_www vg_data -wi-a----- 200.00g这一步的“逻辑卷”三个字很关键:虽然它看起来像分区,但它不再关心底层物理盘是谁。你可以把 LV 随便搬到别的 PV 上,也可以在线扩大,业务进程毫无感知。
2.4 命令、设备文件与三层映射关系速查
下面这张表,是我给团队培训时常用的对照,一眼能看明白命令和三层的对应关系:
| 层级 | 作用 | 常用命令 | 查看命令 | 设备形式 |
|---|---|---|---|---|
| PV | 把物理盘初始化为 LVM 可用单元 | pvcreate | pvs/pvdisplay | /dev/sdb等 |
| VG | 把多个 PV 汇总成一个资源池 | vgcreate | vgs/vgdisplay | vg_data等卷组名 |
| LV | 从 VG 中切出可用的逻辑卷 | lvcreate | lvs/lvdisplay | /dev/vg_data/lv_www |
| 文件系统 | 在 LV 上格式化并挂载 | mkfs.ext4/mount | df -h | /www等挂载点 |
三者依赖关系是自上而下的:PV 是基础,VG 靠 PV 组成,LV 靠 VG 的空间切出。删除时顺序相反:先删 LV,再删 VG,最后才处理 PV。
3. 透过内核看 LVM:“lv内核”到底依赖什么模块
3.1 Device Mapper:LVM 为什么必须依赖内核
很多初学者只学了lvm命令,却不知道 LVM 在内核层面依赖一个叫Device Mapper的机制。Device Mapper 是 Linux 内核提供的一个通用块设备映射框架,它允许用户空间程序定义“虚拟块设备”的映射关系。LVM 的 LV 本质上就是 Device Mapper 设备。
当你执行lvcreate,整个链路是:
- 用户空间的
lvm2工具计算好 LV 到 PV 的映射关系。 - 通过 ioctl 或 netlink 机制,把这个映射关系交给内核的
dm_mod驱动。 - 内核创建出一个新的映射设备,在
/dev/mapper/下出现对应节点。
所以,一个系统要使用 LVM,必须同时具备两个条件:
- 内核里有 Device Mapper 相关模块,常见的是
dm_mod、dm_mirror、dm_snapshot等; - 用户空间安装了
lvm2工具包。
如果你在精简内核或容器宿主机上发现 LVM 命令明明存在,但vgs、pvs报“设备不存在”或“Volume group not found”,第一反应就应该是检查内核模块:
lsmod | grep dm_ modprobe dm_mod3.2 “lv内核下载”背后的真实语义
“lv内核下载”这个搜索词,看起来像在找与 LVM 相关的“内核下载”,但其实更准确的理解是:用户需要的不是某个叫“lv”的内核,而是支持 LVM 的内核驱动 + 配套的 lvm2 用户空间工具。
在常规发行版里,你不需要单独“下载内核”来启用 LVM,只需要确认两件事:
- 内核是否编译了 Device Mapper。大部分发行版默认是
=m,也就是模块方式,能动态加载。 - 用户空间工具是否安装。Debian/Ubuntu 下是
lvm2包,RHEL/CentOS/Rocky 下是lvm2包,SUSE 下也一样。安装命令大致是:
# Debian/Ubuntu apt-get install -y lvm2 # RHEL/CentOS/Rocky dnf install -y lvm2如果只是想在系统里查看内核配置,可以这样确认:
# 查看当前加载的模块 lsmod | grep dm_ # 查看内核是否启用 Device Mapper(取决于发行版) grep -i dm /boot/config-$(uname -r) 2>/dev/null出现类似CONFIG_BLK_DEV_DM=y或=m,就说明内核本身支持。=m时由modprobe dm_mod加载即可。
3.3 initramfs 与启动阶段:内核、LVM 工具、激活顺序
这里值得多说一段,因为“lv内核下载”这个搜索背后,很多用户其实是卡在了 Linux 启动阶段:内核找得到磁盘,但挂在 LVM 上的根分区无法激活,系统进入 emergency mode。这往往不是“内核没下载”,而是initramfs(初始内存盘)里没有包含 LVM 工具和 dm 模块。
Linux 启动时,根文件系统还没有挂载,必须先靠 initramfs 里的工具去识别存储、激活 LVM、再挂载真正的根分区。如果 initramfs 里没有 lvm2 或没有dm_mod,自然就失败。
遇到这种问题,常规修复方式是:
# 重新生成 initramfs(Debian/Ubuntu) update-initramfs -u # 重新生成 initramfs(RHEL/Rocky) dracut -f然后重启验证。我见过不少用 LVM 做根分区的机器,系统大版本升级时忘了重建 initramfs,升级后一重启就卡住。这是 LVM 运维中非常典型的“非数据损坏,但系统起不来”场景。
所以“lv内核下载”如果翻译成运维动作,其实就是:确保内核模块存在、lvm2 工具存在、initramfs 里包含 LVM 支持。
4. 从零实操:初始化 PV 到创建 LV,并完成扩容缩容全流程
4.1 环境预检与 PV/VG/LV 初始化
假设机器上有两块新盘/dev/sdb、/dev/sdc,都是 500G,我们要把它们组成一个卷组vg_data,然后切一个 200G 的逻辑卷给 Web 目录/www用。
第一步,先用lsblk确认盘符和容量,避免搞错设备:
$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sdb 8:16 0 500G 0 disk sdc 8:32 0 500G 0 disk然后用pvcreate初始化两块盘:
pvcreate /dev/sdb /dev/sdc如果你给整块盘做 PV,某些系统会有提示“Device /dev/sdb not found (or ignored by filtering)”或者“device not a partition”。这是正常现象,整块盘没有分区表时 LVM 也能工作,只是部分老工具会多打一行警告。可以用pvcreate --force处理,但我一般建议,如果磁盘是专用数据盘,整块盘直接做 PV 是安全的;如果后续可能还要配合其他分区工具,最好先用fdisk建一个 LVM 类型的分区,再对分区做 PV。
初始化完,创建 VG:
vgcreate vg_data /dev/sdb /dev/sdc再用vgs看,可以看到 VG 总容量约 1000G。这里要注意,磁盘容量看起来是 500G,实际可用会略小一点,因为 PV 头部要写元数据,且 PE 对齐也会吃掉一点点空间。不要对 1000G 这个数字有强迫症。
接下来创建 LV:
lvcreate -L 200G -n lv_www vg_data这时候lvs就能看到lv_www了。
4.2 格式化挂载与开机自启
LV 创建后,它只是一个块设备,还需要文件系统:
mkfs.ext4 /dev/vg_data/lv_www如果是给大数据或数据库场景用,也可以考虑 xfs:
mkfs.xfs /dev/vg_data/lv_www然后建挂载点并挂载:
mkdir -p /www mount /dev/vg_data/lv_www /www为了让重启后自动挂载,需要写入/etc/fstab。这里我强烈建议用UUID而不是设备名,因为 LVM 设备名在迁移或扩容后虽然一般不变,但万一 LV 被删除重建,设备名可能还是同一个,UUID 却是新的。通过blkid获取 UUID:
$ blkid /dev/vg_data/lv_www /dev/vg_data/lv_www: UUID="3b8f1c2a-xxxx-xxxx-xxxx-xxxxxxxxxxxx" TYPE="ext4"在/etc/fstab里加一行:
UUID=3b8f1c2a-xxxx-xxxx-xxxx-xxxxxxxxxxxx /www ext4 defaults 0 2然后执行mount -a验证,没有报错再重启测试。
4.3 扩容:先加盘还是先加卷
扩容是 LVM 最常被问的操作。场景一:VG 里还有空间,LV 扩就可以了。比如要把lv_www从 200G 扩到 250G:
lvextend -L +50G /dev/vg_data/lv_www然后根据文件系统类型执行在线扩容:
# ext4/ext3 resize2fs /dev/vg_data/lv_www # xfs xfs_growfs /www注意:xfs 在线扩容时参数是挂载点,而不是设备路径,这一点老手也会偶尔弄混。
场景二:VG 本身也没空间了,那就得先加一块新盘进 VG。比如再插入一块/dev/sdd:
pvcreate /dev/sdd vgextend vg_data /dev/sdd然后回到刚才的lvextend和文件系统扩容命令。整个过程可以做到业务不中断,前提是文件系统支持在线扩容(ext4、xfs 都支持)。
4.4 缩容与快照:能做的和不能做的
缩容比扩容麻烦得多。ext4 文件系统可以缩容,但 xfs不行,xfs 官方只能增肥不能瘦身。如果必须要缩容,只能备份数据、删 LV、重建 LV、恢复数据。
ext4 缩容的正确顺序是:
- 卸载文件系统:
umount /www - 强制检查:
e2fsck -f /dev/vg_data/lv_www - 先缩小文件系统:
resize2fs /dev/vg_data/lv_www 100G - 再缩小 LV:
lvreduce -L 100G /dev/vg_data/lv_www - 重新挂载:
mount /www
这里最容易出错的是顺序:必须先缩小文件系统,再缩小 LV。如果你先lvreduce,文件系统元数据可能会被截断,数据直接损坏。我见过不止一次因为“图省事”先缩 LV 导致整个目录读不出来的案例。
快照操作相对简单:
lvcreate -s -n snap_www -L 20G /dev/vg_data/lv_www创建后,可以把/dev/vg_data/snap_www挂载到临时目录,用于备份一致性检查。快照本身只在源卷发生变化时才慢慢消耗空间,所以你不能拿一个 10G 快照去覆盖一个 100G 的大库长期变化场景。快照卷一旦写满,会自动失效,千万别把快照当长期备份。
5. 跨领域对比:同样是 PV,K8s 里的 PV 和 LVM 里的 PV 有什么关系
5.1 为什么两个领域的 PV 会撞名
热门搜索词“pv pvc storageclass 这三者关系是什么”说明很多人被 PV 这个缩写搞晕了。LVM 里的 PV 是 Physical Volume,K8s 里的 PV 是 PersistentVolume,两个完全不同的概念,只是恰好都叫 PV。
要区分并不难,看上下文就行:
- 在 Linux 终端里敲
pvcreate、vgs、lvs,那是 LVM。 - 在 K8s 的 YAML 里看到
kind: PersistentVolume、kind: PersistentVolumeClaim、kind: StorageClass,那是云原生存储抽象。
但为什么有人会把它们放一起搜?因为两者都解决同一个问题:把“物理存储资源”和“业务使用请求”解耦。LVM 解耦的是“业务卷”和“物理盘”,K8s 解耦的是“Pod 的存储请求”和“底层真实存储设备”。
5.2 K8s 中 PV、PVC、StorageClass 的关系
简单说,K8s 的三件套是这样的:
- PV(PersistentVolume):集群管理员预先准备好的一块存储,可以来自 NFS、Ceph RBD、云盘、本地目录等。它描述“我有一块多大的存储,支持什么访问模式”。
- PVC(PersistentVolumeClaim):应用提交的存储需求,比如“我要 5Gi,ReadWriteOnce”。它本身不是存储资源,而是一张“申请单”。
- StorageClass:动态制备的模板。PVC 指定了 storageClassName 后,StorageClass 对应的 provisioner 会自动创建并配置 PV,再和 PVC 绑定。没有 StorageClass 时,管理员只能手动创建 PV,然后等待 PVC 来绑定。
三者关系用一个快递场景类比:PV 是仓库里已经打包好的包裹,PVC 是客户下的订单,StorageClass 是生产包裹的自动化流水线。没有流水线,你得提前人工包好包裹放仓库;有流水线,客户一下单,机器自动打包。
在 YAML 里,申请存储的典型写法是:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: www-pvc spec: accessModes: - ReadWriteOnce storageClassName: fast resources: requests: storage: 10Gi如果集群里有名为fast的 StorageClass,并且其 provisioner 正常,系统会自动创建对应的 PV,并把 PVC 绑定到 PV。
5.3 用 LVM 的思维理解 K8s 存储,再回到 LVM 本地存储插件
用 LVM 思维来类比 K8s 存储,会发现很多地方是相通的:
| 维度 | LVM 概念 | K8s 概念 |
|---|---|---|
| 底层存储单元 | PV(物理卷) | 实际存储后端(NFS、Ceph、本地盘等) |
| 资源池/模板 | VG(卷组) | StorageClass(按模板动态制备) |
| 最终给业务用的卷 | LV(逻辑卷) | PV 对应的真实卷 |
| 使用申请 | 无直接类比 | PVC(PersistentVolumeClaim) |
| 挂载给上层应用 | mount 到目录 | 挂载到 Pod 容器路径 |
严格来说,K8s 的 PV 更接近 LVM 的 LV 加上实际存储载体,而 StorageClass 更接近 VG 的“分配策略”。但这不影响我们用 LVM 的思考方式去理解 K8s:先有资源池,再按需切卷,业务只认挂载点,不关心底层出处。
更有趣的是,这两套体系在真实产品里还会直接叠加。比如 TopoLVM、OpenEBS LocalPV 这类存储插件,就是在 Kubernetes 节点上调用 Linux LVM 来创建逻辑卷,然后把逻辑卷映射给 Pod。也就是说,你写一个 PVC 申请 5Gi 空间,K8s 的 StorageClass 会触发插件在节点上执行lvcreate,最终把一个 LV 挂给 Pod 使用。这时候,K8s 的 PV/PVC/StorageClass、Linux 的 PV/VG/LV,就在同一条链路里真实相遇了。
如果你只是想在 K8s 里跑有状态服务,建议先理解好 PVC 和 StorageClass 的绑定逻辑,再决定底层是用本地 LVM、NFS 还是云盘,不要一上来就被缩写绕晕。
6. 运维日常:LVM 高频故障与排查现场
6.1 重启后 VG 无法自动激活
症状:系统启动到一半,卡在类似A start job is running for LVM...的界面,最后进入 emergency mode。
常见原因有三个:
/etc/fstab里写了 LVM 设备路径,但 LVM 服务还没激活时就尝试挂载;- initramfs 里缺少 LVM 工具,导致启动阶段无法激活 VG;
- 多路径环境下,设备路径没有及时出现。
排查顺序:
# 先看 LVM 是否能识别 VG vgscan # 手动激活所有卷组 vgchange -ay # 如果 lvs 能看到 LV,说明数据还在 lvs临时救起来后,要根治就重建 initramfs,并且检查/etc/lvm/lvm.conf里的auto_activation_volume_list,确保没有过滤掉这个 VG。有些发行版默认只自动激活根卷组,其他 VG 需要手动设置。
6.2 明明有 free 空间却创建失败
有时候vgs显示VFree还有几十 G,但执行lvcreate -L 100G却报“Insufficient free space”。这多数是PE 对齐导致的。
假设 PE 大小为 4MB,一个 VG 的可用空间是 100.2G,但你恰好想分配 100.5G,LVM 会告诉你空间不足。解决办法是不要用带小数的容量,尽量用整数 G,或者用百分比:
# 使用剩余空间的百分比 lvcreate -l 80%FREE -n lv_www vg_data-l后面跟的是 PE 数或百分比,-L后面跟的是容量。用-l 100%FREE可以瞬间把 VG 剩余空间全划给一个 LV。这个命令很常用,也很危险,因为划完就没了。
6.3 缩容踩坑:文件系统顺序
前面提到过缩容顺序,这里再展开说一个真实案例。我处理过一台 MySQL 服务器,运维同学想从 500G 数据卷里分出 100G 给别的业务,他看到文档说“先缩文件系统,再缩 LV”,但实际操作时,他用umount后直接执行了:
lvreduce -L 400G /dev/vg_data/lv_mysql然后才去执行resize2fs。结果是 LV 已经变成 400G,但文件系统认为还是 500G,resize2fs直接报错,并且因为卷尾的数据被截断,部分数据目录出现 IO 错误。最后花了一整天才从备份恢复。
所以这里必须明确:
# 正确缩容 ext4 流程 umount /data e2fsck -f /dev/vg_data/lv_data resize2fs /dev/vg_data/lv_data 400G lvreduce -L 400G /dev/vg_data/lv_data mount /data如果是 xfs,直接放弃缩容这条路,换方案。
6.4 删除 PV 与 VG 时的连锁反应
删除 PV 或 VG 时,最常见的错误是“想删一个 PV,但它正被 LV 占用”。系统会提示PV /dev/sdc is used by LV,告诉你这个 PV 上还有数据映射。此时不能直接pvremove,否则会导致 LV 数据丢失。
正确做法是先把该 PV 上的数据迁移走:
# 把 /dev/sdc 上的数据搬到同 VG 其他 PV pvmove /dev/sdc # 确认没有数据后,从 VG 里移除 vgreduce vg_data /dev/sdc # 再删除 PV pvremove /dev/sdc如果删的是整个 VG,也必须先删掉 VG 里所有 LV,再执行vgremove,最后对 PV 执行pvremove。顺序反了,LVM 会阻止你或者直接产生孤儿 VG。
我还遇到过一个情况:用pvremove删掉一个很久不用的旧盘,系统提示成功,但vgs里还残留旧 VG 信息。这通常是/etc/lvm/archive和/etc/lvm/backup里的元数据缓存,执行vgscan --cache或重启后即可刷新。
最后再分享一个实际体会
我用 LVM 这么多年,最大的感受是:它真的能救急,但也真的需要敬畏。扩容、加盘、做快照这些操作虽然命令简单,一旦遇到“数据还在盘上但元数据乱了”的场景,恢复起来相当费劲。我的个人习惯是:第一,涉及缩容、删 LV、删 VG 这类逆向操作,永远先备份;第二,改了/etc/fstab之后,一定要mount -a验证再重启;第三,给根分区用 LVM 的机器,升级内核或重新生成 initramfs 后,务必检查启动能否识别 dm 和 lvm 工具,别让系统永远卡在启动阶段。这几点听起来都是小事,但往往就是它们决定了你下一个凌晨是被电话叫醒,还是安稳睡到天亮。