亚马逊AWS官方博客
Amazon linux XFS 文件系统 EBS 根卷缩容实践
背景
出于成本或架构优化的目的,在某些情况下,客户使用 AWS 的 EC2 实例,会遇到需要对 EBS 卷进行缩容的需求。对于普通的数据卷实现相对容易,通过拷贝数据重新挂载的方式。而对于根卷来说,因为其包含 boot 启动分区(AL2023 中还包含 EFI 分区),要实现缩容则更为挑战。如果是 EXT4 文件系统,可以使用 resize2fs 工具来实现分区的扩缩容。AL2 和 AL2023 的根卷默认使用 XFS 文件系统,可以使用 xfs_growfs 进行扩容,但尚无法支持缩容的能力。
要实现 XFS 文件系统的 EBS 根卷缩容的目的,可行的方式是通过工具将包含 boot 启动分区的根卷备份,然后恢复到新卷,再重新挂载替换 EBS 新卷到目标 ec2 实例。本文中我们将基于 Amazon Linux 2023,实际操作如何完成 100GB XFS 文件系统 EBS 根卷到 50GB 的缩容替换。对于 Amazon Linux 2,本文的操作方法同样适用。区别之处会在相关步骤中给出提示。
实战指南
1. 首先,启动一台 Amazon Linux 2023 操作系统的 EC2 实例,这里我们选择 m6i.large 机型。根卷选择 100GB 的 gp3 EBS 磁盘(Amazon EBS gp3 卷是最新一代的通用型 SSD EBS 卷,相比 gp2 具有更高的性价比和可管理性,可参考:https://aws.amazon.com/cn/ebs/general-purpose/?nc1=h_ls)。
![]() |
![]() |
实例启动后,为根卷创建一个快照,添加自定义描述信息。
![]() |
2. 用此快照创建一个新卷,注意创建新卷所设定的可用区,需与 EC2 实例所在可用区保持一致。
![]() |
3. 启动一台用于执行缩容备份恢复操作的新的临时 EC2 实例,注意可用区保持一致。创建时添加两个 gp3 的 EBS 卷,一个根卷需要临时存储备份文件,这里我们设置为 200GB 保障空间充足;另一个卷设置为我们要收缩的目标大小,这里设置为 50GB。
![]() |
4. 将第 2 步中用快照创建的 EBS 卷挂载到新的临时 EC2 实例。设备名称选择“/dev/sdc”。
![]() |
5. 此时临时 EC2 实例应该挂载了以下三个 EBS 卷:
- 根卷 /dev/xvda (200 GB)
- 数据卷 /dev/sdb: 新 EBS 根卷 (50 GB)
- 数据卷 /dev/sdc: 旧 EBS 根卷 (100 GB)
![]() |
6. 通过 SSH 登录临时 EC2 实例,sudo 到 root 用户。
![]() |
7. 执行 lsblk 观察已经挂载的存储设备列表。
nvme0n1:临时实例的根卷 (200 GB)
nvme1n1:用作新 EBS 根卷 (50 GiB)
nvme2n1:旧 EBS 根卷 (100 GiB)
后缀 p1/p127/p128 是用于标识分区类型的。在 Amazon Linux 2023 中,boot 启动分区是 p127,EFI 分区是 p128,首个数据分区是 p1。
![]() |
在 Amazon Linux 2 中,boot 启动分区是 p128,首个数据分区是 p1。
![]() |
这里设备名称从控制台显示的/dev/xvd* 或 /dev/sd* 修改为了/dev/nvme***。这是因为基于 AWS Nitro 系统(AWS Nitro System)创建的 EC2 实例中,EBS 卷会被展示为 NVMe 块设备,Linux 内核会做名称的映射可参考:https://docs.aws.amazon.com/ebs/latest/userguide/nvme-ebs-volumes.html)。
注意:如果是非 Nitro 系统的早期 EC2 实例,设备名称仍然会使用/dev/xvd* 或 /dev/sd*。
8. 执行
展示 100GB 旧根卷的磁盘和分区信息。记录下 sector size 和 p1 分区(首个数据分区)的 start sector number。
在 Amazon Linux 2023 中,sector size 通常是 512 字节,首个数据分区的 start sector number 通常是 24576。
![]() |
在 Amazon Linux 2 中,sector size 通常是 512 字节,首个数据分区的 start sector number 通常是 4096。
![]() |
9. 然后我们拷贝 boot 启动分区(从旧启动卷 nvme2n1 拷贝到新启动卷 nvme1n1)。基于前一步记录的信息,这里使用 dd 命令创建一个 low-level 的分区拷贝。
对于 Amazon Linux 2023,执行
对于 Amazon Linux 2,执行
if:输入文件/设备,这里用旧根卷设备名
of:输出文件/设备,这里用新根卷设备名
bs:字节表示块大小,这里等同于 sector size
count: 待拷贝的块数量,这里等同于首个数据分区的 start sector number
除了 boot 启动分区,此拷贝动作也会包含分区表(后续步骤中会进行调整)。如果分区工具提示新的根卷大于物理磁盘大小,可以忽略,后续步骤会进行修正。
![]() |
10、执行
展示旧根卷的首个数据分区 UUID 信息,将此信息记录保存,后续会用来调整新根卷的 UUID。
![]() |
11. 接下来我们运行 gdisk 工具,对新根卷进行更新和重新分区:
首先,我们需要调整分区表(GPT)。由于我们拷贝了旧的 GPT,它报告的大小比新根卷上实际可用的要大。这可以在 gdisk 的 expert’s menu 中修复:
回到 main menu:
删除旧的数据分区:
然后,创建正确大小的新数据分区:
最后将所有的更新写入磁盘:
![]() |
![]() |
12. 在新根卷的首个数据分区创建一个 XFS 文件系统:
![]() |
13. 对旧根卷的首个数据分区创建一个备份。
在/mnt 下创建一个目录 100g-old,然后 mount 挂载旧根卷的数据分区到此目录。注意 mount 命令需要添加“-t xfs -o nouuid”参数,不然会因 UUID 重复报错。
![]() |
用 xfsdump 命令对此目录创建一个备份:
取决于实际数据的大小,备份可能会花费一定时间。如担心 ssh 会话过期,可以配合“nohup &”后台运行命令。如果 xfsdump 提示输入“media label”,可以输入特定值或回车跳过。如果不希望出现此交互提示,也可以通过为 xfsdump 命令添加指定参数(参考:https://man7.org/linux/man-pages/man8/xfsdump.8.html).
![]() |
14. 将此备份恢复至新根卷的首个数据分区。首先在/mnt 下创建一个目录 50g-new,然后 mount 挂载新根卷的数据分区到此目录:
用 xfsrestore 命令恢复备份:
![]() |
同样,恢复时间取决于实际数据的大小。如担心 ssh 会话过期,可以配合“nohup &”后台运行命令。
15. 然后,更新新根卷首个数据分区的 UUID 和 LABEL 信息,与旧根卷匹配。
先卸载数据分区挂载以便执行更新操作:
更新 UUID(第 10 步记录的)和 LABEL:
![]() |
16. 最后,我们从临时 EC2 实例分离此 50GB 的新根卷,替换原始 EC2 实例的 100GB 旧根卷。原始实例需要先停机。挂载新根卷时设备名需要使用“/dev/xvda”。
![]() |
完成根卷替换后,启动和登录原始 EC2 实例,验证缩容成功。通过 lsblk 和 bf 命令,查看根卷已经显示为 50GB:
![]() |
替换前根卷为 100GB:
![]() |
至此,基于 Amazon Linux 的整个 XFS EBS 根卷收缩切换操作顺利完成。验证无误后可以终止临时 EC2 实例。旧根卷的快照备份可以保留以备后续使用。
总结
以上实战操作演示了如何将 Amazon Linux 2023(Amazon Linux 2)的 XFS 文件系统 EBS 根卷进行顺利缩容。如果涉及到要缩容的实例数量较多,有自动化需求,理论上可以实现。文中的 console 控制台操作步骤可以通过 CLI 命令/python 程序完成,涉及 shell 命令行操作可以通过 Systems Manager Automation runbook(SSM 自动化运行手册)执行,对于涉及命令行交互操作的,如 xfsdump 可以通过命令行参数完成 label 设置,gdisk 可以用 sgdisk 替换通过命令行参数代替交互式操作,以便于将操作自动化,如有自动化的需求可以参考以上思路。也期待后续 EBS 产品会推出支持缩容的磁盘类型或功能。
参考文档
https://docs.aws.amazon.com/ebs/latest/userguide/nvme-ebs-volumes.html
https://docs.aws.amazon.com/zh_cn/systems-manager/latest/userguide/systems-manager-automation.html
https://man7.org/linux/man-pages/man8/xfsdump.8.html