| 场景 | 文件级备份(rsync/tar) | 块级克隆(dd/pv) |
|---|---|---|
| 系统迁移 | ❌ 需重新安装引导 | ✅ 完整复制包括引导扇区 |
| 加密磁盘 | ❌ 无法读取加密内容 | ✅ 逐扇区复制,加密状态保留 |
| LVM/RAID | ❌ 需重建卷结构 | ✅ 元数据一并复制 |
| 坏道恢复 | ❌ 跳过损坏文件 | ✅ 可配合 conv=noerror,sync 强制读取 |
| 取证分析 | ❌ 修改时间戳 | ✅ 比特级一致,哈希可验证 |
核心原则:操作系统、引导加载器、分区表、隐藏扇区(如UEFI启动分区)必须块级复制才能确保启动能力。
展开代码所需物品: - Ubuntu Desktop ISO(≥20.04,带图形界面) - Rufus/Ventoy 制作的启动U盘(≥4GB) - 目标磁盘(容量≥源盘,接口兼容SATA/NVMe) - 可选:USB硬盘盒(用于笔记本外接)
展开代码开机 → BIOS/UEFI(按Del/F2/F12)→ 关闭 Secure Boot → 选择U盘启动 → 选择 "Try Ubuntu"(不要选Install)
关键:Live 环境的内核不挂载本地磁盘,确保源盘处于离线状态。
展开代码# 查看所有磁盘拓扑 lsblk -fp 典型输出: NAME FSTYPE LABEL UUID MOUNTPOINT /dev/sda ← 源盘(系统盘,正在运行) ├─/dev/sda1 vfat EFI 1234-5678 ├─/dev/sda2 ext4 rootfs a1b2c3d4-e5f6-7890-abcd-ef1234567890 └─/dev/sda3 swap 8765-4321-fedc-ba09-876543210987 /dev/sdb ← 目标盘(全新或待覆盖) /dev/sdc iso9660 Ubuntu 22.04 LTS amd64 ← 启动U盘
关键安全操作:设置只读锁
展开代码# 对源盘设置内核级只读标记(防止任何误写) sudo blockdev --setro /dev/sda # 验证只读状态 sudo blockdev --getro /dev/sda # 输出 1 表示只读生效 # 如需临时解除(谨慎) # sudo blockdev --setrw /dev/sda
原理:
blockdev --setro修改内核块设备标志,即使root用户也无法写入,比mount -o ro更底层可靠。
展开代码# 安装进度工具 pv(Pipe Viewer) sudo apt update sudo apt install -y pv # 方案A:精确进度(需知道源盘大小) sudo pv -tpreb -s $(blockdev --getsize64 /dev/sda) /dev/sda | \ sudo dd bs=4M of=/dev/sdb iflag=fullblock # 方案B:简单进度(无总进度条,仅速率) sudo pv /dev/sda | sudo dd bs=4M of=/dev/sdb # 参数说明: # -t: 时间统计 -p: 进度条 -r: 速率 -e: 剩余时间 -b: 已传输字节 # bs=4M: 4MB块大小,平衡内存与效率 # iflag=fullblock: 确保每次读取完整块
克隆时间估算:
| 源盘类型 | 容量 | 典型速度 | 耗时 |
|---|---|---|---|
| SATA SSD | 500GB | 400 MB/s | ~21分钟 |
| NVMe SSD | 1TB | 1 GB/s | ~17分钟 |
| 机械硬盘 | 2TB | 150 MB/s | ~3.7小时 |
展开代码# 方法1:全盘 MD5(准确但极慢,大盘不推荐) sudo md5sum /dev/sda > /tmp/sda.md5 & sudo md5sum /dev/sdc > /tmp/sdc.md5 & wait diff /tmp/sda.md5 /tmp/sdc.md5 && echo "✅ 校验通过" || echo "❌ 数据不一致" # 方法2:抽样校验(推荐,平衡速度与可靠性) # 每 1MB 抽样前 4KB,覆盖全盘关键区域 sample() { local offset=$1 local device=$2 sudo dd if=/dev/$device bs=4K count=1 skip=$((offset/4)) 2>/dev/null | md5sum | cut -d' ' -f1 } echo "开始抽样校验(每1MB抽样4KB)..." total_mb=$(($(blockdev --getsize64 /dev/sda) / 1048576)) match=0 mismatch=0 for i in $(seq 0 1048576 $((total_mb * 1048576))); do if [ $((i/1048576)) -ne 0 ] && [ $(( (i/1048576) % 1000 )) -eq 0 ]; then echo "已检查 $((i/1048576)) / $total_mb MB..." fi hash_sda=$(sample $i sda) hash_sdb=$(sample $i sdb) if [ "$hash_sda" = "$hash_sdb" ]; then ((match++)) else ((mismatch++)) echo "⚠️ 差异发现于偏移 $i bytes" fi done echo "匹配: $match, 不匹配: $mismatch" [ $mismatch -eq 0 ] && echo "✅ 抽样校验通过" || echo "❌ 发现差异,建议重新克隆"
展开代码# 挂载新盘根分区(根据实际分区调整) sudo mount /dev/sdb2 /mnt # 假设 sdb2 是 / 分区 # 绑定挂载虚拟文件系统(chroot 环境必需) for i in /dev /dev/pts /proc /sys /run; do sudo mount --bind $i /mnt$i done # 切换根环境 sudo chroot /mnt # 重新安装 GRUB(关键!适配新硬件) grub-install /dev/sdb # 写入 MBR/GPT 启动扇区 update-grub # 生成新配置,检测可用内核 # 处理 UEFI 启动(如适用) # grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=ubuntu exit # 清理挂载 sudo umount -R /mnt
展开代码# 首次启动新盘前,在新盘环境中执行: sudo rm /etc/machine-id /var/lib/dbus/machine-id sudo systemd-machine-id-setup # 或克隆前在源盘执行(源盘会重新生成) # 注意:某些许可证绑定 machine-id,需提前记录
展开代码# 在 Ubuntu Server 安装界面选择语言后 Alt+F2 或 Ctrl+Alt+F2 # 进入 TTY2 # 如无法切换,尝试: Ctrl+Alt+F1 # 返回图形/主界面 Ctrl+Alt+F3 # 切换到 TTY3(备用)
展开代码# 解决热插拔磁盘不识别问题 for host in /sys/class/scsi_host/host*; do echo "- - -" > "$host/scan" done sleep 2 # NVMe 磁盘特殊处理(PCIe 热插拔) echo 1 > /sys/bus/pci/rescan # 重新查看 lsblk -fp
展开代码# 识别网卡 ip link # 输出示例: # 1: lo: <LOOPBACK> ... # 2: ens33: <BROADCAST,MULTICAST> ... ← 物理网卡 # 启用网卡 sudo ip link set ens33 up # 配置静态IP(无DHCP环境) sudo ip addr add 192.168.1.99/24 dev ens33 sudo ip route add default via 192.168.1.1 echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf # 测试 ping -c 3 8.8.8.8
替代方案:Alt+F1 返回安装界面,完成网络配置后 不继续安装,再 Alt+F2 返回 TTY,此时网络已配置好。
| 条件 | 要求 |
|---|---|
| 带宽 | 千兆以太网理论 125MB/s,万兆更佳 |
| 稳定性 | 建议独立交换机或直连,避免生产网络 |
| 安全 | 内网可信环境,或增加 SSH/SSL 隧道 |
| 时间 | 1TB 数据 @ 100MB/s ≈ 2.8 小时 |
展开代码# 接收端(新机器,目标盘) sudo ip addr add 10.0.0.2/24 dev eth0 sudo ip link set eth0 up # 发送端(旧机器,源盘) sudo ip addr add 10.0.0.1/24 dev eth0 sudo ip link set eth0 up # 验证连通 ping -c 3 10.0.0.2 # 从发送端测试
接收端先启动监听:
展开代码sudo -i # 获取精确字节数用于进度显示 DISK_SIZE=$(blockdev --getsize64 /dev/sda) # 监听 12345 端口,接收后直接写入磁盘 nc -l 12345 | pv -s $DISK_SIZE | dd bs=4M of=/dev/sda iflag=fullblock # 参数说明: # nc -l: 监听模式 12345: 任意高端口 # pv -s: 设置总大小,显示真实进度百分比
发送端开始推送:
展开代码sudo -i DISK_SIZE=$(blockdev --getsize64 /dev/sda) # 读取源盘并通过 nc 发送 dd bs=4M if=/dev/sda iflag=direct | pv -s $DISK_SIZE | nc -q0 10.0.0.2 12345 # 参数说明: # iflag=direct: 绕过页缓存,减少内存压力 # nc -q0: 发送完立即关闭,不等待
展开代码# 发送端(通过 SSH 隧道,自动加密) sudo dd bs=4M if=/dev/sda iflag=direct | \ pv -s $(blockdev --getsize64 /dev/sda) | \ ssh root@10.0.0.2 "dd bs=4M of=/dev/sda" # 或使用更高效的压缩+加密 sudo dd bs=4M if=/dev/sda | gzip -1 | \ pv | ssh root@10.0.0.2 "gunzip | dd bs=4M of=/dev/sda" # gzip -1: 最快压缩,减少带宽占用,CPU开销低
展开代码# 快速校验:对比前 4GB 数据 cmp -n $((4*1024*1024*1024)) /dev/sda <(ssh 10.0.0.1 dd bs=4M count=1024 if=/dev/sda) # 或抽样校验(同2.5方法) # 在接收端执行抽样,与发送端对比
| 现象 | 原因 | 解决 |
|---|---|---|
GRUB rescue> 提示 | 引导扇区未写入或损坏 | chroot 重装 grub-install |
UUID=xxx not found | fstab 使用旧 UUID | 编辑 /etc/fstab 更新 UUID,或使用 LABEL |
| 内核 panic | 缺少新硬件驱动 | 进入 Live 环境,chroot 后安装 linux-generic |
| 黑屏无输出 | 显卡驱动不匹配 | 临时添加 nomodeset 启动参数 |
展开代码# 源盘有坏道时,使用 conv 参数 sudo pv /dev/sda | sudo dd bs=4M of=/dev/sdb conv=noerror,sync # noerror: 读取错误继续 # sync: 错误位置填充空块,保持偏移对齐 # 事后标记坏块(ext4) sudo badblocks -v /dev/sdb > /tmp/badblocks.txt sudo e2fsck -l /tmp/badblocks.txt /dev/sdb2
展开代码# 仅克隆已使用空间(需文件系统支持) sudo apt install partclone sudo partclone.ext4 -c -s /dev/sda2 | pv | gzip -c > /mnt/backup/sda2.img.gz # 恢复 gunzip -c sda2.img.gz | pv | sudo partclone.ext4 -r -o /dev/sdb2
展开代码# 同时克隆到多个目标(如 RAID1 预配置) sudo apt install tee sudo pv /dev/sda | sudo tee >(dd bs=4M of=/dev/sdb) >(dd bs=4M of=/dev/sdc) > /dev/null
| 工具 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
dd | 通用块复制 | 无处不在,精确控制 | 无进度,出错即停 |
pv + dd | 本地克隆 | 实时进度,可限速 | 需计算总大小 |
partclone | 分区级备份 | 只拷已用空间,快 | 需匹配文件系统类型 |
Clonezilla | 批量部署 | 图形界面,自动化 | 需额外ISO,灵活性低 |
nc 网络对传 | 远程无介质克隆 | 无需中间存储 | 无加密,需稳定网络 |
rsync | 文件级同步 | 增量更新,可断点续传 | 不复制引导扇区 |
展开代码#!/bin/bash # clone_disk.sh - 安全磁盘克隆脚本 set -euo pipefail # 颜色输出 RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m' # 交互式选择磁盘 echo "可用磁盘:" lsblk -dpno NAME,SIZE,MODEL | grep -E "sd|nvme|hd" read -rp "源盘 (如 /dev/sda): " SRC read -rp "目标盘 (如 /dev/sdb): " DST # 确认 SRC_SIZE=$(blockdev --getsize64 "$SRC") DST_SIZE=$(blockdev --getsize64 "$DST") echo -e "${YELLOW}警告:目标盘 $DST 将被完全覆盖!${NC}" echo "源盘: $SRC ($(numfmt --to=iec-i $SRC_SIZE))" echo "目标: $DST ($(numfmt --to=iec-i $DST_SIZE))" if [ "$DST_SIZE" -lt "$SRC_SIZE" ]; then echo -e "${RED}错误:目标盘容量不足${NC}" exit 1 fi read -rp "确认继续? [yes/N]: " CONFIRM [ "$CONFIRM" != "yes" ] && exit 0 # 设置只读保护 echo "设置源盘只读保护..." sudo blockdev --setro "$SRC" # 执行克隆 echo "开始克隆..." sudo pv -tpreb -s "$SRC_SIZE" "$SRC" | sudo dd bs=4M of="$DST" iflag=fullblock # 校验 echo "抽样校验..." SAMPLES=1000 MATCH=0 for i in $(seq 0 $((SRC_SIZE/SAMPLES/1024/1024)) $((SRC_SIZE/1024/1024))); do OFFSET=$((i*1024*1024)) [ $OFFSET -ge $SRC_SIZE ] && break HASH_SRC=$(sudo dd if="$SRC" bs=4K count=1 skip=$((OFFSET/4/1024)) 2>/dev/null | md5sum | cut -d' ' -f1) HASH_DST=$(sudo dd if="$DST" bs=4K count=1 skip=$((OFFSET/4/1024)) 2>/dev/null | md5sum | cut -d' ' -f1) [ "$HASH_SRC" = "$HASH_DST" ] && ((MATCH++)) || echo "差异 at ${i}MB" done echo -e "${GREEN}校验通过: $MATCH/$SAMPLES 样本匹配${NC}" # 修复引导 echo "修复 GRUB 引导..." sudo mount "${DST}2" /mnt 2>/dev/null || sudo mount "${DST}1" /mnt for i in /dev /dev/pts /proc /sys /run; do sudo mount --bind $i /mnt$i; done sudo chroot /mnt grub-install "$DST" 2>/dev/null || echo "GRUB 安装失败,请手动处理" sudo umount -R /mnt echo -e "${GREEN}克隆完成!请执行 UUID 清理后重启${NC}" echo "sudo rm /etc/machine-id /var/lib/dbus/machine-id" echo "sudo systemd-machine-id-setup"
本文作者:zzz
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!