做服务器运维和虚拟化这行,VMware里跑Ubuntu虚拟机再常见不过了。但凡是用了Ubuntu Server默认安装的人,十有八九会遇到一个让人头疼的问题:磁盘空间不够了。在VMware里把虚拟磁盘调大,重启进去一看,df -h还是老样子,甚至分区工具里能看到空闲空间,系统就是用不上。这时候就要聊到LVM(逻辑卷管理)了。这篇文章就把VMware环境下Ubuntu LVM扩展容量的完整操作讲透,从虚拟磁盘扩容到分区调整、PV/VG/LV扩容、文件系统resize,每一步干什么、为什么这么干、有哪些坑,全给你摊开说。
1. 扩容前准备与整体思路
1.1 为什么Ubuntu Server默认就用LVM
先说说LVM是个什么东西。LVM把物理磁盘或分区抽象成物理卷(PV),多个PV可以组成卷组(VG),再从VG里划分出逻辑卷(LV)给系统当“虚拟磁盘”用。这样做的核心好处是:你不再受单个物理分区的容量限制,空间可以在不同逻辑卷之间灵活调整,而且扩展的时候通常不需要重启机器。
打个比方,普通分区就像你租了一间固定大小的仓库,想变大只能把仓库拆了重建;LVM则像一个物业公司统一管理一整层楼,哪个租户需要更多空间,直接从公共区域划一块过去就行,不用动墙。
Ubuntu Server安装程序在检测到整块磁盘时,默认会创建LVM卷组,根目录就落在逻辑卷上。很多云镜像、虚拟化模板也沿用这个习惯。因此在VMware里扩容Ubuntu虚拟机,绕不开LVM这一层。如果你不懂LVM,直接在VMware里把磁盘扩大,系统只能看到物理磁盘变大了,但文件系统还是缩在原来的逻辑卷里,等于白扩。
1.2 VMware层面对虚拟磁盘扩容的正确姿势
VMware层面扩容挺简单,但有几个前提必须满足。
打开虚拟机设置,在“硬盘”一栏右侧能看到“扩展”按钮。如果虚拟机正在运行,有些场景可以直接扩展,但为了稳妥,我向来建议先关机再扩。尤其数据库、生产环境这类对数据一致性要求高的,千万别图省事在线扩。
具体操作:
- 关闭Ubuntu虚拟机。
- 编辑设置,选中硬盘。
- 磁盘大小右侧点“扩展”,输入新的总大小。
- 确认后等待VMware完成操作。
这里有个特别容易卡壳的坑:如果虚拟机有快照,扩展按钮是灰色的,点不动。VMware不允许对带快照的虚拟磁盘做扩容。必须先把快照删掉。删快照会把虚拟机当前状态“合并”进基础磁盘,这个过程中虚拟机状态应该关机或处于正常运行状态,千万不要在删除快照期间强制断电,否则虚拟磁盘可能损坏。如果快照里有重要数据又不想丢,可以先克隆虚拟机或者先开机制作备份,再处理快照。
扩展完成后,物理磁盘大小变了,但分区、PV、LV、文件系统全都还没变。接下来的顺序必须严格:分区扩容 -> PV扩容 -> LV扩容 -> 文件系统扩容。任何一步颠倒都可能造成数据不一致。
1.3 整体操作蓝图
先给一张执行顺序图,后面每一步都按这个来:
- VMware层面:关机、删快照(如有)、扩展虚拟磁盘。
- 开机,用
lsblk确认物理磁盘的新容量。 - 扩展物理分区(通常是
/dev/sda5或/dev/sda3)。 - 用
pvresize刷新物理卷,让PV识别分区新空间。 - 用
lvextend把空闲空间划给目标逻辑卷。 - 用
resize2fs或xfs_growfs扩展文件系统。 df -h验证最终容量。
这个顺序为什么不能乱?因为LVM是分层的:文件系统建在LV上,LV建在VG上,VG建在PV上,PV建在物理分区上。你只有先把底层的空间“喂”到上层,上层才能拿到新容量。如果直接扩大文件系统,而LV没扩,文件系统会报错;如果LV扩了但PV没扩,LV会用掉VG里的空闲空间,等下次重启可能卷组空间又不够。所以,低层到高层,一层层来。
2. 虚拟机磁盘扩容后的系统侧检测与分区调整
2.1 开机后用lsblk确认新磁盘空间
虚拟机启动后,第一件事就是打开终端,敲:
lsblk这个命令会非常清晰地列出所有块设备。你会看到类似这样的输出:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sda 8:0 0 100G 0 disk ├─sda1 8:1 0 1M 0 part ├─sda2 8:2 0 1G 0 part /boot └─sda5 8:5 0 99G 0 part └─vgubuntu-root 253:0 0 99G 0 lvm /假如原来磁盘是50G,现在VMware里扩展到了100G,lsblk应该显示sda为100G,但sda5还是50G。如果sda都还是旧大小,说明VMware扩展没生效,或者虚拟机没识别到新容量。这时候可以重启一次,一般就会看到。
还有一种情况:VMware对SCSI控制器热添加支持很好,但有些老版本或特定控制器需要重新扫描SCSI总线。可以用:
echo "- - -" | sudo tee /sys/class/scsi_host/host0/scan对host0、host1等都试一遍。不过最省心的还是直接重启虚拟机。
确认物理磁盘容量到位后,再确认LVM当前状态:
sudo pvdisplay sudo vgdisplay sudo lvdisplaypvdisplay会显示PV大小,比如现在还是50G,那我们就知道需要扩容分区和PV了。
2.2 处理物理分区:用growpart而不是fdisk
这是整个操作里最容易被搞砸的一步。
很多教程会让你用fdisk删掉旧分区、重建一个大分区。对普通数据盘这么干勉强可以,但对承载LVM PV的分区来说,直接删了重建风险极大。因为LVM的PV元数据存放在分区头部,你要是删分区时记错起始扇区,PV元数据就废了,卷组有可能丢失。我见过不止一个人因为想“顺手把分区删了重建”结果整个卷组挂不上,数据全乱。
正确做法是用growpart这个工具。它做的事情本质上是重写分区表,把分区结束位置挪到磁盘末尾,同时保留起始位置不变。这样分区数据从头到尾没有任何位移,安全性高得多。
Ubuntu通常自带cloud-guest-utils这个包,里面就有growpart。如果提示命令不存在,先安装:
sudo apt update sudo apt install cloud-guest-utils然后查看当前分区结构,确认要扩展的分区号。假如sda5是LVM PV所在分区,执行:
sudo growpart /dev/sda 5注意写法:第一个参数是磁盘设备名/dev/sda,第二个参数是分区号5,不是/dev/sda5。growpart会自动把第5个分区的结束扇区扩展到磁盘可用的最大位置。
执行后会输出类似“CHANGED: partition=5 start=... old: end=... new: end=...”的信息。再跑一遍lsblk,sda5的大小应该已经变成新的容量了。
如果你的LVM PV直接建在整个磁盘上(比如/dev/sdb没有分区表,整块盘就是PV),那就跳过growpart这步,直接走到后面pvresize /dev/sdb即可。
如果你的分区是NVMe设备(比如/dev/nvme0n1p3),同样可以用growpart /dev/nvme0n1 3来扩展。
2.3 分区扩容后的校验与常见异常
growpart执行完之后,用lsblk确认分区已经变大。如果分区大小没变,先别急着往下走,排查几个问题:
第一个,内核还没刷新分区表。growpart虽然改了分区表,但内核缓存可能还是旧的。执行:
sudo partprobe让内核重新读取分区表。有时候partprobe也刷不进去,因为分区正在使用,这时可以重启一次。
第二个,报错说“unexpected output in sfdisk”。这通常是分区表状态和工具预期不一致,常见于之前使用过parted或fdisk改过分区。可以先查看分区表:
sudo parted /dev/sda print确认磁盘确实有未分配空间,分区格式正常。然后再试一次growpart,或者用sudo sfdisk -d /dev/sda看看分区定义。
第三个,MBR分区表中的扩展分区。有些老系统会把LVM PV放在逻辑分区(比如sda5),而sda5属于扩展分区内部的逻辑分区。growpart对这类逻辑分区的支持要看具体情况。如果遇到问题,最稳妥的办法是提前规划:在安装Ubuntu时如果打算长期用LVM,尽量让LVM PV直接建在主分区上,或者用GPT分区表。Ubuntu Server默认安装到整块磁盘时,一般会创建主分区和逻辑分区。但也不用太担心,多数情况下growpart能正常处理。
还有一点要记住:如果分区表是GPT,磁盘末尾有备份的GPT头部,growpart扩展分区时不会去覆盖备份头。如果磁盘已经扩到最大,但还有几个MB的剩余空间,那可能是GPT备份保留区,不用担心,不影响使用。
3. LVM层面的扩容:pvresize、lvextend与文件系统
3.1 扩展物理卷:pvresize
分区扩好了,接着让LVM认账。执行:
sudo pvresize /dev/sda5pvresize会重新扫描这个物理卷,把PV的大小更新为分区的新大小。如果分区是/dev/sdb整块盘作为PV,直接:
sudo pvresize /dev/sdb执行后可以用:
sudo pvdisplay看到PV Size已经变成扩展后的容量,Free PE(空闲物理扩展块)也有了数值。PE全称是Physical Extent,物理扩展块,LVM分配空间的最小单位,默认通常是4MiB。所以看到PE数量增加,就说明VG拿到了新空间。
这里有个细节,pvresize不会自动把VG里的空闲空间分配给某个LV,它只是刷新了“仓库总面积”。真正要给你根目录用的空间,还得靠下一步lvextend。
3.2 确定目标逻辑卷:用lvextend把空间划给根目录
先用df -h看当前根目录挂载点对应的逻辑卷。比如:
df -h /输出里文件系统列可能是/dev/mapper/ubuntu--vg-ubuntu--lv,注意这里有个细节:卷组和逻辑卷名字里的横杠在/dev/mapper路径里会变成双横杠。也可能显示为/dev/mapper/vgubuntu-root。
再用lvdisplay确认逻辑卷信息:
sudo lvdisplay看到逻辑卷路径,比如/dev/vgubuntu/root,这时候就可以扩充它了。
最简单的方式,把所有空闲空间都分配给这个逻辑卷:
sudo lvextend -l +100%FREE /dev/vgubuntu/root-l是按PE数量扩展,+100%FREE表示把VG中当前所有可用PE全部划到这个LV里。这台虚拟机如果只有这一个LV,这么操作最干净。
如果你想保留一点空间给其他逻辑卷,可以按指定大小扩展:
sudo lvextend -L +20G /dev/vgubuntu/root或者扩展到绝对大小:
sudo lvextend -L 80G /dev/vgubuntu/root执行完lvextend后,再跑lvdisplay或lvs,可以看到LV Size已经变大了。但这时候df -h里的根目录容量仍然没变,因为文件系统还不知道LV扩大了。所以下一步才是关键。
3.3 文件系统扩容:ext4与xfs的差异
文件系统扩容必须和文件系统类型匹配,这一点很多人翻车。
先确认根目录文件系统类型:
blkid /dev/vgubuntu/root或者直接:
df -T /输出里Type一列会告诉你是ext4还是xfs。
如果是ext4(Ubuntu Server默认通常就是ext4),用:
sudo resize2fs /dev/vgubuntu/rootresize2fs会读取文件系统当前块数,结合设备新大小,把文件系统扩展到整个设备。这个命令在挂载状态下也能执行,ext4支持在线扩容。执行后会看到类似“Resizing the filesystem on /dev/... to ... blocks”的信息。
如果是xfs,命令要换成:
sudo xfs_growfs /注意,xfs_growfs的参数是挂载点,不是设备路径。它可以直接在线扩容,不需要卸载。千万不要对xfs使用resize2fs,系统会提示找不到有效的超级块。
最后再执行:
df -h /看到根目录大小已经变成新容量,整个流程就算正式走完了。
4. 实战踩坑:从快照到分区到文件系统的排查记录
4.1 快照导致虚拟磁盘扩展按钮置灰
这是VMware里最经典的一个坑。虚拟机建好之后,很多人习惯在装完系统、打完补丁后打个快照当“后悔药”。结果等到需要扩容时,打开设置一看,硬盘的“扩展”按钮是灰色的,鼠标点上去毫无反应。
原因很简单:VMware不允许对存在快照的虚拟磁盘进行容量调整。因为快照文件依赖原始磁盘的虚拟RDM映射和CoW结构,扩容会破坏快照链的一致性。
遇到这种情况,常规操作是先删除快照。快照管理器里选中快照点删除,VMware会执行合并操作,把快照中的数据写回基础虚拟磁盘。如果快照创建之后虚拟机的磁盘数据变动很大,合并可能需要一段时间,期间千万不要断电或强制关闭VMware。
但删除快照意味着你失去了回滚点。所以更稳妥的流程是:如果对当前系统状态不放心,先克隆一台虚拟机(完整克隆),或者用导出OVF方式备份,确认备份可用后再删快照、扩容。我自己的操作习惯是:把快照删除后,再创建一个新的“扩容前”快照,这样扩容过程中万一出问题还能回退。注意,这个新的快照是在扩容前的原始状态上创建的,等扩容完以后要再删掉它,否则以后又要扩容时又得删快照,循环麻烦。
4.2 扩展后虚拟机里看不到新磁盘空间
有一次我给一台Ubuntu 20.04虚拟机扩容,VMware里已经显示磁盘从60G变成100G了,但进系统后lsblk里的sda依然显示60G。排查了很久,最后发现虚拟机使用的SCSI控制器类型和VMware版本之间存在兼容问题,热添加扫描没生效。
后来我总结了一套排查顺序:
- 先重启虚拟机。最直接,90%的问题重启能解决。
- 重启后还不行,检查
dmesg | grep -i scsi和/sys/class/scsi_host/目录,挨个主机编号执行echo "- - -" > /sys/class/scsi_host/hostX/scan,强制触发SCSI设备重新扫描。 - 如果虚拟磁盘是NVMe类型,检查
/sys/class/nvme/,重新扫描方式略有不同,但重启基本能解决。 - 最后检查VMware虚拟机的磁盘类型。如果原来用IDE接口,扩容支持度不好,建议把磁盘控制器改成SCSI或NVMe,改控制器阵列可能需要做一次克隆迁移,别忘了重新配置引导顺序。
还有一个很小的坑:如果虚拟机里跑的是比较老的Ubuntu内核,对超过2T的磁盘可能会有MBR限制,需要确认分区表是GPT。LVM本身不限制容量,但MBR分区表最大只能寻址2T空间,超了就会有问题。新装的Ubuntu默认用GPT,但老虚拟机难说。
4.3 growpart报错与手动调整分区表
growpart正常情况很省心,但偶尔会报一个奇怪错误:
FAILED: unable to grow partition 5: unexpected output in sfdisk这种错误绝大多数情况下是因为分区表状态和内核不一致,或者分区表本身就有点“脏”。先用partprobe刷新,再跑一次growpart。如果还不行,就手动用parted检查:
sudo parted /dev/sda unit s print确认分区起始扇区、结束扇区、剩余空间大小。只要起始扇区没错,理论上可以把结束扇区手动改到磁盘末尾。但手动操作前必须搞清楚分区表类型,GPT和MBR的命令不同,MBR还有主分区和逻辑分区嵌套问题,没把握就不要轻易动。
我的建议是:遇到growpart死磕不动,干脆直接从备份恢复或者重装系统,不要硬来。LVM PV里的数据可比多折腾出来的几十G空间值钱得多。
4.4 xfs_growfs和resize2fs用错导致报错
很多从CentOS转过来的朋友习惯了用resize2fs,到了Ubuntu上发现根文件系统是xfs,直接执行resize2fs,报错:
resize2fs: Bad magic number in super-block while trying to open /dev/vgubuntu/root看到这个提示,八成是文件系统类型弄错了。先df -T /或blkid看看类型再动手。
如果确认是xfs,使用xfs_growfs /,注意参数是挂载点。xfs文件系统不支持缩容,所以当初创建逻辑卷的时候就不要随便缩减,否则只能备份重来。ext4支持在线扩容,也支持离线缩容,但生产环境我从不缩容,除非是测试机。
4.5 一块磁盘不够用,再加一块盘并加入VG
如果根目录扩容一次还不够,比如数据增长特别快,可以给虚拟机再添加一块新虚拟磁盘,然后把新盘加入VG,再把空间分配给LV。这是LVM的另一个核心玩法。
流程是这样:
- VMware里添加一块新虚拟磁盘,比如50G,设备名为
/dev/sdb。 - 开机后,创建物理卷:
sudo pvcreate /dev/sdb- 把新PV加入现有VG。先找到VG名:
sudo vgs通常叫vgubuntu。然后:
sudo vgextend vgubuntu /dev/sdb- 最后继续用
lvextend和resize2fs或xfs_growfs扩展目标LV和文件系统。
这种方式不用动原磁盘分区,风险更小,而且可以反复添加多块盘。不过要注意,如果以后想从VMware里移除某块旧盘,得先pvmove把数据迁移到其他PV上,再从VG里移除,否则数据就丢了。
5. 实际操作经验与最终建议
5.1 最推荐的扩容流程速查表
给一台新装的Ubuntu Server虚拟机做LVM扩容,我最顺手的流程就是下面这套,可以直接抄:
| 步骤 | 操作 | 常用命令 |
|---|---|---|
| 1 | 关机,删除快照,VMware扩展虚拟磁盘 | VM GUI操作 |
| 2 | 开机确认磁盘容量 | lsblk |
| 3 | 扩展物理分区 | sudo growpart /dev/sda 5 |
| 4 | 刷新PV容量 | sudo pvresize /dev/sda5 |
| 5 | 扩展逻辑卷 | sudo lvextend -l +100%FREE /dev/vgubuntu/root |
| 6 | 扩展ext4文件系统 | sudo resize2fs /dev/vgubuntu/root |
| 7 | 确认最终容量 | df -h / |
如果是xfs文件系统,把第6步换成sudo xfs_growfs /即可。如果PV直接建在整块盘上,第3步不需要,第4步把设备名改成/dev/sda就行。
5.2 扩容前的数据备份策略
虽然LVM扩容本身相对成熟,但自己动手操作生产虚拟机之前,还是要做备份。我见过太多人自信满满地说“LVM扩容很简单”,然后分区表写错,卷组识别不出来。
最低成本的备份方案:虚拟机开启时在VMware里做一次文件系统级别的快照?注意,VMware快照和LVM快照不是一回事,VMware快照对虚拟磁盘的一致性保障取决于虚拟机配置和磁盘IO状态,对数据库这类应用建议先停机或确保应用处于静默状态。
更稳的办法是:在VMware层面克隆虚拟机。完整克隆一份,确认克隆机能正常启动,再对原机操作。这样真出问题,还能从克隆机恢复数据。
如果机器本身数据量不大,也可以直接tar打包关键目录,或者用duplicity、rsync同步到其他存储。但不管用哪种方式,核心原则是:在没有可用回滚方案之前,不碰分区表。
5.3 我的几点体会
折腾过这么多台LVM虚拟机,最大的体会是:LVM扩容这套东西,看着命令不多,但每一步都在操作元数据。分区表是元数据,PV头部是元数据,VG描述符是元数据,文件系统超级块也是元数据。任何一个环节没刷新,系统就会给你“颜色”看。
所以操作完每一步,我都习惯用对应命令验证一下:
lsblk看分区大小pvs看PV大小vgs看VG空闲空间lvs看LV大小df -h看文件系统大小
这五条命令挨个跑一遍,基本能定位出是哪一层没生效。
还有一个小经验:如果你要给运行着MySQL或PostgreSQL的虚拟机扩容,最好先停掉数据库服务,或者至少确保没有大批量写入后再执行lvextend和resize2fs。虽然ext4和xfs都支持在线扩容,但文件系统元数据操作期间如果发生意外断电,恢复起来会非常麻烦。停机五分钟换来的安全边际,比什么后手都值。
扩容完成后,别忘了重启一次虚拟机,确认所有服务正常挂载、分区表干净、日志里没有异常IO错误。我习惯再看一眼/etc/fstab,确认里面用的是UUID而不是设备名。因为LVM设备路径在重启后理论上不会变,但一旦以后加盘、删盘,设备顺序有可能会变,用UUID挂载最保险。查看逻辑卷UUID可以用blkid /dev/vgubuntu/root,然后把/etc/fstab里的对应项改成UUID=...。
如果你按照这套流程走下来,基本上不会再被VMware里Ubuntu磁盘空间不足的问题卡住。以后遇到磁盘不够,你只需要记住:先动VMware,再动分区,再动PV,再动LV,最后动文件系统。顺序对了,一切都很顺。