前两天夜里我那台天天在跑的 Ubuntu 22.04 虚拟机突然开始整活:SSH 能连进去,但 docker、nginx 和 postgresql 全都处于半死状态,journalctl 疯狂刷 no space left on device。df -h 一看,根分区直接 100%。这种事经历过一次就知道有多痛——服务不会立刻崩,但会在你最不想看到的时候一个个掉链子。
这台虚拟机跑在 VMware Workstation Pro 17 上,系统是 Ubuntu 22.04 LTS,当初创建磁盘时只给了 40GB,结果 Docker 镜像、日志、构建缓存一多,空间就见底了。我当时的第一反应是清理垃圾,但删掉几个 GB 之后发现根本不顶用,数据增长的速度远比清理快。最终决定走正经路子:给虚拟磁盘扩容,让 Ubuntu 真正用上更大的空间。
这篇文章就是把我在 VMware 里给 Ubuntu 22.04 虚拟机扩磁盘的完整过程拆给你看。从判断磁盘为什么满、到 VMware 层把虚拟磁盘加大、再到 Ubuntu 系统内部分区和文件系统扩容,每一步的操作和原理都会讲清楚,顺带把我踩过的坑也一并交代。适合所有被虚拟机磁盘空间逼到墙角的人,尤其是跑 Ubuntu Server 或 Docker 环境的老哥。
1. 动手之前的定位:磁盘满、分区满、inode 满,是三种完全不同的病
1.1 先用两条命令搞清楚,到底卡在哪一层
扩容不是打开 VMware 点两下就完事的,你得先知道 Ubuntu 里到底哪里满了。我习惯先跑这两条:
bash复制df -h
df -i
df -h 看的是文件系统空间占用,df -i 看的是 inode 使用率。很多人只盯着第一条,结果明明空间还有 20%,服务照样报 No space left on device,一查才发现是 inode 爆了——磁盘上已经塞满了海量小文件,每个文件都要消耗一个 inode,存量文件把 inode 池吃光之后,哪怕物理空间还有富余也建不了新文件。
我自己那次是两种问题叠加:根分区 filesystem 空间耗尽,同时 /var/log/journal 底下的日志文件像滚雪球一样膨胀。所以定位阶段顺手再查一下大目录:
bash复制sudo du -xh --max-depth=2 / 2>/dev/null | sort -rh | head -30
这条能把根目录下最占空间的目录按大小排出来。我见过太多人漫无目的地删文件,其实真正的元凶就那么几个:Docker 的 overlay2 目录、systemd journal 日志、apt 缓存、还有 MySQL / PostgreSQL 的数据目录。
1.2 摸清你的磁盘布局:MBR 还是 GPT,LVM 还是普通分区
在动手之前,花两分钟把 Ubuntu 的磁盘布局看清楚,能帮你少走一大截弯路。执行:
bash复制sudo fdisk -l /dev/sda
lsblk -f
重点看两件事。
第一,磁盘分区表是 MBR(dos)还是 GPT。这会影响到后续分区扩展工具的行为,虽然 growpart 两种都支持,但扩展时如果分区表是 MBR,且根分区被四个主分区占满了,后续扩展空间可能会撞上限。Ubuntu 22.04 默认安装时,UEFI 模式一般用 GPT,传统 BIOS 模式可能是 MBR。
第二,根分区到底是挂在普通分区上,还是挂在 LVM 逻辑卷上。这一步最关键,因为后续的操作路径完全不同。
用 lsblk -f 看输出就很直观。如果看到 /dev/mapper/ubuntu--vg-ubuntu--lv 挂载在 / 上,说明你用的是 LVM 布局,Ubuntu Server 默认安装选 "Use an entire disk and set up LVM" 时就是这种结构。如果直接看到 /dev/sda3 挂载在 / 上,那就是普通分区布局,Ubuntu Desktop 默认就是这种。
有人觉得 LVM 麻烦,其实恰恰相反,LVM 在扩容场景下反而更灵活。但前提是你得知道它的层级关系。不管是普通分区还是 LVM,扩容的本质都是同一件事:虚拟磁盘大了之后,要让分区跟着大,文件系统再跟着分区大。区别只是 LVM 中间还多了一个 PV、VG、LV 的映射层。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. VMware 层面先动手:在虚拟机设置里把虚拟磁盘“加量”
2.1 扩展磁盘前的安全准备:快照、关机、备份,按这个顺序确认
很多人在 VMware 里直接右键虚拟机,点设置,然后发现硬盘的扩展按钮是灰的,第一反应是软件出 bug 了。其实绝大多数情况是快照在捣鬼。
只要虚拟机上挂着快照,VMware Workstation Pro 就不允许修改虚拟磁盘的大小。这是因为快照本质上保存了磁盘在某一个时间点的状态链,扩磁盘意味着要改 vmdk 的基础容量描述,这跟快照链记录的位置信息会互相冲突,VMware 干脆直接锁死这个操作。
所以动手之前先去快照管理器看一眼,有快照就合并或者删掉。删快照之前千万想清楚,快照可能是你手头的回滚保险,尤其是系统层面改造成半截发现搞砸了的场景。我的习惯是:如果虚拟机上只有一两个临时快照,果断合并;如果快照有生产环境回滚意义,那就先把关键数据用 scp 或者挂载共享目录拷一份出来,再删快照。
再一个硬性要求:扩展虚拟磁盘之前在 guest 系统里把虚拟机关机。不是说不能在线扩,在线状态下 VMware 也能执行一部分磁盘操作,但分区扩展这种底层变更,Linux 内核在运行期间对磁盘参数变更的感知并不总是那么顺利,刚扩展完你去 lsblk 看可能还是旧的大小,重启之后才刷新出来。为了避免这种说不清道不明的状态,老老实实关机再操作。
最后还有一条,如果有人问我有什么最重要的建议,我一定说:重要数据先备份。扩容本身不算高风险操作,但涉及到分区表和文件系统的调整,断电、误操作、工具异常,任何一个环节出问题都可能让系统起不来。别拿生产虚拟机的唯一副本裸奔。
2.2 在 VMware Workstation Pro 17 中完成虚拟磁盘扩展
确认快照清干净、虚拟机关机后,流程就很简单了:
- 在 VMware Workstation Pro 17 的虚拟机列表里,右键当前虚拟机,选择 "Settings"(设置)。
- 左侧选择 "Hard Disk"(硬盘),右侧会显示当前磁盘容量信息。
- 点击 "Utilities"(实用工具)按钮,在展开的菜单里选择 "Expand"(扩展)。
- 输入你期望的磁盘新大小。比如原来的 40GB,我直接扩到了 120GB。
- 点击 "Expand" 确认,等待 VMware 完成文件系统层面的磁盘扩容,完成后点 "OK" 保存设置。
有一点值得说明:这里扩展的是虚拟磁盘本身,也就是 .vmdk 文件对应的底层容量。比如你把虚拟磁盘从 40GB 扩到 120GB,那么虚拟机里看到的 /dev/sda 这块裸盘就会从 40GB 变成 120GB。
但你在这个阶段启动系统,再用 df -h 一看,根分区还是老样子,使用率照样 100%。很多第一次干这事的人到这里就懵了,其实这个现象完全正常,因为变大的只是“磁盘”,磁盘里面的“分区”和“文件系统”还没有跟着动。
打个比方:你原本租了个 40 平的小房间,现在物业跟你说房子扩建到了 120 平。房子(虚拟磁盘)确实变大了,但你里面那面隔断墙(分区)还在原地,你实际能住的空间(文件系统)还是原来的 40 平。要真正用上新面积,得自己动手把隔断墙往后退。
2.3 顺带说一嘴:扩容原盘和加一块新盘,怎么选
扩容过程中我也考虑过另一个方案:不扩原盘,直接给虚拟机加一块新虚拟硬盘,挂载到某个目录下用。这两种方案的取舍其实很简单:
- 根分区空间不够,又想把问题一次性解决掉,扩容原盘是最直接的路径,不用迁移数据,不用改挂载点。
- 如果只是某个数据目录膨胀,比如
/var/lib/mysql要单独隔离出来,那加一块新盘、格式化、挂载到新目录,再迁移数据过去,也是常见玩法。好处是数据跟系统盘分开,以后系统盘出问题数据还在。
但既然标题就是磁盘扩容,我建议优先把原盘扩完。加新盘看着简单,实际上要处理新分区格式化、fstab 永久挂载、数据迁移和目录权限这些事,反而更繁琐。
3. Ubuntu 22.04 系统内分区与文件系统扩容:真正的重头戏
3.1 普通分区布局:一条 growpart + resize2fs 走到底
这是 Ubuntu Desktop 默认安装时最常见的场景。启动系统后,先用 lsblk 确认当前磁盘结构,我当时看到的输出大概是这样的:
code复制NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 120G 0 disk
├─sda1 8:1 0 1G 0 part /boot/efi
├─sda2 8:2 0 1G 0 part /boot
└─sda3 8:3 0 38G 0 part /
注意看,/dev/sda 已经是 120G 了,但根分区 sda3 还是原来的 38G,多出来的空间目前处于未分配状态。这时候就要分两步走:先把分区扩大,再把文件系统扩大到整个分区。
Ubuntu 22.04 上推荐用 growpart 这个工具来扩展分区,它不是直接操作分区起始位置,而是把指定分区向磁盘末尾方向扩展,正好是我们要的效果。如果系统里没有这个命令,先装一下:
bash复制sudo apt update
sudo apt install -y cloud-guest-utils
然后执行:
bash复制sudo growpart /dev/sda 3
注意 growpart 的写法,第一个参数是磁盘名,第二个参数是分区号,中间有个空格,不是 /dev/sda3 这种写法。执行成功之后会输出类似 CHANGED: partition=3 start=... old: size=... end=... new: size=... end=... 的信息。
接下来扩展文件系统。Ubuntu 22.04 根分区默认是 ext4,直接:
bash复制sudo resize2fs /dev/sda3
resize2fs 会在线把 ext4 文件系统扩展到整个分区大小,不需要卸载根分区,这一点很方便。执行完之后再次 df -h,根分区应该已经变成新的容量,比如 116G 左右,使用率大幅下降。
整个过程就是这么短,核心就是两条命令。但你要是自己拿着 fdisk 去删了分区再重建,那就完全不是一回事了,那是在玩火。
3.2 LVM 布局:Ubuntu Server 默认结构的完整扩容链
如果你的 Ubuntu 装的是 Server 版,安装时用了 "Use an entire disk and set up LVM",那 lsblk -f 的输出结构会是这样的:
code复制NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 120G 0 disk
├─sda1 8:1 0 1G 0 part /boot/efi
├─sda2 8:2 0 1G 0 part /boot
└─sda3 8:3 0 38G 0 part
└─ubuntu--vg-ubuntu--lv 253:0 0 38G 0 lvm /
注意最后一行,/ 挂载在 LV 上,不直接挂在 sda3 上。sda3 只是 LVM 的物理卷(PV),真正的根文件系统是在 LV 里面。这时候扩容链条比普通分区多一层:
第一步,扩展物理卷所在的分区。 跟普通分区一样的操作:
bash复制sudo growpart /dev/sda 3
第二步,让 PV 感知到分区变大了。
bash复制sudo pvresize /dev/sda3
执行完可以用 pvs 检查一下,物理卷的 PFree(可用空间)应该已经增加了。
第三步,把 VG 里多出来的空闲空间分配给 LV。
先用 vgs 和 lvs 确认卷组名和逻辑卷名,Ubuntu Server 默认的卷组是 ubuntu-vg,逻辑卷是 ubuntu-lv。然后扩展 LV:
bash复制sudo lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv
-l +100%FREE 的意思是,把卷组里所有空闲的物理扩展(PE)全部交给这个逻辑卷。如果你想留点余量以后建独立 LV,可以写成 -L +80G 这种形式。
第四步,扩展文件系统。
如果这个 LV 上也是 ext4:
bash复制sudo resize2fs /dev/ubuntu-vg/ubuntu-lv
如果当初格式化的时候选了 XFS,那不能用 resize2fs,要用:
bash复制sudo xfs_growfs /
所以先确认文件系统类型再选工具,别想当然。
3.3 一步对不上号的常见情形:我这篇里最容易出岔子的一层
上面两套流程看着简单,但实际操作里最容易出的问题就是“层级对不上”。
比如你在 LVM 环境里直接对 /dev/mapper/ubuntu--vg-ubuntu--lv 跑 resize2fs,可 PV 所在分区没动过,LV 也还是老样子,那 resize2fs 会告诉你“文件系统已经是 38G 了,没啥可扩的”。反过来,如果你只执行了 growpart 扩展分区,没跑 pvresize,那 LVM 也感知不到新空间,后面 lvextend 自然也分不到空间。
我用一张表把两条链路理一理,方便你对照:
| 层级 | 普通分区布局 | LVM 布局 |
|---|---|---|
| 物理磁盘 | VMware Expand,sda 变大 | 同左 |
| 分区 | growpart /dev/sda 3 |
growpart /dev/sda 3 |
| PV | 无 | pvresize /dev/sda3 |
| LV | 无 | lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv |
| 文件系统 | resize2fs /dev/sda3 |
resize2fs /dev/ubuntu-vg/ubuntu-lv |
这表的每一行都对应命令行里的一步操作。少了任何一层,最终 df -h 看到的结果都不会让你满意。
还有一个细节:resize2fs 对 ext4 是支持在线扩展的,所以不用担心要进入单用户模式或者卸载根分区。但如果你是在 root 文件系统上操作,命令执行过程中尽量不要去动其他任务,等它跑完再继续。我那次的输出就是温和的几行信息,几十秒就完事了。跑完之后我习惯再执行一次 df -h 和 lsblk,确认最终容量确实上去了,再重启一次验证系统能稳定起来。
4. 我踩过的坑:快照、swap 末尾、PMBR mismatch 和“进不去系统”
4.1 快照导致扩展按钮失效:不是故障,是保护机制
前文提过一次,这里再细说。如果你在 VMware 设置里点开硬盘扩展,发现 "Expand" 按钮是灰色的,页面还提示有快照存在,千万别硬来。这时候正确的做法是到 "Manage Snapshots"(快照管理器)里把快照删掉或合并掉,再做扩展。
我自己的教训是:某次给一台虚拟机扩容时,明明 VM 里快照已经删了,结果点扩展还是灰的,折腾半天才想起这台机器的快照管理界面里还残留着一个旧快照。所以别只看列表,进快照管理器确认一下状态再回来操作。
如果你很不幸只有这一个快照,但它是 10 天前的系统状态,而你 10 天里已经改了一堆东西,合并快照会把这段增量写到基础盘里,是不可逆的。这时候先想清楚:要不要做镜像备份,或者直接把快照改名导出一份做保险。这一点在操作前十分钟想明白,比事后拍大腿好一百倍。
4.2 根分区后面还躺着 swap 分区:growpart 扩不动怎么办
Ubuntu 22.04 默认安装时,如果内存不大,安装器经常会在根分区后面分配一个 swap 分区。结构大概是这样的:
code复制sda 8:0 0 120G 0 disk
├─sda1 8:1 0 1G 0 part /boot/efi
├─sda2 8:2 0 1G 0 part /boot
├─sda3 8:3 0 38G 0 part /
└─sda4 8:4 0 2G 0 part [SWAP]
这种时候你想扩 sda3,growpart /dev/sda 3 就会拒绝,因为 sda3 的末尾不是磁盘末尾,后面还有 sda4 挡着。
处理这个局面的思路是把 swap 从物理分区挪走,给根分区让路。最省事的方案是关掉 swap 分区,改用 swapfile:
bash复制sudo swapoff /dev/sda4
sudo rm /etc/fstab 中对应的 swap 分区行
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
然后在 fstab 里用 swapfile 的方式重新挂载 swap。这套操作做完之后,sda4 分区就变成空闲了,但如果你不想留着它,还得手动用 fdisk 或 parted 删除分区、再 growpart。这已经是相对高级的操作了,我建议没把握的话宁可先备份,再操作。
另外特别提醒一下:如果你的 swap 分区在根分区之后,操作前要确认系统的可用内存足够,别把 swap 一关就急着下一步,那段时间内存压力会全部打在物理内存上,搞不好 OOM 就把服务杀了。
4.3 VMware 扩展后 Ubuntu 报 GPT PMBR size mismatch
这是给虚拟磁盘扩展完之后,第一次在 Ubuntu 里执行 sudo fdisk -l /dev/sda 时挺容易撞见的一个提示:
code复制GPT PMBR size mismatch (41943039 != 251658239) will be corrected by write.
The backup GPT table is not on the end of the device.
翻译成大白话就是:GPT 分区表头部记录的分区表大小和磁盘真实大小对不上了。因为 VMware 偷偷把磁盘物理空间加大了,但 GPT 元数据里记录的备份表位置还留在旧位置。
这个提示本身不致命,但系统分区表处于“不一致”状态。正确的处理方式是用 parted 或 gdisk 修复:
bash复制sudo parted /dev/sda
(parted) print
如果 parted 说检测到 GPT table 不一致并要求修复,可以输入 Fix 让它纠正。或者用 gdisk:
bash复制sudo gdisk /dev/sda
输入 w 写回分区表,gdisk 也会把备份 GPT 表挪到磁盘末尾并修正 PMBR 中的 size 字段。
这里有个细节值得注意:修复分区表之后,最好先执行 partprobe 重新读取分区表,再去做 growpart 和 resize2fs。别在分区表不一致的状态下强行分区扩展,哪怕工具允许你这么做,风险也太大。
4.4 扩容后系统进不去的兜底思路
虽然我操作了很多次都没翻过车,但不能保证你也这么顺利。如果你扩容完成后重启系统,发现卡在文件系统检查阶段,或者直接掉进 initramfs 的 rescue shell,别慌,按照这个顺序处理:
第一步,在引导界面进 recovery mode(高级选项里选 recovery),得到 root shell 后先执行:
bash复制mount -o remount,rw /
第二步,检查文件系统是否一致。ext4 的根分区如果在扩容过程中被异常中断,resize2fs 留下的状态可能不干净,需要 e2fsck 修复:
bash复制e2fsck -f /dev/sda3
注意,一定要确保这个分区没有被挂载,recovery 模式的 root shell 里通常是只读挂载,非常合适做强制检查。如果是普通分区布局就检查 /dev/sda3 这种路径,如果是 LVM 就检查对应的 LV 路径。
第三步,e2fsck 报的问题如果提示 “block bitmap differences” 或者 “free blocks count wrong”,放心让它修。修完再重启,一般就能正常起来了。
这套兜底流程能撑住 90% 的“扩容后起不来”场景。剩下的 10% 基本是引导层面的事,跟扩容本身的关联其实不大,就不在这里展开了。
最后补充一条小贴士
在整个扩容过程中,我最深的感触是:别在磁盘快满的时候才动手。 磁盘使用率一旦超过 90%,系统行为就开始变得不可预测。日志写入失败、临时文件清理不及时、Docker 容器无法创建层,这些都会在你没留意的时候悄悄发酵。所以给虚拟机扩容的时候,我建议宁可多扩一点,也别掐着算。比如从 40GB 扩到 120GB,看着挺浪费,但过半年你就知道这块预留有多值钱。
还有一件事值得养成习惯:扩容完成后,顺手把 lsblk -f 的输出和 cat /etc/fstab 的内容记一下。下次再扩容或者做救援操作时,这些信息就是最可靠的地图。真实经验是:解决了这一次膨胀,不代表以后不用再管磁盘。虚拟机的空间管理,本质上是个需要定期照看的事情。
