在云控制台上把数据盘从 200GB 扩到 500GB,付完款回到服务器执行 df -h,显示的还是 196G,一点没变。这是云服务器扩容最经典的一幕,也是工单量最大的问题之一。原因并不神秘:控制台完成的是底层块设备的容量变更,而操作系统里还有三层结构没跟着变——内核看到的分区表、分区本身的大小、以及分区之上的文件系统。这三层要依次扩展,缺一步容量就用不上。好消息是整个过程不需要停机、不需要卸载、不需要重启,可以完全在线完成。本文按真实操作顺序讲清每一步,并区分 ext4 与 XFS、GPT 与 MBR 的不同处理方式。
一、先理解:为什么扩容后 df 不变
一块被使用的云硬盘,从底到上有四层:
- 块设备(云硬盘本身):控制台扩容改变的就是这一层。
- 分区表:写在磁盘头部的元数据,记录"第 1 个分区从第几扇区到第几扇区"。
- 分区(如 /dev/vdb1):分区表里划出来的那段连续空间。
- 文件系统(ext4/XFS):建在分区之上,有自己的"总块数"记录。
控制台扩完第一层后,第 2、3、4 层还停留在旧尺寸。所以 lsblk 会看到磁盘变成 500G 但分区仍是 200G,df 看到的则是文件系统记录的容量,自然也不变。
# 对比磁盘容量与分区容量,一眼看清卡在哪一层 lsblk # NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT # vda 253:0 0 50G 0 disk # vdb 253:16 0 500G 0 disk ← 磁盘已是 500G # └─vdb1 253:17 0 200G 0 part /data ← 分区仍是 200G df -h /data # 文件系统仍是 196G 左右(200G 减去文件系统开销与保留块) # 查看文件系统类型(决定后续用哪个扩容命令) df -Th /data lsblk -f /dev/vdb1 blkid /dev/vdb1 # 查看分区表类型:gpt 还是 msdos(MBR) parted /dev/vdb print | grep -i 'partition table' fdisk -l /dev/vdb | grep -i 'label'
二、操作前必须做的三件事
扩容涉及分区表修改,虽然是在线的,但仍然存在风险。动手前请完成:
- 打快照。云控制台对这块盘做一次快照,或在支持的情况下用 LVM 快照。分区表一旦写错,快照是唯一的后悔药。
- 确认数据已备份。快照不等于备份,重要数据另有一份异地副本。
- 确认分区表类型与文件系统类型。GPT 和 MBR 的处理方式不同(MBR 有 2TB 硬上限),ext4 和 XFS 的扩容命令完全不同。
# 安装扩容工具(growpart 在 cloud-utils 包里) yum install -y cloud-utils-growpart # RHEL/CentOS/Rocky apt-get install -y cloud-guest-utils # Debian/Ubuntu # XFS 扩容工具 yum install -y xfsprogs apt-get install -y xfsprogs # 确认工具可用 growpart --version which resize2fs xfs_growfs
三、GPT 与 MBR:2TB 是分水岭
如果你的目标容量超过 2TB,必须先确认分区表类型,因为 MBR(msdos)分区表最大只能寻址 2TB。这不是"建议",是硬性限制:MBR 用 32 位 LBA 字段,按 512 字节扇区计算上限就是 2TB 左右。超过部分即使底层磁盘变大了,分区也无法扩展过去。
| 对比项 | MBR(msdos) | GPT |
|---|---|---|
| 最大寻址 | 约 2TB | 远超当前实际需求 |
| 主分区数 | 最多 4 个(或 3 主 + 扩展) | 默认 128 个 |
| 元数据冗余 | 仅开头一份 | 头尾各一份,带 CRC 校验 |
| 扩容到 2TB 以上 | 不支持,需转 GPT | 支持 |
| 转换风险 | MBR → GPT 转换有数据丢失风险,必须先备份 | — |
判断与处理:如果分区表是 msdos 且需要扩到 2TB 以上,正确做法是新建一块 GPT 分区表的云硬盘,把数据迁移过去,而不是在线转换。虽然 gdisk/sgdisk 理论上可以做 MBR→GPT 转换,但在有数据的生产盘上做这件事风险极高,不推荐。
# 新盘建 GPT 分区表的正确做法(迁移方案) parted -s /dev/vdc mklabel gpt parted -s /dev/vdc mkpart primary ext4 1MiB 100% mkfs.ext4 -F /dev/vdc1 mkdir -p /mnt/new && mount /dev/vdc1 /mnt/new rsync -avHAX --numeric-ids /data/ /mnt/new/ # 校验后再切换挂载点 umount /data && umount /mnt/new mount /dev/vdc1 /data blkid /dev/vdc1 # 更新 /etc/fstab 中的 UUID
四、扩展分区:growpart 与 parted resizepart
4.1 用 growpart 扩展(推荐,最简单)
growpart 的作用是把指定分区的结束位置扩展到磁盘末尾,它只改分区表,不动分区内的数据,也不动文件系统。
# 语法:growpart 磁盘设备 分区号(注意磁盘与分区号之间有空格) growpart /dev/vdb 1 # 输出示例:CHANGED: partition=1 start=2048 old: size=419428352 end=419430400 new: size=1048573952 end=1048576000 # 先做干跑(有些版本支持 -N,不实际修改) growpart -N /dev/vdb 1 # 让内核重新读取分区表 partprobe /dev/vdb partx -u /dev/vdb # 或 blockdev --rereadpt /dev/vdb # 确认分区已变大 lsblk /dev/vdb # vdb 253:16 0 500G 0 disk # └─vdb1 253:17 0 500G 0 part /data ← 分区已到 500G # 此时 df -h 仍然显示旧容量,因为文件系统还没扩
常见报错与处理:
NOCHANGE: partition 1 is size xxx. it cannot be grown:通常是因为该分区后面没有连续空闲空间(比如磁盘上有多个分区,要扩的不是最后一个),或者磁盘容量本身没变化(控制台扩容未生效,先检查控制台状态)。unexpected output in sfdisk --version:growpart 依赖 sfdisk 输出格式,某些环境下需要设置LANG=en_US.UTF-8再执行。growpart: command not found:安装 cloud-utils-growpart / cloud-guest-utils。
4.2 用 parted resizepart 扩展(growpart 不可用时的备选)
parted /dev/vdb # 交互过程: # (parted) print 查看当前分区编号与结束位置 # (parted) resizepart 1 指定要扩展的分区号 # End? [215GB]? 100% 输入 100% 或 500GB # (parted) print 确认 # (parted) quit # 非交互写法 parted -s /dev/vdb resizepart 1 100% # 刷新分区表并确认 partprobe /dev/vdb && lsblk /dev/vdb
注意:resizepart 会提示"分区正在使用"并询问是否继续,回答 Yes。在线调整分区表是支持的,但务必确认分区号没选错——选错分区号会破坏那一个分区。
五、扩展文件系统:ext4 与 XFS 完全不同
这一步是很多人卡住的第二个点,因为两个文件系统的命令和参数形式都不一样。
| 文件系统 | 扩容命令 | 参数形式 | 是否需卸载 | 是否支持缩容 |
|---|---|---|---|---|
| ext2/ext3/ext4 | resize2fs | 传设备路径,如 /dev/vdb1 | 不需要,支持在线扩容 | 支持(必须离线 + 先 e2fsck) |
| XFS | xfs_growfs | 传挂载点,如 /data | 不需要,且必须处于挂载状态 | 不支持 |
5.1 ext4 扩容
# 在线扩容(挂载状态下直接执行,不指定大小即扩展到分区最大) resize2fs /dev/vdb1 # 若提示需要先检查文件系统(部分场景),先执行 e2fsck -f /dev/vdb1 # 注意:e2fsck 对已挂载的文件系统执行会损坏数据, # 只有在文件系统已卸载时才可安全执行;在线扩容一般不需要这一步 # 也可以显式指定目标大小 resize2fs /dev/vdb1 400G # 确认结果 df -h /data tune2fs -l /dev/vdb1 | grep -i 'block count'
ext4 扩容的典型输出:resize2fs 1.42.9 (28-Dec-2013)
Filesystem at /dev/vdb1 is mounted on /data; on-line resizing required
old_desc_blocks = 25, new_desc_blocks = 63
The filesystem on /dev/vdb1 is now 131071744 (4k) blocks long.
5.2 XFS 扩容
# XFS 必须传挂载点,不能传设备名 xfs_growfs /data # 加 -d 表示扩展到设备最大值(多数版本默认行为) xfs_growfs -d /data # 查看扩容后的文件系统信息 xfs_info /data df -h /data # 若误传设备名会报错: # xfs_growfs: /dev/vdb1 is not a mounted XFS filesystem
记住这个区别:ext4 传设备、XFS 传挂载点。这是扩容操作里最高频的报错来源。
5.3 如果文件系统直接建在整块盘上(无分区)
有些环境直接用 mkfs.xfs /dev/vdb 而不建分区,此时没有分区表需要扩展,直接扩文件系统即可:
lsblk # 确认 /dev/vdb 下面没有 vdb1 # ext4 resize2fs /dev/vdb # XFS xfs_growfs /data
六、LVM 场景的扩容
如果数据盘是用 LVM 管理的,扩容链路上多了一层:磁盘 → 分区(PV)→ 卷组(VG)→ 逻辑卷(LV)→ 文件系统。要依次执行四条命令。
# 1. 扩展物理卷(若 PV 建在分区上,先扩分区) growpart /dev/vdb 1 pvresize /dev/vdb1 # 2. 查看卷组空闲空间 vgdisplay vg_data | grep -Ei 'free|size' vgs # 3. 扩展逻辑卷 lvextend -l +100%FREE /dev/vg_data/lv_data # 或指定增量 lvextend -L +100G /dev/vg_data/lv_data # 4. 扩展文件系统 resize2fs /dev/vg_data/lv_data # ext4 xfs_growfs /data # XFS,传挂载点 # 确认 df -h /data lvs
如果 PV 是直接建在整块盘上(pvcreate /dev/vdb),则跳过 growpart,直接 pvresize /dev/vdb 即可。这也是 LVM 场景比裸分区更省事的地方。
七、完整操作流程清单
- 控制台完成云硬盘扩容,确认状态为"已扩容/可用"。
- 打快照,确认有可用备份。
lsblk对比磁盘与分区容量,df -Th确认文件系统类型,parted print确认分区表类型。- 若目标容量超 2TB 且分区表为 MBR,改为新建 GPT 盘 + 数据迁移方案。
growpart /dev/vdb 1扩展分区(失败则用parted resizepart)。partprobe /dev/vdb刷新分区表,lsblk确认分区已变大。- ext4 执行
resize2fs /dev/vdb1;XFS 执行xfs_growfs /挂载点。 df -h确认最终容量,并做一次读写测试。- 检查
/etc/fstab:UUID 不会因扩容改变,但建议用blkid复核一次,避免误改。 - 清理临时状态,确认业务正常,保留快照 24 小时后再删除。
小结与避坑建议
云服务器扩容的完整链路是:控制台扩容 → growpart 扩分区 → partprobe 刷新 → resize2fs 或 xfs_growfs 扩文件系统。四步缺一不可,全程可在线完成,业务无需中断。最容易出错的两点是:忘了扩文件系统(df 不变),以及 XFS 传了设备名而不是挂载点。
避坑清单:
- 只做控制台扩容就以为完事了。这是"扩容后容量没变"的绝对主流原因。
- 扩完分区就收工,没跑
resize2fs/xfs_growfs,df依旧显示旧容量。 - XFS 传设备名。
xfs_growfs /dev/vdb1会报 "is not a mounted XFS filesystem",必须传挂载点。 - ext4 传挂载点。
resize2fs /data在新版本中虽可能被接受,但标准用法是传设备路径。 - MBR 分区表想扩到 2TB 以上。做不到,必须迁移到 GPT。
- growpart 报 NOCHANGE:先确认控制台扩容真的生效了(
lsblk里磁盘是否已是新容量),再确认要扩的分区后面是否有连续空闲空间。多分区场景下只有最后一个分区能被扩展。 - 在已挂载的文件系统上跑
e2fsck -f。这极可能损坏数据。e2fsck 只在卸载状态下执行。 - 忘记确认分区号。
growpart /dev/vdb 1里的 1 是分区号,多分区磁盘上写错会动到错误的分区。 - 想缩容。XFS 完全不支持缩容;ext4 支持但必须离线操作,且顺序与扩容相反——先缩文件系统,再缩分区,顺序错了直接丢数据。云平台多数也不支持缩小云硬盘容量,缩容通常需要新建小盘后迁移。
- 扩容后不验证。一定要跑一次
df -h并实际写入文件测试,确认业务能真正用到新空间。 - 立刻删除快照。扩容后建议保留快照一段时间,确认业务稳定后再清理。
最后补充一个容易被忽略的点:扩容前先确认文件系统的 inode 是否充足(df -i)。ext4 的 inode 数量在格式化时固定,扩容容量不会增加 inode。如果业务是海量小文件,可能容量扩了但 inode 依然不够,此时需要重新格式化并调整 inode 密度后迁移数据——这是一个必须在规划阶段就考虑的问题。
本文为存储与硬盘管理系列,操作前请务必创建快照并确认设备名与分区号。
企业QQ咨询




