1. 程式人生 > >樹莓派入門(二) - 樹莓派系統備份指令碼,Raspberry pi

樹莓派入門(二) - 樹莓派系統備份指令碼,Raspberry pi

系統備份步驟

簡述

我們經常需要複製已經部署好的樹莓派系統,批量複製到更多的樹莓派上。或者當我們只有一張記憶體卡時,我們希望隨時更換不同的系統,希望備份當前的系統。

樹莓派系統的備份可以分為兩種,一種是簡單的全卡備份,備份出來的映象大小與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卡的大小。
官方映象

備份流程

  1. 讀取當前系統boot、root分割槽的大小
  2. 建立img檔案,並根據當前系統大小,對img檔案分割槽和格式化,分出fat32格式的boot分割槽、ext4格式的root分割槽。
  3. 備份boot、root分割槽
  4. 和原來一樣的方式,燒錄備份的系統到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"