Raspberrypi 3 系统备份还原, 基于最小系统镜像实现

来源:互联网 发布:换手率软件使用说明 编辑:程序博客网 时间:2024/06/17 13:58

Raspberrypi 3 备份还原系统


一、为什么要备份系统?

1 经常在树莓派上调试程序, 安装各种软件,越来越多的库和程序的安装带来的系统更改几乎是不可逆的,一旦某个程序或者驱动出现问题, 如果有备份, 可以快速方便的恢复到系统正常的状态
  
2批量发布树莓派产品,当在一台树莓派上把程序调试好后, 需要批量化到几十上百个树莓派, 如果每台树莓派再去安装一遍环境和程序, 需要耗费很多时间, 假如其中有用到apt-get安装的软件, 那更是不堪繁琐
二、备份系统镜像
    备份现有的系统镜像, 需要的时候既可以用来恢复系统, 又可以用来批量化生产, 非常方便。
目前的备份系统大多是基于SD卡的完整备份, 比如SD卡16G, 备份镜像也是16G,这带来了许多空间浪费,并且用镜像还原的时候, 新的SD卡容量必须要比镜像文件大,否则无法写入SD卡,况且就算同型号大小的SD卡, 容量不一定相等, 往往相差一点,这就导致镜像写入的时候经常出现SD卡容量就差那么一点点,结果无法使用。


    所以, 需要制作最小系统镜像, 按当前实际使用的空间来备份镜像, 而并非完整备份SD卡。


三、制作步骤

环境: Windows 7 64位 + 虚拟机Ubuntu 16 

树莓派 3 ,  Linux raspberrypi 4.9.35-v7+ #1014 SMP Fri Jun 30 14:47:43 BST 2017 armv7l GNU/Linux

1 如果直接在树莓派系统内部进行操作, 需要SD卡剩余空间大于已用空间, 一般来说大家使用的都是16G或者32G, 并没有多少剩余空间, 因此不采用此方案。因此我换到PC上的Ubuntu里面, 直接对已有的完整SD卡镜像进行处理。

2 在Wndows上先Win32 Disk Imager把树莓派的SD卡镜像完整读取出来放好, 我的镜像名称是rspi-backup-8G.img。

3 进入虚拟机Ubuntu16 , 你可以将SD卡完整镜像拷贝到虚拟机, 或者将路径共享到虚拟机, 我采用的后一种

4 安装虚拟机需要用到的工具

   sudo apt-get -y install rsync dosfstools parted kpartx

5 虚拟SD卡完整镜像为块设备

   sudo losetup -f --show 你的SD卡完整镜像路径

   

  完成后会显示路径为 /dev/loop0

6 挂载虚拟文件系统,即挂载上一步的/dev/loop0, 如下所示


挂载完成后会在/dev/mapper/ 路径下生成两个路径, 即/dev/mapper/loop0p1, /dev/mapper/loop0p2,

其中vfat格式的是boot分区, ext4的是root分区, 注意挂载后的路径在/medai/root/ (我用的root用户,所以在root路径下)

PS: ubuntu会自动挂载,如果你的系统没有自动挂载, 请手动挂载。



7 这步比较关键了, 根据上一步看到的分区大小, 可以发现root分区大小7G,实际使用4.3G, 有2.5G的空余空间

   我们要做的就是省下这部分空间。

   首先查看SD卡完整镜像大小,需要注意的是root分区是完整大小, root分区是使用的大小

    

    建立一个镜像文件,boot空间大小保持不变, root空间大小根据实际使用的大小增加一定冗余空间, 我取的30%冗余空间

种。    

    sudo dd if=/dev/zero of=rspi-backup.img bs=1K count=$totalsz

    (totalsz =  42030+4449932*1.3)

8 对虚拟镜像文件进行分区, 首先查看完整SD卡镜像的分区信息

  

可以获知, root分区从8192开始, 到93596结束,共85405块, root分区从94208开始,到15126527结束

我们新建的boot分区和这个保持一致, 需要缩减的是root分区, 因此我们分区如下

sudo parted rspi-backup.img --script -- mklabel msdossudo parted rspi-backup.img --script -- mkpart primary fat32 8192s 93596ssudo parted rspi-backup.img --script -- mkpart primary ext4  94208s -1

-1(这里是数字1  不是字母L)

然后将此空镜像文件挂载进系统进行格式化

losetup -f --show rspi-backup.imgkpartx -va /dev/loop1sudo mkfs.vfat /dev/mapper/loop1p1 -n bootsudo mkfs.ext4 /dev/mapper/loop1p2

最后如下图所示

   

最下面的就是刚才挂载进来的空镜像,(图中我挂载的是已经制作好的镜像,所以已用空间不是0)

PS: Ubuntu在执行kpartx -va ***.img后会自动挂载里面的分区, 如果你在系统里面找不到, 请手动挂载。


10 现在SD卡完整镜像和空镜像都挂载进来了, 直接完整拷贝文件

sudo cp -rfpd $src_boot_path/* $dst_boot_pathsyncsudo cp -rfpd $src_root_path/* $dst_root_pathsync

其中 的src_boot_path等 为上图中的boot和root路径,自行查看


11 关键的一步, 每个生成的img文件的文件系统uuid不一样, 在完整拷贝的时候, 原来的镜像里面boot里面记录的UUID和root里面记录的UUID 是自己专有的, 拷贝到空img后, 和现有的不匹配, 如果不修改的话会导致系统boot完后无法jump到root分区, 导致无法进入系统。所以要修改UUID

如下所示查看UUID


替换我们的img内的PARTUUID

opartuuidb=`blkid -o export ${device}p1 | grep PARTUUID`opartuuidr=`blkid -o export ${device}p2 | grep PARTUUID`npartuuidb=`blkid -o export ${device_dst}p1 | grep PARTUUID`npartuuidr=`blkid -o export ${device_dst}p2 | grep PARTUUID`echo "BOOT uuid old=$opartuuidb -> new=$npartuuidb"echo "ROOT uuid old=$opartuuidr -> new=$npartuuidr"sudo sed -i "s/$opartuuidr/$npartuuidr/g" $dst_boot_path/cmdline.txtsudo sed -i "s/$opartuuidb/$npartuuidb/g" $dst_root_path/etc/fstabsudo sed -i "s/$opartuuidr/$npartuuidr/g" $dst_root_path/etc/fstab

最后的卸载清理工作

sudo umount /dev/mapper/loop0p1sudo umount /dev/mapper/loop0p2sudo umount /dev/mapper/loop1p0sudo umount /dev/mapper/loop1p1kpartx -d /dev/loop0kpartx -d /dev/loop1    

得到如下镜像文件


拷贝到windows, 使用Win32 Disk Imager工具写入SD卡或者dd写入也行。 

树莓派开机进入系统, sudo raspi-config->Advanced options->Expand Filesystem, 确认重启, 

分区就扩大到你现在的SD卡大小了。


最后附上完整脚本,使用的时候在脚本后输入SD卡完整镜像的路径, 脚本运行完成后会在脚本运行路径生成一个名为rspi-backup.img的镜像文件

#!bin/bash# backup img file to new img file,  delete the empty space# e.g source a.img = 8G, # run this script# the new b.img maybe = 5G or less## created by liubo 20170825#if [ -z $1 ]; then    echo "no argument, assume the *.img file path please !"    exit 0else    imgpath=$1filoopdevice=`losetup -f --show $imgpath`echo "======1 losetup img file ========"device=`kpartx -va $loopdevice | sed -E 's/.*(loop[0-9])p.*/\1/g' | head -1`device="/dev/mapper/${device}"#echo $device $loopdevicesleep 5bootsz=`df -P | grep $device'p1' | awk '{print $2}'`rootsz=`df -P | grep $device'p2' | awk '{print $3}'`totalsz=`echo $bootsz $rootsz | awk '{print int(($1+$2)*1.3)}'`echo "======2 make empty img file======"echo 'boot :'$bootsz'K'echo 'root :'$rootsz'K'echo 'total:'$totalsz'K'sudo dd if=/dev/zero of=rspi-backup.img bs=1K count=$totalszbootstart=`sudo fdisk -l $loopdevice | grep $loopdevice'p1' | awk '{print $2}'`bootend=`sudo fdisk -l $loopdevice | grep $loopdevice'p1' | awk '{print $3}'`rootstart=`sudo fdisk -l $loopdevice | grep $loopdevice'p2' | awk '{print $2}'`echo "======3 format empty img========="echo "boot: $bootstart >>> $bootend, root: $rootstart >>> end"sudo parted rspi-backup.img --script -- mklabel msdossudo parted rspi-backup.img --script -- mkpart primary fat32 ${bootstart}s ${bootend}ssudo parted rspi-backup.img --script -- mkpart primary ext4  ${rootstart}s -1loopdevice_dst=`sudo losetup -f --show rspi-backup.img`device_dst=`kpartx -va $loopdevice_dst | sed -E 's/.*(loop[0-9])p.*/\1/g' | head -1`sleep 1device_dst="/dev/mapper/${device_dst}"sleep 1sudo mkfs.vfat ${device_dst}p1 -n bootsleep 1sudo mkfs.ext4 ${device_dst}p2sleep 1echo "======4 copy file to img========="sleep 5src_boot_path=`sudo df -lhT | grep $device'p1' | awk '{print $7}'`src_root_path=`sudo df -lhT | grep $device'p2' | awk '{print $7}'`dst_boot_path=`sudo df -lhT | grep $device_dst'p1' | awk '{print $7}'`dst_root_path=`sudo df -lhT | grep $device_dst'p2' | awk '{print $7}'`sudo cp -rfpd $src_boot_path/* $dst_boot_pathsync#sudo rsync -aP $src_root_path/ $dst_root_path/sudo cp -rfpd $src_root_path/* $dst_root_pathsync# replace PARTUUIDecho "======5 replace PARTUUID========="opartuuidb=`blkid -o export ${device}p1 | grep PARTUUID`opartuuidr=`blkid -o export ${device}p2 | grep PARTUUID`npartuuidb=`blkid -o export ${device_dst}p1 | grep PARTUUID`npartuuidr=`blkid -o export ${device_dst}p2 | grep PARTUUID`echo "BOOT uuid old=$opartuuidb -> new=$npartuuidb"echo "ROOT uuid old=$opartuuidr -> new=$npartuuidr"sudo sed -i "s/$opartuuidr/$npartuuidr/g" $dst_boot_path/cmdline.txtsudo sed -i "s/$opartuuidb/$npartuuidb/g" $dst_root_path/etc/fstabsudo sed -i "s/$opartuuidr/$npartuuidr/g" $dst_root_path/etc/fstabecho "Create backup img done, clear job ? Y/N"read keyif [ "$key" = "y" -o "$key" = "Y" ]; then    sudo umount $src_boot_path    sudo umount $src_root_path    sudo umount $dst_boot_path    sudo umount $dst_root_path    kpartx  -d $loopdevice    losetup -d $loopdevice    kpartx  -d $loopdevice_dst    losetup -d $loopdevice_dst    fiecho "==========Done==================="exit 0



如有错误请指正。谢谢。

参考链接

http://blog.csdn.net/talkxin/article/details/50464313






原创粉丝点击