您的位置:首页 > 编程语言 > ASP

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

2017-08-25 15:19 621 查看

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 msdos
sudo parted rspi-backup.img --script -- mkpart primary fat32 8192s 93596s
sudo parted rspi-backup.img --script -- mkpart primary ext4  94208s -1


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

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

losetup -f --show rspi-backup.img
kpartx -va /dev/loop1
sudo mkfs.vfat /dev/mapper/loop1p1 -n boot
sudo mkfs.ext4 /dev/mapper/loop1p2

最后如下图所示

   


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

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

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

sudo cp -rfpd $src_boot_path/* $dst_boot_path
sync
sudo cp -rfpd $src_root_path/* $dst_root_path
sync

其中 的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.txt
sudo sed -i "s/$opartuuidb/$npartuuidb/g" $dst_root_path/etc/fstab
sudo sed -i "s/$opartuuidr/$npartuuidr/g" $dst_root_path/etc/fstab


最后的卸载清理工作

sudo umount /dev/mapper/loop0p1
sudo umount /dev/mapper/loop0p2
sudo umount /dev/mapper/loop1p0
sudo umount /dev/mapper/loop1p1
kpartx -d /dev/loop0
kpartx -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 0
else
imgpath=$1
fi

loopdevice=`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 $loopdevice
sleep 5
bootsz=`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=$totalsz

bootstart=`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 msdos
sudo parted rspi-backup.img --script -- mkpart primary fat32 ${bootstart}s ${bootend}s
sudo parted rspi-backup.img --script -- mkpart primary ext4 ${rootstart}s -1

loopdevice_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 1
device_dst="/dev/mapper/${device_dst}"
sleep 1
sudo mkfs.vfat ${device_dst}p1 -n boot
sleep 1
sudo mkfs.ext4 ${device_dst}p2
sleep 1

echo "======4 copy file to img========="
sleep 5
src_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_path
sync

#sudo rsync -aP $src_root_path/ $dst_root_path/
sudo cp -rfpd $src_root_path/* $dst_root_path
sync

# replace PARTUUID
echo "======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.txt sudo sed -i "s/$opartuuidb/$npartuuidb/g" $dst_root_path/etc/fstab sudo sed -i "s/$opartuuidr/$npartuuidr/g" $dst_root_path/etc/fstab

echo "Create backup img done, clear job ? Y/N"
read key
if [ "$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
fi
echo "==========Done==================="
exit 0



如有错误请指正。谢谢。

参考链接
http://blog.csdn.net/talkxin/article/details/50464313
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐