聚热点 juredian

【终极解决方案】树莓派RaspberryPi系统备份Image的制作

在树莓派上做开发,难免会弄出各种版本的系统,加上有时候还需要拿给客户自己烧录或者demo,总是要clone一下TF卡,做系统做到烦躁。于是想想有没有什么办法能做跟官方release一样的烧录img出来,基本要求就是全系统克隆,但做出的img跟卡的容量无关,只跟系统占用的存储大小有关。

简单全卡备份

其实如果只是简单备份,可以直接将TF卡插入Linux电脑,用dd命令来备份和恢复(设备号不固定这里只是例子,还是用fdisk -l先查看一下比较保险):

#BackupthesystemtoimgfileinLinux

ddif=/dev/mmcblk0of=raspberrypi-bak.imgbs=1M

#AlittledifferenceifMac

ddif=/dev/rdisk2of=raspberrypi-bak.imgbs=1M

#

#Restoresystemfromimg

ddif=raspberrypi-bak.imgbs=1Mof=/dev/mmcblk0

#OrinMac

ddif=raspberrypi-bak.imgbs=1Mof=/dev/rdisk2

不过这样做,16G的卡哪怕你只用了1G,整个备份文件也有16G,占用空间耗时间是小事,想想拿这么大文件给客户不方便,也显得太不专业了。

在网上搜了一下,已经有朋友先行做过类似的事情:树莓派 Raspberry Pi SD卡系统备份与还原,看了一下按照他的步骤实践了一遍,但出现了一些问题,折腾了很久最终才解决。

RaspberryPi的文件系统

首先介绍一下RaspberryPi的文件系统。树莓派的官方系统是基于Debian的,主要是两个分区:启动分区boot和根分区。boot分区为fat32格式,挂载在/boot,存放一些系统启动需要的基本文件,包括内核、驱动、firmware、启动脚本等;根分区文件系统是ext4格式,挂载于/,存放一些安装的软件和库文件、系统配置、用户数据等等;另外当系统启动时会自动生成和挂载一些必要的其他文件夹,包括temfs、sysfs、proc、debugfs、configfs等(使用mount可以看到他们),这些都是虚拟文件系统,由操作系统自动管理,备份时不需要关注。日常使用时,修改的文件包括安装的软件都是在根分区中,而如果自行编译内核,需要更新的文件都在/boot中。

所以备份一个系统,实际上是要备份这两个分区,官方发布的烧录镜像,也是包含了这样的两个分区,并保证通过dd的操作,能将其完整写入目标TF卡。首次烧录完毕后,不论你的TF卡容量为多少,启动后的boot和/分区大小都是固定的,然后可以使用raspi-config来扩展根分区的大小,boot分区不变,来达到使用所有卡内容量的目的。

相对应的备份步骤,大致为:创建img,把img当作一个磁盘分区和格式化,mount各个分区,将文件备份至对应的分区中,umount分区结束备份。

目标备份文件的创建和分区

既然是备份到文件,那么首先需要创建一个备份文件,并且把这个文件看作一个虚拟设备,对其进行分区。

需要用到的工具有_parted, kpartx, dosfstools, rsync_,使用apt-get安装:

sudoapt-get-yinstallrsyncdosfstoolspartedkpartx

创建新的文件

我们用dd命令来创建一个新的img,img的大小和当前RaspberryPi已使用的存储空间有关。使用df -P来查看磁盘空间状态(以1K为单位),会看到如下的信息:

其中红框的部分就是我们需要关注的,将它们对应的Used数目取出,计算得到需要创建的img大小:

img=rpibackup.img

#sudorm$img

bootsz=df-P|grep/boot|awk"{print$2}"

rootsz=df-P|grep/dev/root|awk"{print$3}"

totalsz=echo$bootsz$rootsz|awk"{printint(($1+$2)*1.3)}"

sudoddif=/dev/zeroof=$imgbs=1Kcount=$totalsz

这里的totalsz是基于两个分区使用总和的1.3倍,一方面是分区表、格式化等操作造成的空间损失,另一方面是系统对剩余空间的要求。我曾经尝试过使用1.1,然而系统启动后在终端大部分命令都报错,说空间不足。

将文件分区

工具parted可以用来把一个img文件当作一个磁盘来分区。使用fdisk -l能够看到系统中各个磁盘分区的情况,在我的树莓派上系统TF卡的分区情况如下:

我们把boot分区设计为跟原分区大小一样,root分区则是扩展到img文件的末尾。对应的脚本:

bootstart=sudofdisk-l/dev/mmcblk0|grepmmcblk0p1|awk"{print$2}"

bootend=sudofdisk-l/dev/mmcblk0|grepmmcblk0p1|awk"{print$3}"

rootstart=sudofdisk-l/dev/mmcblk0|grepmmcblk0p2|awk"{print$2}"

echo"boot:$bootstart>>>$bootend,root:$rootstart>>>end"

rootend=sudofdisk-l/dev/mmcblk0|grepmmcblk0p2|awk"{print$3}"

sudoparted$img--script--mklabelmsdos

sudoparted$img--script--mkpartprimaryfat32${bootstart}s${bootend}s

sudoparted$img--script--mkpartprimaryext4${rootstart}s-1

分别格式化fat32和ext4分区

然后对分区分别进行格式化,我们可以通过loop来建立虚拟的磁盘挂载点,进行后续的操作:

loopdevice=sudolosetup-f--show$img

device=/dev/mapper/sudokpartx-va$loopdevice|sed-E"s/.*(loop[0-9])p.*/1/g"|head-1

sleep5

sudomkfs.vfat${device}p1-nboot

sudomkfs.ext4${device}p2

在中间的这个sleep是我在实践中发现,创建loop的设备节点需要一些时间,如果手动在终端贴命令则没有问题,而若是用脚本执行,则在format时节点还没有创建好,从而造成格式化失败。

备份Boot分区

Boot分区是Fat32格式且数据不多,直接mount然后copy数据即可,注意权限问题。

mountb=$usbmount/backup_boot/

mkdir-p$mountb

sudomount-tvfat${device}p1$mountb

sudocp-rfp/boot/*$mountb

sync

sudoumount$mountb

备份根(root)分区

根分区的备份是我折腾了很久的部分。因为它文件众多,不止一个文件夹,而且在系统运行时有一些文件、文件夹是临时生成或者mount的,不能全部直接copy。这就需要有一种方法能区分真正写在磁盘上的文件并将其备份。

失败的经验:dump&restore

在开头提到的道友那篇文章里,是采用了针对ext4备份和恢复的工具dump和restore。

sudomount-text4$partRoot/media/

cd/media

sudodump-0uaf-/|sudorestore-rf-

cd;sudoumount/media

但在关键的步骤出现了 Broken pipe错误,经过重复测试多次,虽然每次报错的文件和inode不一样,但每次都无法顺利完成。这个问题在原博的评论中也有人遇到了:

restore:./lost+found:Fileexists

./tmp/rstdir1445584846:(inode159534)notfoundontape

./tmp/rstmode1445584846:(inode161527)notfoundontape

DUMP:Brokenpipe

DUMP:TheENTIREdumpisaborted.

………………

针对这个问题我做了一些测试。首先是把sudo dump -0uaf - / | sudo restore -rf -分开,取消管道,先dump到一个文件再restore。发现dump总是成功的,而问题出在restore上,所以就broken pipe了。开始怀疑是restore的时候权限有问题,后来把dump出来的文件拿到PC上做restore,也同样是失败。这样就很有可能是dump的文件有问题,仔细看看每次出错的文件都是隐藏或者临时文件,怀疑跟这个有关,但找了一大圈dump相关的参数尝试,依然是没有解决。

使用rsync备份

由于每次测试dump和restore花的时间很长,项目又比较急,我实在是不能再耽搁下去了,于是决定放弃这种方式。想了一下,曾经用过rpi-clone来做卡与卡之间的备份,那么备份成文件也应该是一样的。我于是去看了下它的源码,把根文件系统备份相关的部分提出来就能直接用起来了。这里用的是备份工具rsync,其本质上是基于文件系统之上的的直接拷贝,因为没有用到增量备份,所以跟cp其实是一回事。只是rsync能很好的保留各种权限、时间戳、软链接和文件信息,避免了一些用cp的问题。保险起见,我依然沿用了它。

首先还是mount一下:

mountr=$usbmount/backup_root/

mkdir-p$mountr

sudomount-text4${device}p2$mountr

然后要对存在swap分区的情况进行特殊处理:

if[-f/etc/dphys-swapfile];then

SWAPFILE=`cat/etc/dphys-swapfile|grep^CONF_SWAPFILE|cut-f2-d=`

if["$SWAPFILE"=""];then

SWAPFILE=/var/swap

fi

EXCLUDE_SWAPFILE="--exclude$SWAPFILE"

fi

接着就是所有文件的备份,跳过某些临时、系统自动生成的文件和文件夹:

sudorsync--force-rltWDEgopt--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

完成后新建一些特殊文件夹,用来做系统启动时某些文件的自动挂载点,例如proc和mnt等等:

foriindevmediamntprocrunsysboot;do

if[!-d$mountr/$i];then

sudomkdir$mountr/$i

fi

done

if[!-d$mountr/tmp];then

sudomkdir$mountr/tmp

sudochmoda+w$mountr/tmp

fi

到这里基本上就完成了,有一点小小的修改就是把网络配置文件删掉,以免换了新的网络环境启动系统无法自动配置:

sudorm-f$mountr/etc/udev/rules.d/70-persistent-net.rules

sync

最后umount,清理loop设备,结束备份:

sudoumount$mountr

#umountloopdevice

sudokpartx-d$loopdevice

sudolosetup-d$loopdevice

sudoumount$usbmount

rm-rf$mountb$mountr

Mount USB device

使用dump/restore的时候,有一个跳过特定文件的参数,在前文提到的教程中,作者是将img放到用户目录,并且跳过这个文件的。虽然使用rsync备份的时候,依然可以这么做,但考虑到通用性和调试的方便,我把文件直接备份在了外接的U盘上,以避免考虑各种“自己备份自己”可能带来的问题。顺便把相关的USB mount脚本和判断也贴一下供参考:

usbmount=/mnt

mkdir-p$usbmount

if[-z$1];then

echo"noargument,assumethemountdeviceis/dev/sda1?Y/N"

readkey

if["$key"="y"-o"$key"="Y"];then

sudomount-tvfat-ouid=1000/dev/sda1$usbmount

else

echo"$0[backupdestdevicename],e.g.$0/dev/sda1"

exit0

fi

else

sudomount-tvfat-ouid=1000$1$usbmount

fi

if[-z"`grep$usbmount/etc/mtab`"];then

echo"mountfail,exitnow"

exit0

fi

当然,如果你有兴趣,大可以自己改一改尝试直接备份在用户目录,只是我感觉依然是需要copy出来,意义不大。??

恢复备份

备份img做好了,恢复备份就无需多谈了。既然是用树莓派的小伙伴,自然有一百种方法将镜像烧录到卡上。实在不知道的,放官方链接:Installing images,或者看这篇:树莓派初始配置指南(2代B型)。

最后,我再补充一点,恢复备份的系统可能会出现系统盘只有几G的情况

1.扩容步骤:

使用树莓派自带的工具可以快速将树莓派挂载所有的 SD卡空间,将系统分区扩展到 SD卡的最大容量

(1)打开raspi-config系统配置工具

sudoraspi-config

(2)选择Advancd Options:

(3)选择Expand Filesystem:

(4)Ok:

(5)重启树莓派

小结

整理后完整的script我已经更新到GitHub:https://github.com/conanwhf/RaspberryPi-script/blob/master/rpi-backup.sh,如果对你有所帮助,欢迎Star!??有什么疑问和质疑,也欢迎砸过来!??

顺便提一下,写这篇文章的时候,我发现官方已经release了一个备份工具,piclone,看上去是在两个TF卡之间备份,跟rpi-clone功能一样。我还没有测过,有兴趣的小伙伴可以试一下,看看源码,也许/的备份能有新的思路和方法。

搜索建议:
热评

 小学英语作文400字

【必备】小学英语作文400字4篇在学习、工作或生活中,大家一定都接触过作文吧,作文是人们把记忆中所存储的有关知识、经验和思想用书面形式表达出来的记叙方式。如何写...(展开)

热评

 电影《后天》主要讲了什么?

该内容涉及剧透,所以我还是简略说一下好了。电影的精彩推荐还是亲身去看比较好哦《后天》是由20世纪福克斯公司制作的的科幻片,该片由罗兰·艾默里奇执导,丹尼斯·奎德...(展开)