云服务器扩容硬盘后如何扩展分区?Linux文件系统在线扩容教程

在云控制台上把数据盘从 200GB 扩到 500GB,付完款回到服务器执行 df -h,显示的还是 196G,一点没变。这是云服务器扩容最经典的一幕,也是工单量最大的问题之一。原因并不神秘:控制台完成的是底层块设备的容量变更,而操作系统里还有三层结构没跟着变——内核看到的分区表、分区本身的大小、以及分区之上的文件系统。这三层要依次扩展,缺一步容量就用不上。好消息是整个过程不需要停机、不需要卸载、不需要重启,可以完全在线完成。本文按真实操作顺序讲清每一步,并区分 ext4 与 XFS、GPT 与 MBR 的不同处理方式。

一、先理解:为什么扩容后 df 不变

一块被使用的云硬盘,从底到上有四层:

  1. 块设备(云硬盘本身):控制台扩容改变的就是这一层。
  2. 分区表:写在磁盘头部的元数据,记录"第 1 个分区从第几扇区到第几扇区"。
  3. 分区(如 /dev/vdb1):分区表里划出来的那段连续空间。
  4. 文件系统(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'

二、操作前必须做的三件事

扩容涉及分区表修改,虽然是在线的,但仍然存在风险。动手前请完成:

  1. 打快照。云控制台对这块盘做一次快照,或在支持的情况下用 LVM 快照。分区表一旦写错,快照是唯一的后悔药。
  2. 确认数据已备份。快照不等于备份,重要数据另有一份异地副本。
  3. 确认分区表类型与文件系统类型。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/ext4resize2fs传设备路径,如 /dev/vdb1不需要,支持在线扩容支持(必须离线 + 先 e2fsck)
XFSxfs_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 场景比裸分区更省事的地方。

七、完整操作流程清单

  1. 控制台完成云硬盘扩容,确认状态为"已扩容/可用"。
  2. 打快照,确认有可用备份。
  3. lsblk 对比磁盘与分区容量,df -Th 确认文件系统类型,parted print 确认分区表类型。
  4. 若目标容量超 2TB 且分区表为 MBR,改为新建 GPT 盘 + 数据迁移方案。
  5. growpart /dev/vdb 1 扩展分区(失败则用 parted resizepart)。
  6. partprobe /dev/vdb 刷新分区表,lsblk 确认分区已变大。
  7. ext4 执行 resize2fs /dev/vdb1;XFS 执行 xfs_growfs /挂载点。
  8. df -h 确认最终容量,并做一次读写测试。
  9. 检查 /etc/fstab:UUID 不会因扩容改变,但建议用 blkid 复核一次,避免误改。
  10. 清理临时状态,确认业务正常,保留快照 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 密度后迁移数据——这是一个必须在规划阶段就考虑的问题。


本文为存储与硬盘管理系列,操作前请务必创建快照并确认设备名与分区号。