當前位置:首頁 > 科技 > 正文

【終極解決方案】樹莓派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功能一樣。我還沒有測過,有興趣的小夥伴可以試一下,看看源碼,也許/的備份能有新的思路和方法。

你可能想看:

有話要說...

取消
掃碼支持 支付碼