简介
本文介绍一种适用于云服务器和普通 Linux 服务器的通用系统迁移方案:在 Rescue(恢复)环境中,将一个容量较大的 Linux 系统盘迁移到容量更小但足以容纳实际数据的新硬盘,并使新硬盘能够独立启动。
一、为什么不能直接使用 dd
假设源盘为 10GB,系统实际只使用了 1.2GB,目标盘为 5GB。下面的整盘复制方式仍然会按照源盘容量复制:
dd if=/dev/source of=/dev/target
因此,当目标盘小于源盘时,dd 通常会失败,或者在目标盘上留下不完整的分区表。
更适合的思路是文件级迁移:
确认磁盘和启动模式
→ 在目标盘建立分区
→ 源盘只读挂载
→ rsync 复制系统文件
→ 复制 EFI 文件
→ 更新 UUID / PARTUUID
→ chroot 后更新 initramfs 和 GRUB
→ 切换云平台启动盘
→ 验证系统、网络和业务
核心条件是:
源系统实际使用空间 + EFI/boot 分区 + 安全余量 < 目标盘容量
![Linux 系统从大容量硬盘迁移到小容量硬盘:Rescue 模式下的通用方案[linux-disk-migration-rescue-guide]-CXT - Enjoy Life | 生活、技术、交友、分享](https://www.cxthhhhh.com/wp-content/uploads/2026/07/2026071212394239.jpg)
二、适用范围
本文适合以下系统:
- ext4、xfs 等普通文件系统;
- 可以进入 Rescue 环境;
- 可以同时访问源盘和目标盘;
- 目标盘可以被设置为启动盘;
- 使用 GRUB + UEFI,或 BIOS + GPT;
- 没有复杂的 LVM、RAID、加密或特殊存储层。
以下情况需要额外设计方案:
- LVM、LUKS、mdadm RAID;
- Btrfs 子卷、ZFS;
- 独立的
/boot、/home、/var分区; - 数据库、Docker 等持续高频写入的业务;
- 严格的 Secure Boot 环境;
- 云平台不允许目标卷作为启动卷。
三、迁移前的安全原则
1. 必须确认设备名称
进入 Rescue 后,设备名称可能变化。不要假定正常系统中的 /dev/sda 在 Rescue 中仍然是 /dev/sda。
lsblk -e7 -o NAME,PATH,SIZE,TYPE,FSTYPE,LABEL,UUID,PARTUUID,MOUNTPOINTS
fdisk -l
blkid
典型设备可能是:
旧系统盘:/dev/sda,约 10GB
Rescue 系统盘:/dev/sdb
目标空盘:/dev/vda,约 5GB
2. 不要修改源盘
在确认迁移成功前,不要对源盘执行:
mkfs.*
wipefs
sgdisk --zap-all
parted
fdisk 写入操作
dd
即使 fdisk -l 报告源盘 GPT 有警告,只要源盘仍能正常读取,也不要在迁移过程中修复源盘。
3. 源盘只读,目标盘读写
mount -o ro /dev/source-root /mnt/old
mount -o ro /dev/source-efi /mnt/old/boot/efi
mount /dev/target-root /mnt/new
mount /dev/target-efi /mnt/new/boot/efi
旧盘应保留几天作为回滚方案,不要在第一次启动成功后马上删除。
四、记录启动和串口配置
云服务器通常通过串口控制台查看启动过程。迁移前记录当前启动参数和 EFI 文件:
cat /proc/cmdline
grep -nE \
'GRUB_TERMINAL_OUTPUT|GRUB_SERIAL_COMMAND|GRUB_CMDLINE_LINUX' \
/etc/default/grub /etc/default/grub.d/* 2>/dev/null || true
systemctl --no-pager --full status \
[email protected] \
[email protected] 2>&1 || true
find /boot/efi/EFI -maxdepth 3 -type f \
-printf '%P\t%s bytes\n' | sort
常见配置如下:
console=tty0
console=ttyS0,115200
earlyprintk=ttyS0,115200
GRUB_TERMINAL_OUTPUT="gfxterm serial"
GRUB_SERIAL_COMMAND="serial --speed=115200"
迁移时不要无依据地将 ttyS0 改成 ttyS1,应以本机日志和当前配置为准。
五、确认启动模式和源盘结构
test -d /sys/firmware/efi && echo UEFI || echo BIOS
lsblk -e7 -o NAME,PATH,SIZE,TYPE,FSTYPE,FSVER,LABEL,UUID,PARTUUID,MOUNTPOINTS
fdisk -l
一个典型的源盘结构是:
/dev/sda
├─/dev/sda1 ext4 根分区
├─/dev/sda14 BIOS Boot 分区
└─/dev/sda15 vfat EFI System Partition
检查源系统实际使用量:
du -xsh /mnt/old
df -h /mnt/old
六、建立目标盘分区
以下示例假定目标盘为 /dev/vda。执行前必须再次确认它可以清空。
目标结构:
/dev/vda1 剩余空间,ext4 根分区
/dev/vda14 4MiB,BIOS Boot
/dev/vda15 128MiB,EFI System Partition
创建 GPT 和分区:
sgdisk --zap-all /dev/vda
sgdisk \
-n 14:2048:+4M -t 14:EF02 -c 14:BIOS-BOOT \
-n 15:10240:+128M -t 15:EF00 -c 15:EFI \
-n 1:0:0 -t 1:8300 -c 1:ROOT \
/dev/vda
sgdisk -e /dev/vda
partprobe /dev/vda
udevadm settle
检查:
sgdisk -p /dev/vda
lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,PARTTYPENAME,PARTLABEL,PARTUUID /dev/vda
格式化目标分区:
mkfs.ext4 -F -L root /dev/vda1
mkfs.vfat -F 32 -n EFI /dev/vda15
BIOS Boot 分区 /dev/vda14 不需要格式化。
记录新标识:
blkid /dev/vda1 /dev/vda15
七、挂载源盘和目标盘
mkdir -p /mnt/old /mnt/new
mount -o ro /dev/sda1 /mnt/old
mkdir -p /mnt/old/boot/efi
mount -o ro /dev/sda15 /mnt/old/boot/efi
mount /dev/vda1 /mnt/new
mkdir -p /mnt/new/boot/efi
mount /dev/vda15 /mnt/new/boot/efi
检查挂载:
findmnt /mnt/old
findmnt /mnt/old/boot/efi
findmnt /mnt/new
findmnt /mnt/new/boot/efi
应当确认旧盘为 ro,目标盘为 rw。
八、使用 rsync 复制系统
复制根文件系统:
rsync -aHAXx --numeric-ids --info=progress2 \
--exclude='/boot/efi/*' \
--exclude='/dev/*' \
--exclude='/proc/*' \
--exclude='/sys/*' \
--exclude='/run/*' \
--exclude='/tmp/*' \
--exclude='/mnt/*' \
--exclude='/media/*' \
--exclude='/lost+found' \
/mnt/old/ /mnt/new/
参数含义:
-a:保留权限、所有者、符号链接等;-H:保留硬链接;-A:保留 ACL;-X:保留扩展属性;-x:不跨越其他文件系统;--numeric-ids:保留原始 UID/GID;--exclude:排除运行时文件系统和单独挂载的 EFI 分区。
单独复制 EFI:
rsync -r --info=progress2 \
/mnt/old/boot/efi/ \
/mnt/new/boot/efi/
检查复制结果:
df -h /mnt/old /mnt/new
du -xsh /mnt/old /mnt/new
find /mnt/new/boot/efi/EFI -maxdepth 3 -type f -printf '%P\t%s bytes\n' | sort
比较关键 EFI 文件:
sha256sum /mnt/old/boot/efi/EFI/BOOT/BOOTX64.EFI /mnt/new/boot/efi/EFI/BOOT/BOOTX64.EFI
sha256sum /mnt/old/boot/efi/EFI/debian/shimx64.efi /mnt/new/boot/efi/EFI/debian/shimx64.efi
sha256sum /mnt/old/boot/efi/EFI/debian/grubx64.efi /mnt/new/boot/efi/EFI/debian/grubx64.efi
每一对文件的 SHA256 应当一致。
九、更新 fstab 和 EFI GRUB 配置
复制后,目标系统中的配置仍然引用源盘标识。先备份:
cp -a /mnt/new/etc/fstab /tmp/fstab.target.before-migration
cp /mnt/new/boot/efi/EFI/debian/grub.cfg /tmp/efi-grub.cfg.before-migration
查询目标盘标识:
blkid /dev/vda1 /dev/vda15
示例:
根文件系统 UUID:c5a70f46-85f6-4d49-bdf1-88d0805ab970
根分区 PARTUUID:79b9d558-e660-49d0-b578-b94282b9b32e
EFI 分区 PARTUUID:8c6a6d70-e9f6-4380-9f63-32a6532f5fb0
写入目标 fstab:
cat > /mnt/new/etc/fstab <<'EOF'
PARTUUID=79b9d558-e660-49d0-b578-b94282b9b32e / ext4 rw,discard,errors=remount-ro,x-systemd.growfs 0 1
PARTUUID=8c6a6d70-e9f6-4380-9f63-32a6532f5fb0 /boot/efi vfat defaults,umask=077 0 2
EOF
更新 Debian EFI 配置:
cat > /mnt/new/boot/efi/EFI/debian/grub.cfg <<'EOF'
search.fs_uuid c5a70f46-85f6-4d49-bdf1-88d0805ab970 root
set prefix=($root)'/boot/grub'
configfile $prefix/grub.cfg
EOF
需要区分:
| 配置位置 | 使用的标识 |
|---|---|
/etc/fstab | 分区 PARTUUID |
EFI/debian/grub.cfg | 根文件系统 UUID |
/boot/grub/grub.cfg | 由 update-grub 重新生成 |
十、进入 chroot 并更新启动文件
挂载虚拟文件系统:
for i in dev proc sys run; do
mount --rbind "/$i" "/mnt/new/$i"
mount --make-rslave "/mnt/new/$i"
done
进入目标系统:
chroot /mnt/new /bin/bash
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
验证目标文件系统:
grub-probe --target=fs_uuid /
grub-probe --target=partuuid /
grub-probe --target=fs /boot/efi
预期输出应为目标盘的新 UUID、PARTUUID 和 fat。
重新生成启动文件:
update-initramfs -u -k all
update-grub
检查:
grep -n 'root=PARTUUID=' /boot/grub/grub.cfg
grep -nE 'serial --speed|terminal_output|console=tty|earlyprintk' /boot/grub/grub.cfg
grub-script-check /boot/grub/grub.cfg
应当看到目标盘的 PARTUUID,以及原有的 ttyS0 和 115200 串口参数。
如果目标盘有 BIOS Boot 分区,并希望同时支持传统 BIOS 启动,可以执行:
grub-install --target=i386-pc --recheck /dev/vda
对于使用 Debian signed shim 的 UEFI 系统,不要未经确认就使用 UEFI 版 grub-install 覆盖现有签名 EFI 文件。保留原 EFI 文件、更新 EFI 配置并执行 update-grub 通常更加稳妥。
十一、卸载并切换启动盘
退出 chroot:
exit
同步并卸载:
sync
umount -R /mnt/new
umount /mnt/old/boot/efi
umount /mnt/old
sync
检查:
findmnt | grep -E '/mnt/(old|new)' || echo "old/new 已全部卸载"
然后在云平台控制台:
- 停止服务器;
- 将目标盘设置为启动盘;
- 第一次测试时不要删除旧盘;
- 如果平台无法明确选择启动盘,可以暂时卸载旧系统盘;
- 启动服务器并打开串口控制台;
- 观察 UEFI、GRUB、内核和 systemd 启动过程。
十二、启动后的验证
findmnt /
findmnt /boot/efi
lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,LABEL,UUID,PARTUUID,MOUNTPOINTS
cat /proc/cmdline
df -h
systemctl --failed
systemctl is-active [email protected]
重点确认:
- 根分区来自目标盘;
/boot/efi已挂载;/proc/cmdline使用目标 PARTUUID;systemctl --failed没有失败服务;- 串口登录服务为
active。
IPv6 服务器还应检查:
ip -6 addr
ip -6 route
ping -6 -c 3 google.com
十三、常见故障
启动进入 grub rescue>
检查:
cat /boot/efi/EFI/debian/grub.cfg
blkid
ls -l /boot/efi/EFI/BOOT/BOOTX64.EFI
ls -l /boot/efi/EFI/debian/shimx64.efi
ls -l /boot/efi/EFI/debian/grubx64.efi
重点确认 search.fs_uuid 使用目标根文件系统 UUID。
启动进入 initramfs
常见原因:
/etc/fstab仍使用旧 PARTUUID;- GRUB 中的
root=PARTUUID没有更新; - 目标根分区没有正确挂载;
- 独立
/boot分区没有复制。
找不到 EFI 启动项
确认存在:
EFI/BOOT/BOOTX64.EFI
EFI/debian/shimx64.efi
EFI/debian/grubx64.efi
EFI/debian/grub.cfg
云服务器上,EFI/BOOT/BOOTX64.EFI 这个通用 fallback 文件尤其重要,因为云平台不一定保留原硬盘的 UEFI NVRAM 启动项。
串口控制台没有输出
检查:
console=ttyS0,115200
earlyprintk=ttyS0,115200
serial --speed=115200
terminal_output gfxterm serial
同时检查:
systemctl status [email protected]
目标盘空间不足
检查大目录:
du -xhd1 / | sort -h
重点关注 /var/log、/var/cache、/var/lib/docker、数据库文件和隐藏目录。目标根分区应保留足够余量,不要长期运行在 95% 以上。
十四、总结
从大容量硬盘迁移到小容量硬盘时,关键不是源盘的分区容量,而是源系统实际使用的数据量。
最重要的注意事项:
- 不要把大容量源盘直接
dd到小容量目标盘; - 任何破坏性操作前必须确认目标设备;
- 源系统尽量只读挂载;
- 使用
rsync复制文件,而不是复制整块磁盘; - 同时处理文件系统 UUID 和分区 PARTUUID;
- 保留 EFI fallback 文件和 signed shim;
- 保留串口控制台参数;
- 旧盘不要立即删除;
- 迁移完成后必须验证真实启动盘、IPv6 和业务服务。
本文中的
/dev/sda、/dev/vda、UUID 和 PARTUUID 都是示例。实际操作时必须以当前 Rescue 环境的lsblk、blkid和fdisk -l输出为准。