STC214/树莓派入门系列02

Created Fri, 05 Feb 2021 22:46:52 +0800 Modified Fri, 05 Feb 2021 22:46:52 +0800
2746 Words

原帖链接:https://blog.csdn.net/zhshh123/article/details/85063916

系统备份步骤

简述  
全卡备份  
    创建img文件  
    使用软件备份  
压缩备份  
    原理  
    备份流程  
    安装工具  
    存储镜像到U盘  
    创建img文件  
    对img文件分区  
    格式化img文件中的分区  
    备份boot分区  
    备份root分区  
    清理工作  
完整的压缩备份脚本  

简述

我们经常需要复制已经部署好的树莓派系统,批量复制到更多的树莓派上。或者当我们只有一张内存卡时,我们希望随时更换不同的系统,希望备份当前的系统。

树莓派系统的备份可以分为两种,一种是简单的全卡备份,备份出来的镜像大小与sd卡容量一致,另一种就是压缩备份,备份出来的镜像大小与系统实际占用的大小一致。
全卡备份

全卡备份是最简单的一种,直接使用我们烧录镜像的软件——Win32DiskImager即可。
但是备份出来的镜像大小会和sd卡的容量一致,如果sd卡16G,备份的镜像也是16G。所有请谨慎选择全卡备份。
创建img文件

在硬盘上创建一img后缀的空文件,并保证该分区的可用容量大于sd卡的容量。
在这里插入图片描述 在这里插入图片描述
使用软件备份

选择刚刚创建的空文件,选择要备份的sd卡盘符,点击read。
在这里插入图片描述
压缩备份

压缩备份比全卡备份要复杂,从网上找到了一个可以直接使用的脚本,直接安利给大家。
【终极解决方案】树莓派RaspberryPi系统备份Image的制作:http://www.360doc.com/content/17/0805/03/40492717_676751545.shtml
原理

树莓派的官方系统是基于Debian的,主要是两个分区:启动分区(boot)、根分区(root)。
boot分区:fat32格式,挂载点/boot,存放一些系统启动需要的基本文件,包括内核、驱动、firmware、启动脚本等。
root分区:ext4格式,挂载点/,存放一些安装的软件和库文件、系统配置、用户数据等。
另外当系统启动时会自动生成和挂载一些必要的其他文件夹,包括temfs、sysfs、proc、debugfs、configfs等(使用mount可以看到他们),这些都是虚拟文件系统,由操作系统自动管理,备份时不需要关注。
所以对于树莓派系统的备份,主要就是对boot、root分区的备份。

官方镜像也是包含这两个分区,并通过烧录的操作,将这两个分区写入sd卡。首次烧录完毕后,不论你的sd卡容量为多少,启动后的boot和/分区大小都是固定的,然后使用raspi-config,扩展root分区的大小为整张sd卡的大小。
官方镜像
备份流程

读取当前系统boot、root分区的大小  
创建img文件,并根据当前系统大小,对img文件分区和格式化,分出fat32格式的boot分区、ext4格式的root分区。  
备份boot、root分区  
和原来一样的方式,烧录备份的系统到sd卡,正常使用。  

安装工具

rsync:镜像备份工具
dosfstools:fat32分区格式化工具
parted & kpartx:虚拟磁盘工具
pv:显示进度

  
sudo apt-get -y install rsync dosfstools parted kpartx pv  
  

存储镜像到U盘

img备份完成后反正是要拷贝出来的,所以直接将img文件创建在U盘上。并且避免了备份时自己备份自己,可能带来的问题。
下面这段脚本,可以直接带上u盘参数,也可以直接默认/dev/sda1。

  
#mount USB device  
usbmount=/mnt  
mkdir -p $usbmount  
if [ -z $1 ]; then  
	echo "no argument, assume the mount device is /dev/sda1 ? Y/N"  
	read key  
	if [ "$key" = "y" -o "$key" = "Y" ]; then  
		sudo mount -o uid=1000 /dev/sda1 $usbmount  
	else  
		echo "$0 [backup dest device name], e.g. $0 /dev/sda1"  
		exit 0  
	fi  
else  
	sudo mount -o uid=1000 $1 $usbmount  
fi  
if [ -z "`grep $usbmount /etc/mtab`" ]; then  
	echo "mount fail, exit now"  
	exit 0  
fi   
img=$usbmount/rpi-back-`date +%Y%m%d-%H%M`.img  
echo "备份文件位置:$img"  
  

创建img文件

使用df -P命令可以查看磁盘使用的情况:
在这里插入图片描述

先读取当前系统分区的大小,获取boot size和root size,并计算总空间。因为需要留一部分空间,供备份过程中临时使用,以及开机后需要的正常运行和执行命令的空间。所有总大小扩大1.3倍。
dd命令用来创建img镜像,pv命令用来显示进度,该过程需要等待较长的一段时间。

  
bootsz=`df -P | grep /boot | awk '{print $2}'`  
rootsz=`df -P | grep /dev/root | awk '{print $3}'`  
totalsz=`echo $bootsz $rootsz | awk '{print int(($1+$2)*1.3)}'`  
echo "start create img,total size=${totalsz}K"  
#sudo dd if=/dev/zero of=$img bs=1K count=$totalsz  
pv -tpreb /dev/zero |sudo dd of=$img bs=1K count=$totalsz  
  

对img文件分区

  
bootstart=`sudo fdisk -l /dev/mmcblk0 | grep mmcblk0p1 | awk '{print $2}'`  
bootend=`sudo fdisk -l /dev/mmcblk0 | grep mmcblk0p1 | awk '{print $3}'`  
rootstart=`sudo fdisk -l /dev/mmcblk0 | grep mmcblk0p2 | awk '{print $2}'`  
#rootend=`sudo fdisk -l /dev/mmcblk0 | grep mmcblk0p2 | awk '{print $3}'`  
echo "format virtual disk:    boot: $bootstart >>> $bootend, root: $rootstart >>> end"  
sudo parted $img --script -- mklabel msdos  
sudo parted $img --script -- mkpart primary fat32 ${bootstart}s ${bootend}s  
sudo parted $img --script -- mkpart primary ext4 ${rootstart}s -1  
  

格式化img文件中的分区

  
loopdevice=`sudo losetup -f --show $img`  
device=/dev/mapper/`sudo kpartx -va $loopdevice | sed -E 's/.*(loop[0-9])p.*/\1/g' | head -1`  
sleep 5  
sudo mkfs.vfat ${device}p1 -n boot  
sudo mkfs.ext4 ${device}p2  
echo "format finish"  
  

备份boot分区

  
mountb=$usbmount/backup_boot/  
mkdir -p $mountb   
sudo mount -t vfat ${device}p1 $mountb  
sudo cp -rfp /boot/* $mountb  
sync  
echo "...Boot partition done"  
  

备份root分区

  
mountr=$usbmount/backup_root/    
mkdir -p $mountr     
sudo mount -t ext4 ${device}p2 $mountr  
  
#对swap分区特殊处理  
if [ -f /etc/dphys-swapfile ]; then  
        SWAPFILE=`cat /etc/dphys-swapfile | grep ^CONF_SWAPFILE | cut -f 2 -d=`  
	if [ "$SWAPFILE" = "" ]; then  
		SWAPFILE=/var/swap  
	fi  
	EXCLUDE_SWAPFILE="--exclude $SWAPFILE"  
fi  
#复制文件,并跳过特殊目录  
sudo rsync --force -rltWDEgop --delete --stats --progress \  
	$EXCLUDE_SWAPFILE \  
	--exclude '.gvfs' \  
	--exclude '/dev' \  
        --exclude '/media' \  
	--exclude '/mnt' \  
	--exclude '/proc' \  
        --exclude '/run' \  
	--exclude '/sys' \  
	--exclude '/tmp' \  
        --exclude 'lost\+found' \  
	--exclude '$usbmount' \  
	// $mountr  
  
#创建一下特殊目录  
for i in dev media mnt proc run sys boot; do  
	if [ ! -d $mountr/$i ]; then  
		sudo mkdir $mountr/$i  
	fi  
done  
if [ ! -d $mountr/tmp ]; then  
	sudo mkdir $mountr/tmp  
	sudo chmod a+w $mountr/tmp  
fi  
  
#移除网络配置  
#sudo rm -f $mountr/etc/udev/rules.d/70-persistent-net.rules  
  
sync   
ls -lia $mountr/home/pi/  
echo "...Root partition done"  
  
清理工作  
  
# replace PARTUUID  
opartuuidb=`blkid -o export /dev/mmcblk0p1 | grep PARTUUID`  
opartuuidr=`blkid -o export /dev/mmcblk0p2 | grep PARTUUID`  
npartuuidb=`blkid -o export ${device}p1 | grep PARTUUID`  
npartuuidr=`blkid -o export ${device}p2 | grep PARTUUID`  
sudo sed -i "s/$opartuuidr/$npartuuidr/g" $mountb/cmdline.txt  
sudo sed -i "s/$opartuuidb/$npartuuidb/g" $mountr/etc/fstab  
sudo sed -i "s/$opartuuidr/$npartuuidr/g" $mountr/etc/fstab  
  
sudo umount $mountb  
sudo umount $mountr  
  
# umount loop device  
sudo kpartx -d $loopdevice  
sudo losetup -d $loopdevice  
sudo umount $usbmount  
rm -rf $mountb $mountr  
echo "==== All done. You can un-plug the backup device"  
  

完整的压缩备份脚本

  
#!/bin/sh  
  
echo ===================== part 1, install tools ===============================  
sudo apt-get -y install rsync dosfstools parted kpartx exfat-fuse pv  
  
echo ===================== part 2, choose USB ===============================  
#mount USB device  
usbmount=/mnt  
mkdir -p $usbmount  
if [ -z $1 ]; then  
	echo "no argument, assume the mount device is /dev/sda1 ? Y/N"  
	read key  
	if [ "$key" = "y" -o "$key" = "Y" ]; then  
		sudo mount -o uid=1000 /dev/sda1 $usbmount  
	else  
		echo "$0 [backup dest device name], e.g. $0 /dev/sda1"  
		exit 0  
	fi  
else  
	sudo mount -o uid=1000 $1 $usbmount  
fi  
if [ -z "`grep $usbmount /etc/mtab`" ]; then  
	echo "mount fail, exit now"  
	exit 0  
fi   
  
img=$usbmount/rpi-back-`date +%Y%m%d-%H%M`.img  
echo "备份文件位置:$img"  
  
echo ===================== part 3, create a new blank img ===============================  
# New img file  
#sudo rm $img  
bootsz=`df -P | grep /boot | awk '{print $2}'`  
rootsz=`df -P | grep /dev/root | awk '{print $3}'`  
totalsz=`echo $bootsz $rootsz | awk '{print int(($1+$2)*1.3)}'`  
echo "start create img,total size=${totalsz}K"  
#sudo dd if=/dev/zero of=$img bs=1K count=$totalsz  
pv -tpreb /dev/zero |sudo dd of=$img bs=1K count=$totalsz  
  
# format virtual disk  
bootstart=`sudo fdisk -l /dev/mmcblk0 | grep mmcblk0p1 | awk '{print $2}'`  
bootend=`sudo fdisk -l /dev/mmcblk0 | grep mmcblk0p1 | awk '{print $3}'`  
rootstart=`sudo fdisk -l /dev/mmcblk0 | grep mmcblk0p2 | awk '{print $2}'`  
echo "format virtual disk:    boot: $bootstart >>> $bootend, root: $rootstart >>> end"  
#rootend=`sudo fdisk -l /dev/mmcblk0 | grep mmcblk0p2 | awk '{print $3}'`  
sudo parted $img --script -- mklabel msdos  
sudo parted $img --script -- mkpart primary fat32 ${bootstart}s ${bootend}s  
sudo parted $img --script -- mkpart primary ext4 ${rootstart}s -1  
loopdevice=`sudo losetup -f --show $img`  
device=/dev/mapper/`sudo kpartx -va $loopdevice | sed -E 's/.*(loop[0-9])p.*/\1/g' | head -1`  
sleep 5  
sudo mkfs.vfat ${device}p1 -n boot  
sudo mkfs.ext4 ${device}p2  
echo "format finish"  
  
echo ===================== part 4, fill the data to img =========================  
# mount partitions  
mountb=$usbmount/backup_boot/  
mountr=$usbmount/backup_root/  
mkdir -p $mountb $mountr  
# backup /boot  
sudo mount -t vfat ${device}p1 $mountb  
sudo cp -rfp /boot/* $mountb  
sync  
echo "...Boot partition done"  
# backup /root  
sudo mount -t ext4 ${device}p2 $mountr  
if [ -f /etc/dphys-swapfile ]; then  
        SWAPFILE=`cat /etc/dphys-swapfile | grep ^CONF_SWAPFILE | cut -f 2 -d=`  
	if [ "$SWAPFILE" = "" ]; then  
		SWAPFILE=/var/swap  
	fi  
	EXCLUDE_SWAPFILE="--exclude $SWAPFILE"  
fi  
sudo rsync --force -rltWDEgop --delete --stats --progress \  
	$EXCLUDE_SWAPFILE \  
	--exclude '.gvfs' \  
	--exclude '/dev' \  
        --exclude '/media' \  
	--exclude '/mnt' \  
	--exclude '/proc' \  
        --exclude '/run' \  
	--exclude '/sys' \  
	--exclude '/tmp' \  
        --exclude 'lost\+found' \  
	--exclude '$usbmount' \  
	// $mountr  
# special dirs   
for i in dev media mnt proc run sys boot; do  
	if [ ! -d $mountr/$i ]; then  
		sudo mkdir $mountr/$i  
	fi  
done  
if [ ! -d $mountr/tmp ]; then  
	sudo mkdir $mountr/tmp  
	sudo chmod a+w $mountr/tmp  
fi  
  
#移除网络配置  
#sudo rm -f $mountr/etc/udev/rules.d/70-persistent-net.rules  
  
sync   
ls -lia $mountr/home/pi/  
echo "...Root partition done"  
# if using the dump/restore   
# tmp=$usbmount/root.ext4  
# sudo chattr +d $img $mountb $mountr $tmp  
# sudo mount -t ext4 ${device}p2 $mountr  
# cd $mountr  
# sudo dump -0uaf - / | sudo restore -rf -  
# cd  
  
  
# replace PARTUUID  
opartuuidb=`blkid -o export /dev/mmcblk0p1 | grep PARTUUID`  
opartuuidr=`blkid -o export /dev/mmcblk0p2 | grep PARTUUID`  
npartuuidb=`blkid -o export ${device}p1 | grep PARTUUID`  
npartuuidr=`blkid -o export ${device}p2 | grep PARTUUID`  
sudo sed -i "s/$opartuuidr/$npartuuidr/g" $mountb/cmdline.txt  
sudo sed -i "s/$opartuuidb/$npartuuidb/g" $mountr/etc/fstab  
sudo sed -i "s/$opartuuidr/$npartuuidr/g" $mountr/etc/fstab  
  
sudo umount $mountb  
sudo umount $mountr  
  
# umount loop device  
sudo kpartx -d $loopdevice  
sudo losetup -d $loopdevice  
sudo umount $usbmount  
rm -rf $mountb $mountr  
echo "==== All done. You can un-plug the backup device"