磁盘扩容这件事,做过运维的都懂:机房里新上架的机器插满了盘,监控面板上还是那几行熟悉的挂载点,业务方的催办邮件已经进来了。这时候无论如何都绕不开pvcreate命令。它不是 LVM 工具链里最耀眼的那个,但它是把一块"裸盘"变成"可以被灵活切割、随时扩容、在线迁移的存储资源"的第一道手续;没有它,后面的vgcreate、lvcreate、lvextend全都无从谈起。很多人在这一步图省事直接pvcreate -ff -y /dev/sdb,也有不少老手会先把lsblk、blkid、wipefs -n挨个跑一遍再动手,两种做法背后其实是同一件事——你对自己正在写的那块盘有多少把握。
这篇内容想讲清楚三件事:pvcreate到底往磁盘上改了什么,各种参数背后的取舍逻辑是什么,以及在一个真实的多盘扩容场景里,这条命令应该怎么和前后环节串起来。目标读者是已经能敲基本命令、但对 LVM 元数据结构和踩坑点还没什么底的系统运维、后端开发,还有需要在虚拟机和云主机上临时扩盘的同学。全文基于 LVM2 环境,命令在主流发行版上通用,个别参数在较老版本上行为有差异的地方我会单独标出来。
1. pvcreate 在 LVM 三层结构里的确切位置
1.1 从裸块设备到可挂载文件系统
LVM 的模型其实就三层,用盖房子打比方最直观。物理卷(Physical Volume,PV)是"砖块",一块盘、一个分区、甚至一个回环文件,只要被pvcreate打过标记,它就成了一批标准规格的砖。卷组(Volume Group,VG)是"砖块堆场",把若干 PV 合到一起,形成一个大的容量池,池里的砖可以来自不同型号、不同大小的盘。逻辑卷(Logical Volume,LV)是"砌出来的墙",从池子里按需取料,砌成 100G、500G、2T 大小不等的块设备,最后在 LV 上建文件系统、挂载到目录。
这个模型和直接分区最大的区别在于"解耦"。传统分区一旦划好,扩容就得挪分区、改起始扇区,风险极高;LVM 里 LV 只是 VG 里一段 extent 映射,加盘、扩池、拉长 LV、在线resize2fs或xfs_growfs,全程业务不停。快照、条带、镜像、精简池这些能力也都是建立在这个映射层上的。所以如果你只是给一台临时测试机加块盘放日志,直接mkfs在分区上完全没问题;但凡这块盘未来有可能需要扩容、需要在线迁移、需要做快照备份,那从pvcreate开始就是值得的。
pvcreate处在这条链的最底端,它只做一件事:把一个块设备登记成 LVM 认识的样子。它不建卷组、不分配空间、不碰文件系统,纯粹是"挂牌"。挂牌之后,这块盘就归 LVM 管了,mount它没意义,dd它要小心,mkfs它会被覆盖。这个边界必须先想明白。
1.2 pvcreate 究竟往磁盘上写了什么
这一步搞不清楚,后面所有排错都是盲猜。执行pvcreate之后,LVM 大致在设备上留下了这么几块东西:
第一块是 label header,位于设备最开头的一个扇区(默认 sector 0,也可以用--labelsector挪到别处)。这个扇区里存着 PV 的 UUID、设备总大小、数据区和元数据区的起始偏移。它是"身份证",pvscan、pvs扫描磁盘时就是靠读这个位置来识别"这块盘是不是 PV"。
第二块是元数据区(metadata area),默认大小约 1 MiB。这里放的是文本格式的 LVM 元数据副本——注意是"副本",因为同一个 VG 里每个 PV 上都会存一份完整的 VG 配置。这是 LVM 一个非常关键的设计:任何一个 PV 挂了,只要 VG 里还有别的 PV 活着,元数据就还在,卷组配置不会丢。我第一次知道这个机制的时候还专门做过实验,把一块盘的头部用dd覆盖掉,然后vgcfgrestore从另一块盘的元数据里把配置捞了回来。
第三块是数据区,从元数据区之后按对齐边界开始,按 PE(Physical Extent)大小切成等长的段。默认 PE 是 4 MiB,这个值在vgcreate时用-s参数确定,pvcreate阶段一般不单独指定。数据区就是将来真正装数据的地方。
另外设备末尾附近 LVM 还会保留一小段空间放备份 label,所以你会注意到pvs显示的 PV Size 总是比lsblk看到的磁盘容量小一点点,差个一两兆。这不是 bug,是正常的元数据开销。
1.3 哪些场景必须用它,哪些场景可以绕开
必须走pvcreate的场景很明确:你要用 LVM 的任何能力。包括但不限于跨盘合并容量、在线扩缩容、给数据库做一致性快照备份、用pvmove把数据从旧盘迁到新盘、给虚拟机做精简置备的存储池。这些能力没有一项能在裸分区上实现。
可以绕开的场景也不少。单盘单分区、容量基本不会变、对快照没需求的场景,直接mkfs.ext4 /dev/sdb1更简单。用 btrfs 做多设备池、用 zfs 做存储管理、用软 RAID 之后直接建文件系统,这些方案本身就有卷管理层,再叠一层 LVM 属于重复建设,反而增加故障面。还有一种情况是存储侧已经做了精简置备或快照,上层再套 LVM 快照容易出现两层空间互相拖累的问题,这种要谨慎。
我个人的判断标准比较粗暴:这块盘上的数据,未来一年内有没有可能要"不中断业务地变大"?有,就上 LVM;没有,就别给自己找事。
2. 动手之前:设备确认与风险前置
2.1 用三种方式交叉确认目标盘
pvcreate是个破坏性操作,虽然它只写头部一小段,但对已有数据的设备来说,元数据被覆盖就意味着文件系统废了。所以每次敲这条命令之前,我都会强制自己走一遍三重复核。
第一步看拓扑,lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,UUID。这个输出能一眼看出设备是整盘还是分区、上面有没有文件系统签名、当前挂在哪。如果目标设备那一行MOUNTPOINT不是空的,立刻停手。第二步看签名,blkid /dev/sdb和wipefs -n /dev/sdb,后者是 dry-run 模式,只列出检测到的所有魔术字符串,不删任何东西。如果wipefs -n列出了ext4、xfs、LVM2_member、linux_raid_member中的任何一个,说明这块盘有"前科",得先搞清楚它原来的用途。第三步看稳定路径,ls -l /dev/disk/by-id/和ls -l /dev/disk/by-path/,确认这块盘的真实身份。
为什么要看by-id?因为/dev/sdb这个名字是不稳定的。机器重启、加一块 HBA 卡、换个盘位,sdb可能就变成sdc了。曾经有个案例是脚本里写死了/dev/sdb,某次维护后系统盘顺序变了,脚本直接把系统盘当数据盘pvcreate了,虽然因为系统盘在 LVM 里没造成灾难,但惊出一身冷汗。用/dev/disk/by-id/ata-XXXX这种带序列号的路径,能杜绝这类事故。
2.2 整盘直用还是先分区
这是个长期争论的话题。整盘直接pvcreate /dev/sdb的好处是简单,没有分区表这一层的偏移和对齐问题,容量利用率也最高。缺点是这块盘以后就没法再拆分了,如果哪天想分一半给别的用途,只能整盘pvremove再重来。
先分区再pvcreate /dev/sdb1的好处是灵活,一块盘可以切出多个分区给不同的 VG,也可以留一段空间给别的用途。代价是多了一层分区表,GPT 备份表在盘尾会占用一点空间,而且分区的起始扇区如果没对齐,会拖累整个 I/O 性能。
我自己的习惯是:数据盘、单用途、容量规划清楚的大盘,整盘直用;系统盘、需要混用、或者要给 swap 留位置的盘,先分区。分区的时候起点一定要从 1 MiB 开始,这不是玄学,是因为主流 SSD 的擦除块大小是 512 KiB 到 1 MiB,物理扇区是 4 KiB,1 MiB 起点能保证所有分区边界都落在完整擦除块上。用parted的话写mkpart primary 1MiB 100%,它会自动吸附到最佳对齐位置。
2.3 上线前的三个保护动作
第一,确认这块盘上真的没有要留的数据。不能只信自己记的,要fuser -mv /dev/sdb1看有没有进程在占用,lsof | grep sdb兜一遍。有时候一个跑了几天的备份进程还挂着文件句柄,umount之后设备依然是 busy 状态。
第二,如果盘上有旧的 RAID 或 LVM 签名,先清干净。mdadm --zero-superblock /dev/sdb清软 RAID 超级块,wipefs -a /dev/sdb清所有魔术字符串。注意wipefs -a会连同分区表一起清掉,执行前再确认一遍设备名。这一步别偷懒用pvcreate -ff代替,-ff只处理它认识的 LVM 签名,对linux_raid_member这类外来签名不一定管用。
第三,把当前 LVM 的元数据备份一次。vgcfgbackup -f /root/vg_backup_$(date +%F).vg会把所有 VG 配置导出成文本文件,这个文件小到可以忽略不计,但关键时刻能救命。系统其实每次 VG 变更都会自动往/etc/lvm/archive/里存一份,手动备份只是多一层保险。
3. 参数逐条拆解与取值逻辑
3.1 基础语法和最小可用组合
pvcreate的语法结构极其简单:
pvcreate [选项] 物理卷设备...最常用的形态就是光杆一条:
pvcreate /dev/sdb1它支持一次传多个设备,pvcreate /dev/sdb1 /dev/sdc1 /dev/sdd1,LVM 会依次处理,遇到第一个出错就停下。这个特性在批量初始化时很好用,但也意味着中途失败后你得自己判断哪些成功了、哪些没成功,所以大规模操作我倾向于写 for 循环加判断,而不是一条命令带十几个设备。
执行成功后会输出类似Physical volume "/dev/sdb1" successfully created.的提示。如果只看到Physical volume "/dev/sdb1" successfully created.但随后pvs里没有,多半是lvm.conf的 filter 把它过滤掉了,这个后面排查章节细说。
几个通用的辅助参数值得记住:-v输出详细过程,-d输出调试信息(排错时非常有用,能看到它到底扫了哪些路径、读了哪些配置),-t是测试模式,只走流程不真正写盘,适合在脚本里先做一次预演,--reportformat json让输出变成 JSON,方便程序解析。
3.2 强制类参数:-f、-ff、-y 的区别
这三个参数经常被混着用,但语义完全不同,用错了会出事。
-y(--yes)最简单,它只是对所有交互式提问自动回答 yes。pvcreate在某些情况下会问"这里已经有东西了,确定要继续吗",加-y就不问了。它本身不增加任何强制能力,只是免交互。
-f(--force)表示强制创建。当设备上检测到已有的 PV 标签、或者设备正被某个 VG 引用时,不加-f会直接报错退出。加了-f,LVM 会忽略这些"软性"的顾虑继续执行。
-ff(双 force)是核弹级。它会强制覆盖设备上看起来像文件系统、分区表、或者其他 LVM 结构的内容。注意是"看起来像",LVM 的判断依据是扫描魔术字符串,它并不真的知道那里存的是什么业务数据。
所以正确的组合是这样的:日常新建盘,什么都不用加,让它该报错就报错;脚本里需要免交互,加-y;确认设备上只有旧的 LVM 结构要重建,加-f -y;只有在设备确实被搞脏、且你已经用wipefs确认过内容可以全部丢弃的情况下,才用-ff -y。
提示:较新版本的 LVM2 里,官方更推荐先用
wipefs -a把设备清理干净,再进行pvcreate,而不是依赖-ff硬来。wipefs至少还会告诉你它准备删掉哪些签名。
3.3 元数据区域:--metadatasize 与 --labelsector
--metadatasize控制前面提到的元数据区大小,默认 1 MiB。这个默认值对绝大多数场景都够用,因为 1 MiB 的文本空间能容纳的 VG 配置信息量非常可观——它对应的是"这个 VG 里有多少个 LV、每个 LV 有多少个 segment、每个 segment 映射到哪些 PE"。真正把元数据撑爆的场景很少见,通常是单个 VG 里塞了成百上千个小 LV,或者 PE 设得特别小导致 extent 数量爆炸。
真需要调的时候,写法是pvcreate --metadatasize 64m /dev/sdb1。单位可以写k、m、g。不建议设得比 1 MiB 小,最小也就在 128 KiB 上下,太小的元数据区会导致 VG 稍微复杂一点就写入失败,报Metadata area is too small之类的错误。
--labelsector是把 label header 从默认的 sector 0 挪到别的位置,一般只在磁盘开头有特殊结构、不能被覆盖时才用,比如某些嵌入式设备的引导区。这个参数很少用到,知道有这回事就行。
还有一个--bootloaderareasize,是为老式引导程序预留空间的,纯数据盘用不上,跳过。
3.4 对齐相关:--dataalignment 与 PE 大小
对齐这件事在机械盘时代影响不大,在 SSD 和 RAID 阵列上影响显著。pvcreate启动时会自动做一次对齐探测,读/sys/block/sdb/queue/optimal_io_size、physical_block_size、alignment_offset这几个值,算出建议的对齐边界,然后自动应用。所以大多数情况下你什么都不用管。
需要手动干预的典型场景是底层是硬件 RAID 且条带大小已知。比如一组 6 盘 RAID6,条带单元 256 KiB,那么数据区起始位置对齐到条带边界能让每次写入都落在完整的条带上。写法是pvcreate --dataalignment 256k /dev/sdb。如果 RAID 控制器报告的起始偏移不为零,还要用--dataalignmentoffset补上。
PE 大小这块要特别注意一个容易搞混的点:pvcreate确实有--physicalextentsize这个参数,但 PE 大小本质上是卷组级别的属性,同一 VG 里所有 PV 必须一致。实际项目中,PE 大小几乎都是在vgcreate -s时确定的,pvcreate阶段不指定。默认 4 MiB 是个平衡点:太小则 extent 数量多、元数据膨胀、映射表变长;太大则空间分配粒度粗,一个 10G 的 LV 用 64 MiB 的 PE 也还行,但如果你要建很多小 LV,尾部的浪费就会累积。
经验取值是这样的:常规业务盘 4 MiB 默认值;磁盘阵列上跑大文件、视频、备份库这类顺序 I/O 为主的场景,8 MiB 到 16 MiB 更合适;要在一个 VG 里切几十上百个小逻辑卷(比如给一堆虚拟机做盘),2 MiB 甚至 1 MiB 更省空间。改 PE 大小必须在vgcreate时定,建完之后再改只能靠vgconvert重建,代价很大。
3.5 参数速查表
| 参数 | 作用 | 典型用法 | 风险提示 |
|---|---|---|---|
| 无参数 | 标准创建 PV | pvcreate /dev/sdb1 | 遇到已有签名会报错,这是保护 |
-y | 所有提问自动 yes | pvcreate -y /dev/sdb1 | 仅免交互,不增强制能力 |
-f | 强制覆盖已有 PV 标签 | pvcreate -f -y /dev/sdb1 | 会覆盖设备上原有的 PV 信息 |
-ff | 强制覆盖任意识别到的签名 | pvcreate -ff -y /dev/sdb1 | 高危,可能连带覆盖文件系统 |
-t | 测试模式,不写盘 | pvcreate -t /dev/sdb1 | 适合脚本预演 |
-v/-d | 详细 / 调试输出 | pvcreate -d /dev/sdb1 | 排错利器,输出量大 |
-u UUID | 指定 PV 的 UUID | pvcreate -u xxxx-xxxx /dev/sdb1 | 迁移或重建时保持标识一致 |
--metadatasize | 元数据区大小 | pvcreate --metadatasize 64m /dev/sdb1 | 默认 1m,一般无需调整 |
--dataalignment | 数据区对齐大小 | pvcreate --dataalignment 256k /dev/sdb1 | 底层 RAID 条带对齐时用 |
--labelsector | label 所在扇区 | pvcreate --labelsector 2048 /dev/sdb1 | 极少用,保护盘首结构 |
--reportformat json | JSON 格式输出 | pvcreate --reportformat json /dev/sdb1 | 便于自动化解析 |
4. 完整实操:四盘扩容从裸盘到挂载
4.1 场景与规划
假设有这么一台业务机:原有系统盘 480G,上面已经有一套 LVM,卷组叫vg_sys。现在新加了四块 2T 的 SAS 盘,业务方要求给应用数据目录/data提供 6T 可用空间,并且明确说了"以后可能还要扩"。
规划思路是这样的:四块盘全部做 PV,合并成一个新卷组vg_data,PE 大小取 8 MiB(顺序 I/O 为主,稍大一点减少映射开销),先切一个 6T 的 LV 给/data,剩下约 2T 预留在 VG 里不动。以后扩容有两条路:VG 里还有空间就直接lvextend,空间用完了再加盘vgextend。文件系统选 XFS,因为支持在线扩容、大文件性能好,缺点是只能扩不能缩,对这个场景来说不是问题。
设备命名上,四块盘分别是/dev/sdb到/dev/sde。实际执行时我会用by-id路径,但为了命令可读性,下面还是写sdX,你照做的时候记得换成稳定路径。
4.2 分区与标记
先确认设备识别情况:
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT四块 2T 盘应该都出现在列表里,TYPE是disk,FSTYPE为空。如果SIZE显示的不是 2T 而是 1.8T 左右,这是正常的,厂商按十进制标容量,系统按二进制算。
接着给每块盘建 GPT 分区表并划一个占满全盘的分区。以/dev/sdb为例:
parted -s /dev/sdb mklabel gpt parted -s /dev/sdb mkpart primary 1MiB 100% parted -s /dev/sdb set 1 lvm on第一条建 GPT 标签,注意-s是脚本模式,不进入交互界面。按提示输入Yes确认。第二条从 1 MiB 开始划到盘尾,-s模式下不会提示对齐问题,parted会自动吸附。第三条把分区类型标记成 LVM,在 GPT 里这叫lvmflag,等效于 MBR 里的8e类型。这个标记不是强制的,pvcreate不看这个标志,但它能让lsblk、fdisk的输出更清晰,也让后来接手的人一眼看出用途。
四块盘批量处理可以写成循环:
for d in b c d e; do parted -s /dev/sd$d mklabel gpt parted -s /dev/sd$d mkpart primary 1MiB 100% parted -s /dev/sd$d set 1 lvm on done分区表写完后内核不一定立刻感知,保险起见跑一次partprobe /dev/sdb,或者干脆udevadm settle等一会儿。然后lsblk确认sdb1、sdc1、sdd1、sde1都出现了。
提示:如果大盘超过 2 TiB,MBR 分区表直接不支持,必须用 GPT。这一点在采购盘的时候就要想清楚,别等分区时才发现工具用错。
4.3 pvcreate 执行与校验
分区齐了就可以挂牌。先拿一块做测试预演:
pvcreate -t /dev/sdb1-t会完整走一遍流程但不写盘,输出里能看到它准备写入的元数据区大小、PE 起始位置这些信息。确认无误后正式执行:
pvcreate /dev/sdb1 /dev/sdc1 /dev/sdd1 /dev/sde1正常输出是四行Physical volume "..." successfully created.。如果某一块报错,整条命令会停在那里,前面成功的不会回滚,这时候先看错误信息再决定是补-f还是换设备。
校验用pvs:
pvs -o pv_name,pv_size,pv_free,pv_used,pv_uuid输出里应该看到四块盘,PV列显示设备名,PSize显示 1.82T 左右(2T 十进制换算成二进制的结果),PFree和PUsed相等,PV UUID每块都不一样。这时VG列是空的,因为它们还没被加进任何卷组,这是正常状态。
想看更细的信息用pvdisplay /dev/sdb1,它会列出 PV Name、VG Name、PV Size、Allocatable、PE Size、Total PE、Free PE、Allocated PE、PV UUID 这一串。注意这里的PE Size显示的是 0,因为 PE 大小要等加入 VG 之后才确定。想看物理布局用pvdisplay -m /dev/sdb1,能看到 label 位置、元数据区位置、以及 PE 的映射表(此时是空的)。
pvdisplay -m /dev/sdb1这条命令的输出会明确告诉你元数据区有多大、PE 从哪个偏移开始,是理解前文 1.2 节内容最直观的方式。
4.4 卷组、逻辑卷与文件系统
四块 PV 就位,建卷组:
vgcreate -s 8M vg_data /dev/sdb1 /dev/sdc1 /dev/sdd1 /dev/sde1-s 8M指定 PE 大小,这个参数一旦确定,后续往这个 VG 里加 PV 都必须遵守。vgcreate的输出会告诉你卷组创建成功、以及分配了多少个 PE。四块 1.82T 的盘,总共约 7.28T,按 8 MiB 一个 PE 算,大约 95 万个 extent。
验证一下:
vgs -o vg_name,vg_size,vg_free,vg_extent_size,vg_extent_countVSize约 7.28T,VFree同样是 7.28T,Ext显示 8.00m,说明 PE 设置生效了。
接着切逻辑卷:
lvcreate -L 6T -n lv_app vg_data-L指定大小,-n指定 LV 名字。也可以用-l按 extent 数量分配,比如-l 100%FREE拿走全部剩余空间。切完之后lvs能看到lv_app,Path列是/dev/vg_data/lv_app。
建文件系统:
mkfs.xfs /dev/vg_data/lv_appXFS 建在大容量 LV 上很快,几秒钟就好。如果想用 ext4,mkfs.ext4 -m 1 /dev/vg_data/lv_app,-m 1把保留块从默认 5% 降到 1%,能省出不少空间,这个参数在纯数据盘上很实用。
挂载并写入 fstab:
mkdir -p /data mount /dev/vg_data/lv_app /data blkid /dev/vg_data/lv_app拿到 UUID 之后写进/etc/fstab:
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /data xfs defaults,noatime 0 0noatime这个挂载选项值得加上,它禁止每次读文件都更新访问时间戳,在大量小文件读写的场景下能减少不少写 I/O。写完 fstab 一定要mount -a验证一遍,别等重启时才发现写错了导致进不了系统。
4.5 后续扩容与在线迁移
半年后/data满了,这时候有两种情况。
VG 里还有空间(本例还剩约 1.28T),直接扩 LV:
lvextend -L +1T /dev/vg_data/lv_app xfs_growfs /data如果是 ext4,最后一步换成resize2fs /dev/vg_data/lv_app。两个命令都是在线执行的,不需要卸载。lvextend也支持-l +100%FREE把剩余空间一次吃光。
VG 里没空间了,那就加盘。新盘/dev/sdf走一遍前面的分区和pvcreate,然后:
vgextend vg_data /dev/sdf1 lvextend -L +2T /dev/vg_data/lv_app xfs_growfs /datavgextend会把新 PV 的 PE 加进池子,注意新盘的 PE 大小必须和卷组一致,如果vgcreate时用了-s 8M,新盘pvcreate时就不用管,vgextend会按 VG 的设置来。
还有一种情况是硬件换代,要把数据从旧盘迁到新盘。LVM 的pvmove就是干这个的:
pvmove /dev/sdb1 /dev/sdf1这条命令会把sdb1上的所有 extent 在线迁移到sdf1,全程业务无感,只是会占用大量 I/O 带宽,建议放在业务低峰期跑。迁移完成后:
vgreduce vg_data /dev/sdb1 pvremove /dev/sdb1先把 PV 从 VG 里摘掉,再清除 PV 标签。顺序不能反,vgreduce之前必须先确认这块 PV 上没有已分配的 extent,否则会拒绝执行并提示你先pvmove。
4.6 回收与退役
整块盘要退役或者改用途,反向操作一遍:
umount /data lvremove /dev/vg_data/lv_app vgremove vg_data pvremove /dev/sdb1 /dev/sdc1 /dev/sdd1 /dev/sde1 wipefs -a /dev/sdb1lvremove会二次确认,脚本里加-f跳过。vgremove要求 VG 里没有 LV,否则会报错。pvremove会把 PV 标签抹掉,设备回到"未标记"状态。最后wipefs -a清掉残留的分区表和文件系统签名,这样这块盘拿去给别的系统用不会出现"设备上有未知签名"的告警。
有一个细节:pvremove只清除 LVM 自己的签名,不碰分区表。所以如果你前面是分区之后再pvcreate的,pvremove之后sdb1这个分区还在,只是不再是 PV。要让盘彻底干净,还得wipefs或者重新parted mklabel。
5. 报错排查实录
5.1 设备找不到或被过滤
最典型的报错是Device /dev/sdb1 not found (or ignored by filtering).。这句话有两个可能,得分开看。
第一种可能是设备真的不存在。分区刚建完内核没刷新,lsblk里看不到sdb1,这时候partprobe /dev/sdb或者partx -u /dev/sdb让它重新读分区表就行。虚拟机热插拔的盘也可能有延迟,echo "- - -" > /sys/class/scsi_host/host0/scan触发一次总线重扫,注意host0要换成实际的控制器编号,用ls /sys/class/scsi_host/看有哪些。
第二种可能是被lvm.conf的 filter 挡了。/etc/lvm/lvm.conf里有devices/filter和devices/global_filter两个配置项,前者影响 LVM 命令,后者在扫描的最早期就生效、性能更好。很多发行版的默认配置会排除loop、ram、fd这些设备,有些还会排除特定路径。如果设备名不在允许列表里,pvcreate就会认为它不存在。
查看当前配置和实际扫描结果:
lvmconfig devices/filter lvmconfig devices/global_filter pvscan -apvscan -a会列出所有被扫描到的设备,包括没被标记成 PV 的。如果目标盘不在这个列表里,基本可以确定是 filter 的问题。改 filter 之后不需要重启服务,LVM 命令每次执行都会重新读配置。改之前记得cp /etc/lvm/lvm.conf /etc/lvm/lvm.conf.bak,这个文件改错了会影响整个 LVM 子系统的识别。
5.2 已有签名与占用类报错
Can't initialize physical volume "/dev/sdb1" of volume group "vg_old" without -ff这条错误的意思是:这块盘上已经有 PV 标签,而且它属于一个叫vg_old的卷组。LVM 怕你误操作,拦住了。
正确处理顺序是先搞清楚vg_old是不是还在用。vgs看看这个卷组在不在,pvs -o pv_name,vg_name看还有哪些盘属于它。如果vg_old是历史遗留、确认可以丢弃,正规做法是:
vgremove vg_old pvremove /dev/sdb1 pvcreate /dev/sdb1用-ff直接覆盖也能达到目的,但它跳过了"这个 VG 还有没有别的盘"的判断。万一vg_old里另一块盘上还有在用的 LV,你这么一搞,那个 LV 就永远读不出来了。
另一类报错是Device /dev/sdb1 is used by another device.,通常发生在盘上有残留的 RAID 超级块或者 multipath 映射。前者用mdadm --zero-superblock清,后者要先在lvm.conf里配置 multipath 类型,让 LVM 认识这个映射关系,否则它会同时看到/dev/sdb和/dev/dm-0两个"不同的"设备指向同一份数据。
5.3 重复设备与 multipath 冲突
报错长这样:Cannot use device /dev/sdb1 with duplicates.或者WARNING: Duplicate VG name。这是 LVM 在多路径环境下最常见的问题。
原因是有多路径软件(或者 iSCSI、FC 链路冗余)时,同一块物理盘会通过多条路径暴露成多个块设备,比如/dev/sdb、/dev/sdc实际是同一块盘的两个路径。LVM 扫描时看到两个设备上有相同的 PV UUID,就会认为数据不一致,拒绝操作。
解决办法是在/etc/lvm/lvm.conf的devices段里确认types包含 multipath:
types = [ "multipath", 16 ]并且用global_filter把底层的/dev/sd*排除掉,只让 LVM 看到/dev/mapper/mpath*:
global_filter = [ "a|/dev/mapper/mpath.*|", "r|/dev/sd.*|" ]改完跑一次vgscan --cache刷新缓存。这个坑在接光纤存储的环境里几乎是必修课,配置不对的话,轻则看到重复 PV,重则pvcreate之后发现数据在另一条路径上读不到。
5.4 常见问题速查表
| 报错 / 现象 | 可能原因 | 排查命令 | 处理方式 |
|---|---|---|---|
Device not found (or ignored by filtering) | 设备未识别或 filter 排除 | pvscan -a、lvmconfig devices/filter | 刷新分区表或调整 filter |
Can't initialize ... without -ff | 设备已有 PV 标签且在 VG 中 | pvs -o pv_name,vg_name | 先vgremove+pvremove,再重建 |
Device is used by another device | 残留 RAID 超级块或多路径 | wipefs -n、dmsetup ls | 清超级块或配置 multipath |
Cannot use device with duplicates | 多路径导致同盘多设备 | ls -l /dev/disk/by-id | 配置global_filter排除底层设备 |
Metadata area is too small | 元数据区设得过小 | pvdisplay -m | 重建 PV,元数据取 1 MiB 以上 |
Partition is not properly aligned | 分区起点未对齐 | parted /dev/sdb align-check optimal 1 | 重建分区,起点从 1 MiB 开始 |
PV not found但lsblk能看到 | udev 规则或缓存未更新 | udevadm settle、vgscan --cache | 刷新缓存 |
pvcreate成功但pvs不显示 | VG 未建立时的正常表现 | pvs -a | 加-a看全部,或直接建 VG |
6. 几条踩过才记住的经验
6.1 关于 -ff 的使用边界
我见过太多脚本里默认写pvcreate -ff -y /dev/sdX,理由是"这样不会因为各种莫名其妙的原因失败"。这个习惯很危险,它把 LVM 的安全检查机制整个关掉了。
-ff的实际行为是:扫描设备上所有已知的魔术字符串,只要匹配到就覆盖。它不管那里是旧的 ext4 还是别的什么。有一次排查一台机器的数据丢失,最后定位到是初始化脚本拼错了盘符,-ff把一个还在用的备份盘给盖了头部,虽然文件系统数据区还在,但超级块没了,恢复花了整整两天。
我的做法是:脚本里默认不加-ff,遇到失败就停下来报警,让人来判断。确实需要自动化的场景(比如云主机初始化,盘肯定是干净的),加-f -y就够了,-f能处理绝大多数"有历史遗留"的情况。真需要-ff的时候,前面必须先跟一条wipefs -n把签名列出来,人工确认过再执行。
6.2 元数据备份比想象中重要
LVM 把 VG 元数据的多个副本分散存在各个 PV 上,听起来很安全,但有个致命场景:如果操作失误把所有PV 的头部都覆盖了,那就一个副本都不剩了。
/etc/lvm/archive/目录是这个问题的最后一道防线。每次 VG 发生变化(建卷组、加盘、扩 LV、删 LV),LVM 都会往这里写一个带时间戳和序号的文件。这些文件是纯文本,能用cat直接看,甚至能手动改。恢复的时候用:
vgcfgrestore -l vg_data vgcfgrestore -f /etc/lvm/archive/vg_data_00003-1234567890.vg vg_data-l列出这个 VG 的所有历史版本,挑一个时间点正确的恢复。这个操作不可逆,恢复前先把当前的/etc/lvm/backup/也备份一份。我一般会在做任何涉及lvremove、vgreduce这种破坏性操作之前,手动跑一次vgcfgbackup,多花两秒钟,省下两天时间。
还有个小技巧:/etc/lvm/archive/里的文件会不断累积,默认保留最近若干条。如果你的环境变更很频繁,可以适当调大/etc/lvm/lvm.conf里backup/archive段的retain_days和retain_min,让历史版本留得久一点。磁盘占用几乎可以忽略。
6.3 云主机和容器环境下的差异
在云平台上,pvcreate的行为有些细节和物理机不一样。
第一,盘符识别。云主机的磁盘通常是 virtio 设备,命名是/dev/vdb、/dev/vdc这种,by-id路径可能是virtio-xxxx形式。热插拔的盘有时需要手动 rescan,或者干脆重启实例才能看到。有些云平台还提供一种"设备名由平台指定"的模式,这种盘的名字是稳定的,可以直接用。
第二,已经做过快照或克隆的盘,可能带着旧的 LVM UUID。这时候pvcreate必须加-u重新生成 UUID,否则两台机器上会出现相同的 PV UUID,卷组激活时冲突。vgimportclone这个工具就是专门处理这类场景的,它会把克隆盘上的 PV 和 VG 标识全部换新。
第三,容器里跑 LVM 工具要谨慎。容器默认的权限集往往不够让pvcreate写块设备,即使加了--privileged,/dev的挂载方式和 udev 规则也可能让pvs看不到刚建的 PV。容器里更推荐的做法是把 LVM 操作放在宿主机上,容器只挂载最终的 LV。
一个很实用的习惯:任何环境里执行完pvcreate,立刻用pvs -a、vgs、lvs三个命令连查一遍,确认状态符合预期再往下走。我自己的操作记录里,很多问题都是在这一步被发现的,而不是等到挂载失败才回头看。
最后分享一个在长期维护中养成的小动作:给每个 PV 建完之后,把pvs的输出和pvdisplay -m的关键信息记到变更单里。一卷组几十块盘的时候,谁是后加的、PE 分布什么样、哪块盘上还有多少空闲 extent,靠记忆完全不靠谱。这些信息写下来,下次扩容或者排障时能省掉大量重新扫描和推断的时间。