可以使用Raspberry Pi创建自己的备份吗?


78

这个问题回答了我如何使用外部计算机创建RPi备份的问题。

我想知道是否可以创建当前正在使用的SD卡的备份映像,并将其复制到USB存储设备上的文件中。这可能吗?如果不是,是否有任何方法可以创建RPi的备份而不涉及另一台计算机?


2
可以,但是跳过/ tmp,/ run,/ proc,/ sys,/ dev和/ mnt。您不需要创建映像,您需要可以创建或更新映像的备份。所以不要用dd,研究rsync
goldilocks

1
@goldilocks如果您可以将此注释充实成更完整的答案,并解释您所考虑的备份和还原过程,我会喜欢的。
埃里克·威尔逊

完成-抱歉,我花了几天的时间。
goldilocks

1
如果目标卷足够大,则dd对于“新”副本而言,以只读方式重新挂载文件系统并以适当的块大小进行复制可能最快。逐个文件复制到Flash / SD介质可能是个坏主意。
克里斯·斯特拉顿

Answers:


86

这是rsync在Pi上进行备份的介绍。创建初始备份后,以这种方式保持最新状态要比不断撕裂整个映像快得多。您可以对本地硬盘驱动器或通过网络执行此操作。

实际上,您不希望将正在运行的系统的完整副本作为备份,因为表面上文件系统中的某些内容仅在运行时存在。将其包括在备份中,然后在以后使用它来重新创建映像可能会给您带来问题。

也有其他一些例外。rsync可以接受要排除的(glob)模式列表,并且可以从文件中读取这些模式,因此让我们首先了解一下该文件中应包含的内容。请注意,条目的格式为/directory/*而不是/directory。这是因为我们希望它们存在,但我们不想在其中复制任何内容。

/proc/*
/sys/*

这些在磁盘上实际上并不存在。它们是内核的接口,它在内存中创建和维护它们。如果您复制这些文件,然后将它们复制回系统并进行引导,则(至多)它是毫无意义的,因为内核将这些文件用作接口的安装点[如果要查看在安装文件系统分区时发生了什么在其中有数据的目录上,尝试。它可以工作,不会造成任何伤害,但是现在无法访问目录中的内容。]

注意,必须存在/sys/proc挂载点。 但它们不应包含任何内容。下一个:

/dev/*

dev目录是不太一样的东西procsys,但我们的目的是。如果您认为应该保存此设置,以便备份中可以有相同的设备节点或其他内容,那是错误的。不要打扰 不要复制dev。很久以前,Linux曾以这种方式工作,但现在不再如此。

/boot/*

这是大多数(也许全部)Pi特定发行版(例如Raspbian)的一种特殊情况。它实际上是第一个vfat分区的安装点。我们将分别处理。无论您做什么,都不要在这里包括它,因为同样,它是安装点。

/tmp/*
/run/*

/run通常也不在磁盘上,而是在内存中。也许/tmp也可以(这样可以节省一些SD卡操作),但是在任何情况下,顾名思义,这些都不是存储持久数据的地方。使用它们的应用程序希望可以在每次启动时将其删除。

/mnt/*
/media/*

这是特别是如果你正计划备份到硬盘或USB记忆棒重要设备处于/mnt/media(挂载倾向于使用后者),因为如果你没有在文件系统中,你将排除这些设备的位置创建一个将驱动器内容备份到其自身的循环,直到驱动器空间不足。我认为rsync 可能足够聪明,可以发现一些愚蠢的东西,但要避免测试前提。

进行实际备份:创建一个目录以备份到本地安装的硬盘驱动器,USB设备等上-例如“ pi_backup”。您可以通过ssh(参见下文)或使用网络安装的文件系统来备用备份到远程位置,但这可能需要一段时间。

如果包含要排除的列表的文件为/rsync-exclude.txt1,而您的驱动器为/mnt/usbhd,则执行实际备份:

rsync -aHv --delete --exclude-from=/rsync-exclude.txt / /mnt/usbhd/pi_backup/

请注意,上有一个斜杠pi_backup/

这将花费一些时间并产生大量输出(如果您想在日志中检查它,请添加> rsync.log)。 --delete第一次是没有意义的,但是为了保持备份更新,请使用它。这样可以确保您以后在Pi上删除的内容也可以从备份中删除。该a套递归进入目录,并确保所有的文件属性相匹配。 -H是保留硬链接2v是为了表示冗长,这就是为什么您获得一些输出(否则rsync很安静)的原因。查看man rsync更多。

有一个快捷方式,您可以跳过该--exclude-from文件。如果您确定所有不想复制的内容(/tmp等等)都在单独的文件系统上,则可以使用:

rsync -axHv --delete-during / /mnt/usbhd/pi_backup/

-x已插入。这是的缩写--one-file-system,它告诉您rsync不要跨越文件系统边界。我个人比较喜欢--exclude-from,但是在默认的Raspbian上--one-file-system可以正常工作。如果您想非常-x小心,可以同时使用:D

那不是一个完整的备份。如果您没有放入任何东西就足够了,boot并且可以通过将备份卡插入计算机并运行来使用备份来恢复系统就可以了:

rsync -av --delete-during /mnt/usbhd/pi_backup/ /mnt/sdcard_partition2/

您也可以使用上面有新图像的卡(假设它与基本图像相同)来执行此操作,尽管如果必须创建图像,这会有点效率低下(因为这时您将要覆盖其中的大部分图像)。您也可以通过USB适配器连接另一个SD卡,上面带有这样的图像,并使用上述方法来维护重复的卡。

如果您已经在其中添加了一些东西/boot(例如,一个自定义内核),包括/boot/config.txt,那么您也希望对其进行备份(非常简单-内容不多)。只需单独进行操作,然后在还原时,这些内容将进入第一个分区。

如果要创建空白的 Raspbian样式图像,然后可以将其备份到其中,请参见此处。您可以使用类似的方法来创建一个空的Raspbian样式卡-而不是处理.img文件,而是要处理真实的设备(例如/dev/sdb),这意味着您要做的就是创建分区表fdisk,然后格式/dev/sdb1sdb2(或其他)mkfs

但是复制整个图像更容易!为什么要为此烦恼呢?

并不难;我在10分钟内恢复了一张空白卡(按照最后一个链接的格式格式化)。是的,仅dd在整个过程中使用会更简单(如果您发现令人困惑的单词之类的东西……),但是每次您要更新备份都需要花费相当长的时间,因为您每次必须进行100%的备份。使用rsync,一旦存在备份,更新速度就快得多,因此您可以将其设置为每天通过cron轻松完成。甚至通过网络。每六个小时。您执行的次数越多,所需的时间就越少。

rsync 通过 ssh

这是一个例子:

rsync [options] --rsh="ssh [ssh options]" root@[the pi ip]:/ /backup/rpi/

例如,-av --delete --exclude-from=/rsync-exclude.txt“选项”就是您通常使用的“ ssh选项”(如果有的话)。你必须有root权限通过ssh这样做系统备份的目的(设置PermitRootLogin=yes/etc/ssh/sshd_config并重新启动服务器)。


1您应该保留此文件。您可以在以#或开头的行中添加注释;。这可能包括实际的rsync命令,以后可以将其复制粘贴,因此您不必每次都记住它。

2感谢Kris指出rsync不会自动执行此操作。


金发姑娘。看起来rysync很有用。是否有机会将其滚动成脚本?
极权主义者

与其手动排除所有挂载点,不如mkdir /tmp/backupable && mount --bind / /tmp/backupable与rsync同步?这还具有备份存储在被安装在其中的某些东西“遮盖”的地方的任何数据的优点。
2014年

@ n.st好主意(哈哈)!尽管我仍然认为使用--exclude-from是一个更好的主意,但我已将建议编辑为问题。如果您有时间,可以将其写成一个单独的答案,如果有我的投票,我可以参考。这个答案已经足够长了。
goldilocks

1
@IgorGanapolsky的目的是创建图像(请阅读“但是复制整个图像更容易!为什么要为此烦恼?”部分)。创建后,除了易于维护和快速维护外,此方法通常更灵活。如果您想稍后使用它来创建一个.img,可以;应有助于解释他们是如何结构化和可以被创建。
goldilocks

1
请参阅开头的段落,“那不是一个完整的备份...”。相反,这基本上是完全相同的事情。这可能会帮助人们经常混淆的一些概念。
goldilocks

24

Raspberry社区的成员编写的工作脚本。

您可以根据自己的喜好重复使用和调整代码,它有充分的文档证明和自我解释。

#!/bin/bash

# Setting up directories
SUBDIR=raspberrypi_backups
DIR=/hdd/$SUBDIR

echo "Starting RaspberryPI backup process!"

# First check if pv package is installed, if not, install it first
PACKAGESTATUS=`dpkg -s pv | grep Status`;

if [[ $PACKAGESTATUS == S* ]]
   then
      echo "Package 'pv' is installed."
   else
      echo "Package 'pv' is NOT installed."
      echo "Installing package 'pv'. Please wait..."
      apt-get -y install pv
fi

# Check if backup directory exists
if [ ! -d "$DIR" ];
   then
      echo "Backup directory $DIR doesn't exist, creating it now!"
      mkdir $DIR
fi

# Create a filename with datestamp for our current backup (without .img suffix)
OFILE="$DIR/backup_$(date +%Y%m%d_%H%M%S)"

# Create final filename, with suffix
OFILEFINAL=$OFILE.img

# First sync disks
sync; sync

# Shut down some services before starting backup process
echo "Stopping some services before backup."
service apache2 stop
service mysql stop
service cron stop

# Begin the backup process, should take about 1 hour from 8Gb SD card to HDD
echo "Backing up SD card to USB HDD."
echo "This will take some time depending on your SD card size and read performance. Please wait..."
SDSIZE=`blockdev --getsize64 /dev/mmcblk0`;
pv -tpreb /dev/mmcblk0 -s $SDSIZE | dd of=$OFILE bs=1M conv=sync,noerror iflag=fullblock

# Wait for DD to finish and catch result
RESULT=$?

# Start services again that where shutdown before backup process
echo "Start the stopped services again."
service apache2 start
service mysql start
service cron start

# If command has completed successfully, delete previous backups and exit
if [ $RESULT = 0 ];
   then
      echo "Successful backup, previous backup files will be deleted."
      rm -f $DIR/backup_*.tar.gz
      mv $OFILE $OFILEFINAL
      echo "Backup is being tarred. Please wait..."
      tar zcf $OFILEFINAL.tar.gz $OFILEFINAL
      rm -rf $OFILEFINAL
      echo "RaspberryPI backup process completed! FILE: $OFILEFINAL.tar.gz"
      exit 0
# Else remove attempted backup file
   else
      echo "Backup failed! Previous backup files untouched."
      echo "Please check there is sufficient space on the HDD."
      rm -f $OFILE
      echo "RaspberryPI backup process failed!"
      exit 1
fi

考虑在原始论坛中添加评论或发布您自己的版本以帮助使内容成熟。给一点点。

* 并感谢您回馈AndersW(单击获取GIT脚本)


2
如果在备份pi时文件系统(文件删除,添加了新文件)发生了变化该怎么办?
凯基

2
当它们使用rsync运行时,我备份了几个磁盘,而且我经常能够从这些文件备份中准确获得所需的磁盘。但是,通常,在挂载文件系统(*)时,不能完美地复制UNIX文件系统(每一位都正确无误)。挂载系统时制作的副本有时称为“脏副本”。可以采取几种措施来提高脏副本的质量(就像上面的脚本一样,关闭cron和mysql),但这并不是完美的。干杯! *-我错了,这取决于文件系统。
Tai Viinikka

1
您可以查看Debian建议的备份实用程序,并查看Pi是否具有它们的端口。rsnapshot听起来令人振奋
Piotr Kula

1
@TaiViinikka您不需要完美的副本。您需要部分副本,可以(快速,轻松地)将其重新放置在原始基础图像上。 rsync是要走的路;明天我有时间的时候,我会添加一个答案。 rsnapshot也值得调查。
goldilocks

3
基于上面的ppumkins答案,我将'dd'脚本与原始线程中的最新注释进行了同步,并自己添加了一些小改进。最终结果可以在这里找到:< github.com/aweijnitz/pi_backup >。请不要犹豫,添加改进并向我发送请求。
AndersW 2013年

14

我已经在rsync上修改了@goldilocks答案,以便在pi上进行备份。我备份到ext4Pi上安装的HDD上的分区。如果未装载HDD,则rsync将复制到装载目录(直到SD卡已满)。如果未以rw模式安装HDD,则会产生大量错误消息。这些都不是理想的,因此rw在继续操作之前,请检查我的分区是否以模式挂载。

注意2015-03-03我修改了答案以准确复制硬链接。原始版本可以运行,但是将许多硬链接转换为文件。除了浪费空间之外,这还假设了硬链接到位的许多用途。(我当前的图像有869个链接,其中很多在Raspbian中。)

我的脚本如下。(我的分区是 PiData,安装在/mnt/PiData

#!/bin/bash
# script to synchronise Pi files to backup
BACKUP_MOUNTED=$(mount | awk '/PiData/ {print $6}' | grep "rw")
if [ $BACKUP_MOUNTED ]; then
    echo $BACKUP_MOUNTED
    echo "Commencing Backup"
    rsync -avH --delete-during --delete-excluded --exclude-from=/usr/bin/rsync-exclude.txt / /mnt/PiData/PiBackup/
else
    echo "Backup drive not available or not writable"
fi

使用以下方法还原(或更新其他Pi):

sudo rsync -avH /mnt/PiData/PiBackup/ /

我已经增强了rsync-exclude.txt消除不必要文件的能力。

第一组是@goldilocks https://raspberrypi.stackexchange.com/users/5538/记录的目录

第二组是当我使用AFP(Apple归档协议)访问我的Pi时由OS X创建的文件和目录。(这些通常在OS X上不可见,而在Raspbian上则不可见。无论如何,都不需要备份。)即使您从未使用过AFP,也不会造成任何伤害。

第三组是不需要备份的文件(当然也不必复制到其他Pi)。例如,RPi-Monitor报告的fake-hwclock.data。您可能还会有其他人。

/proc/*
/sys/*
/dev/*
/boot/*
/tmp/*
/run/*
/mnt/*

.Trashes
._.Trashes
.fseventsd
.Spotlight-V100
.DS_Store
.AppleDesktop
.AppleDB
Network Trash Folder
Temporary Items

.bash_history
/etc/fake-hwclock.data
/var/lib/rpimonitor/stat/

1
有没有办法使输出为.img文件?
IgorGanapolsky

@IgorGanapolsky好吧,看到所有基本文件都存在(引导文件除外),这显然是可能的,但是如果要创建映像就可以了。您应该在新帖子中提出任何新问题,而不要发表评论。
Milliways

@Milliways为什么不应该使用“ sudo rsync ...”?会有一些文件无法同步?
Smilia '18

6

我的本地网络中运行着三台Pi,当它们启动并运行时,需要使用cron定期备份它们。这就是为什么我创建了一个脚本,该脚本能够创建dd,tar和rsync备份并进行还原。我更喜欢使用rsync进行备份,但其他人更喜欢dd或tar。它已经被很多人使用。希望它对其他人也有用:-) raspibackup-Raspberry创建自己的备份


1
不,很抱歉:要求用户运行(以root用户身份!)通过HTTP下载的脚本是不负责任的。请通过安全渠道分发此脚本。
克莱门特

1
我不认为这不是题外话,根本与否无关紧要。关键是应将软件分发到安全通道上,您的答案是鼓励不良的安全做法。
克莱门特

1
这将是一个巨大的进步,是:)
克莱门特

2
只是要注意,在这种情况下,通过HTTPS传递不会以任何方式增加任何安全性!您仍在从Internet下载并运行脚本。安全的过程是下载脚本(无关紧要的http / https),在编辑器中打开脚本并从上至下阅读,检查其是否奇怪和不安全。只有当您满意时才可以运行它。Framp可能是我们所有人都知道的黑客,而在这种情况下通过https发送信息只会让他微笑:)(BTW,这不是Framp的指控!)
朱利安·奈特

2
我同意你的看法。这就是为什么有两种方法描述如何安装脚本:1.使用installerScript 2.手动下载它,检查代码,然后手动安装
framp

3

这是我们用于此类目的的稳定工具:https : //github.com/aktos-io/aktos-dcs-tools

首先,该工具是从远程位置写入make ssh连接,的make backup-rootmake mount-root然后添加本地会话。因此,它支持本地备份,直接远程备份,代理远程备份。增量备份(仅传输差异),备份目录是独立的(只需选择要还原的目录/版本,任何目录都具有完整备份)。当然,您有版本(backup.last-0是最新版本)。您可以随时中断备份过程,以后再继续。

以下是针对您的特定问题的说明:

 ssh to-your-raspberry
 cd /mnt/usb0/my-rpi-backups
 git clone https://github.com/ceremcem/aktos-dcs-tools backup-tools
 ln -s backup-tools/Makefile .

 ./backup-tools/configure # you do not need to make any settings for local sessions, just save the default 

 # just for the first time
 make set-local-session  # a flag file is created
 make init               # snapshots directory is created

 # anytime you want to back up
 make backup-root        # backup with rsync

编辑

现在添加了一个新目标:您可以使用一个命令从备份中创建物理SD卡:

make create-disk-from-last-backup

按照说明创建SD卡,并使用此新创建的SD卡启动RaspberryPi。


1

这是一种完全不同的方法。您可以使用LVM大号 ogical V olume 中号 anager)做出一致的备份。除了诸如轻松添加,扩展和减少存储或从快照将操作系统还原到较早状态之类的其他改进之外,您还可以进行备份。您不必担心备份期间动态更改文件,将文件系统设置为只读,排除特定目录或其他内容。使用LVM,您只需创建一个快照,安装该快照并使用您喜欢的方法备份它。您可以使用进行复制cp -a,使用进行镜像rsync,使用进行存档tar或使用图像制作图像。dd。假设您已经安装了备份设备,则/mnt/usbhd/pi_backup/可以执行以下操作:

rpi ~$ sudo lvcreate --snapshot --name rpi_snap --size 1G rpi_vg/root_lv
rpi ~$ sudo mkdir /mnt/snapshot
rpi ~$ sudo mount /dev/mapper/rpi_vg-rpi_snap /mnt/snapshot

# make backups
rpi ~$ sudo cp -a /mnt/snapshot/ /mnt/usbhd/pi_backup/
rpi ~$ sudo rsync -aH --delete /mnt/snapshot/ /mnt/usbhd/pi_backup/
rpi ~$ sudo tar -czf /mnt/usbhd/pi_backup/backup.tar.gz -V "Backup of my Raspberry Pi" -C /mnt/snapshot/ ./
rpi ~$ sudo dd if=/mnt/snapshot/ of=/mnt/usbhd/pi_backup/backup.img bs=4M

rpi ~$ sudo umount /mnt/snapshot/
rpi ~$ sudo lvremove rpi_vg/rpi_snap

设置LVM只需要一点时间。如何做到这一点,您可以查看带有LVM的运行系统的轻松备份和快照


0

我发现了一个可以安装映像的备份工具

它还具有安装和缩小图像的实用程序。

这可能对其他人有用

它随附的文档非常简短,因此请注意以下几点:

  1. 将实用程序解压缩到任何目录中,并使脚本可执行。
  2. ext4/mnt/media(在Pi 上可以使用允许大文件的任何格式,例如exFAT或网络驱动器)在Pi上安装格式化分区。
  3. 对于初始运行,系统将提示您输入备用映像名称,例如 /mnt/Image/BusterBackup.img
  4. 系统将提示您输入Image ROOT文件系统大小(以MB为单位),对于可能的最小值,可以为0;对于完全备份,可以为空白。
  5. 在后续运行中,输入要增量更新的备用映像的路径。
An example of the commands I used:-
# Mount USB
sudo mount /dev/sda1 /mnt/Image/
# Update backup
sudo image-utils/image-backup /mnt/Image/BusterBackup.img
# Mount backup
sudo image-utils/image-mount /mnt/Image/BusterBackup.img  MountedImages
When done, run:
sudo umount MountedImages; sudo losetup -d /dev/loop0
# Compress backup
sudo sh -c "gzip -9c /mnt/Image/BusterBackup.img  > Images/BusterBackup.img.gz"

我对原始文件做了一些修改(以复制安装点),以正确计算分区的偏移量和大小,并添加了一些注释。

#!/bin/bash
# Original https://raspberrypi.org/forums/viewtopic.php?p=1528736
# 2019-09-26    Modified to set size of boot sector

trap '{ stty sane; echo ""; errexit "Aborted"; }' SIGINT SIGTERM

ADDBLK=0

# Set BOOT_SIZE_MB to the Desired boot sector size (in MB) - should be multiple of 4MB
BOOT_SIZE_MB=256
BOOTSIZEM=$BOOT_SIZE_MB'M'

BOOTBEG=8192
BOOT_SIZE="$((BOOT_SIZE_MB * 1024 * 1024))"
ROUND_SIZE="$((4 * 1024 * 1024))"
# Ensure root sector starts on an Erase Block Boundary (4MB)
ROOTBEG=$(((BOOT_SIZE + ROUND_SIZE -1) / ROUND_SIZE * ROUND_SIZE / 512 + BOOTBEG))

MNTPATH="/tmp/img-backup-mnt"

ONEMB=$((1024 * 1024))

# create BOOT loop device
mkloop1()
{
  local INFO1=""
  local SIZE1=0
  local START1=0

  sync
  INFO1="$(sfdisk -d "${IMGFILE}")"
  START1=$(grep type=c <<< "${INFO1}" | sed -n 's|^.*start=\s\+\([0-9]\+\).*$|\1|p')
  SIZE1=$(grep type=c <<< "${INFO1}" | sed -n 's|^.*size=\s\+\([0-9]\+\).*$|\1|p')
  LOOP1="$(losetup -f --show -o $((${START1} * 512)) --sizelimit $((${SIZE1} * 512)) "${IMGFILE}")"
  if [ $? -ne 0 ]; then
    errexit "Unable to create BOOT loop device"
  fi
}

rmloop1()
{
  if [ "${LOOP1}" != "" ]; then
    sync
    losetup -d "${LOOP1}"
    LOOP1=""
 fi
}

# create ROOT loop device
mkloop2()
{
  local INFO2=""
  local SIZE2=0
  local START2=0

  sync
  INFO2="$(sfdisk -d "${IMGFILE}")"
  START2=$(grep type=83 <<< "${INFO2}" | sed -n 's|^.*start=\s\+\([0-9]\+\).*$|\1|p')
  SIZE2=$(grep type=83 <<< "${INFO2}" | sed -n 's|^.*size=\s\+\([0-9]\+\).*$|\1|p')
  LOOP2="$(losetup -f --show -o $((${START2} * 512)) --sizelimit $((${SIZE2} * 512)) "${IMGFILE}")"
  if [ $? -ne 0 ]; then
    errexit "Unable to create ROOT loop device"
  fi
}

rmloop2()
{
  if [ "${LOOP2}" != "" ]; then
    sync
    losetup -d "${LOOP2}"
    LOOP2=""
  fi
}

# Mount Image partitions
mntimg()
{
  MNTED=TRUE
  if [ ! -d "${MNTPATH}/" ]; then
    mkdir "${MNTPATH}/"
    if [ $? -ne 0 ]; then
      errexit "Unable to make ROOT partition mount point"
    fi
  fi
  mkloop2
  mount "${LOOP2}" "${MNTPATH}/"
  if [ $? -ne 0 ]; then
    errexit "Unable to mount image ROOT partition"
  fi
  if [ ! -d "${MNTPATH}/boot/" ]; then
    mkdir -p "${MNTPATH}/boot/"
    if [ $? -ne 0 ]; then
      errexit "Unable to make BOOT partition mount point"
    fi
  fi
  mkloop1
  mount "${LOOP1}" "${MNTPATH}/boot/"
  if [ $? -ne 0 ]; then
    errexit "Unable to mount image BOOT partition"
  fi
}

umntimg()
{
  umount "${MNTPATH}/boot/"
  if [ $? -ne 0 ]; then
    errexit "Unable to unmount image BOOT partition"
  fi
  rmloop1
  umount "${MNTPATH}/"
  if [ $? -ne 0 ]; then
    errexit "Unable to unmount image ROOT partition"
  fi
  rmloop2
  rm -r "${MNTPATH}/"
  MNTED=FALSE
}

errexit()
{
  echo ""
  echo "$1"
  echo ""
  if [ "${MNTED}" = "TRUE" ]; then
    umount "${MNTPATH}/boot/" &> /dev/null
    umount "${MNTPATH}/" &> /dev/null
    rm -rf "${MNTPATH}/" &> /dev/null
  fi
  rmloop1
  rmloop2
  exit 1
}

LOOP1=""
LOOP2=""
MNTED=FALSE

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

if [ $(id -u) -ne 0 ]; then
  errexit "$0 must be run as root user"
fi

PGMNAME="$(basename $0)"
for PID in $(pidof -x -o %PPID "${PGMNAME}"); do
  if [ ${PID} -ne $$ ]; then
    errexit "${PGMNAME} is already running"
  fi
done

rsync --version &> /dev/null
if [ $? -ne 0 ]; then
  errexit "rsync not installed (run: apt-get install rsync)"
fi

if command -v systemctl > /dev/null && systemctl | grep -q '\-\.mount'; then
  SYSTEMD=1
elif [ -f /etc/init.d/cron ] && [ ! -h /etc/init.d/cron ]; then
  SYSTEMD=0
else
  errexit "Unrecognized init system"
fi

if [ ${SYSTEMD} -eq 1 ]; then
  ROOT_PART="$(mount | sed -n 's|^/dev/\(.*\) on / .*|\1|p')"
else
  if [ ! -h /dev/root ]; then
    errexit "/dev/root does not exist or is not a symlink"
  fi
  ROOT_PART="$(readlink /dev/root)"
fi

ROOT_TYPE=$(blkid "/dev/${ROOT_PART}" | sed -n 's|^.*TYPE="\(\S\+\)".*|\1|p')

ROOT_DEV="${ROOT_PART:0:(${#ROOT_PART} - 1)}"
if [ "${ROOT_DEV}" = "mmcblk0p" ]; then
  ROOT_DEV="${ROOT_DEV:0:(${#ROOT_DEV} - 1)}"
fi

PTUUID="$(blkid "/dev/${ROOT_DEV}" | sed -n 's|^.*PTUUID="\(\S\+\)".*|\1|p')"

DEVSIZE=$(blockdev --getsize64 "/dev/${ROOT_PART}")
BLKSIZE=$(blockdev --getbsz "/dev/${ROOT_PART}")
BLKCNT=$((${DEVSIZE} / ${BLKSIZE}))
INFO="$(df | grep /dev/root)"
DFKSIZE=$(awk '{print $2}' <<< "${INFO}")
DFKFREE=$(awk '{print $4}' <<< "${INFO}")
ROOTSIZE=$((${BLKCNT} * ${BLKSIZE}))
ROOTUSED=$(((${DFKSIZE} - ${DFKFREE}) * 1024))
IRFSMIN=$(((${ROOTUSED} + (${ADDBLK} * ${BLKSIZE}) + (${ONEMB} - 1)) / ${ONEMB}))
IRFSMAX=$(((${ROOTSIZE} + (${ONEMB} - 1)) / ${ONEMB}))

IMGFILE="$1"
if [ "${IMGFILE}" = "" ]; then
# Create Image file
  while :
  do
    echo ""
    read -r -e -i "${IMGFILE}" -p "Image file to create? " IMGFILE
    if [ "${IMGFILE}" = "" ]; then
      continue
    elif [[ ! "${IMGFILE}" =~ ^/mnt/.*$ && ! "${IMGFILE}" =~ ^/media/.*$ ]]; then
      echo ""
      echo "${IMGFILE} does not begin with /mnt/ or /media/"
      continue
    fi
    if [ -d "${IMGFILE}" ]; then
      echo ""
      echo "${IMGFILE} is a directory"
    elif [ -f "${IMGFILE}" ]; then
      echo ""
      echo -n "${IMGFILE} already exists, Ok to delete (y/n)? "
      while read -r -n 1 -s answer; do
        if [[ "${answer}" = [yYnN] ]]; then
          echo "${answer}"
          if [[ "${answer}" = [yY] ]]; then
            break 2
          else
            break 1
          fi
        fi
      done
    else
      break
    fi
  done
  IRFSSIZE=""
  while :
  do
    echo ""
    read -r -e -i "${IRFSSIZE}" -p "Image ROOT filesystem size (MB) [${IRFSMAX}]? " IRFSSIZE
    if [ "${IRFSSIZE}" = "" ]; then
      IRFSSIZE=${IRFSMAX}
      break
    elif [ ${IRFSSIZE} -ge ${IRFSMIN} ]; then
      break
    else
      echo ""
      echo "Requested image ROOT filesystem size (${IRFSSIZE}) is too small (Minimum = ${IRFSMIN})"
      IRFSSIZE=${IRFSMIN}
    fi
  done
  echo ""
  echo -n "Create ${IMGFILE} [${IRFSSIZE} MB] (y/n)? "
  while read -r -n 1 -s answer; do
    if [[ "${answer}" = [yYnN] ]]; then
      echo "${answer}"
      if [[ "${answer}" = [yY] ]]; then
        break
      else
        errexit "Aborted"
      fi
    fi
  done
  if [ -f "${IMGFILE}" ]; then
    rm "${IMGFILE}"
    if [ $? -ne 0 ]; then
      errexit "Unable to delete existing image file"
    fi
  fi
  ROOTEND=$((${ROOTBEG} + ((${IRFSSIZE} * ${ONEMB}) / 512) - 1))
  truncate -s $(((${ROOTEND} + 1) * 512)) "${IMGFILE}"
  if [ $? -ne 0 ]; then
    errexit "Unable to create image file"
  fi
# create image/partitions
  sync
  fdisk "${IMGFILE}" <<EOF > /dev/null
p
n
p
1
${BOOTBEG}
+${BOOTSIZEM}
t
c
p
n
p
2
${ROOTBEG}
${ROOTEND}
p
w
EOF

  mkloop1
  mkloop2
  mkfs.vfat "${LOOP1}" > /dev/null
  if [ $? -ne 0 ]; then
    errexit "Unable to create image BOOT filesystem"
  fi
  dosfsck "${LOOP1}" > /dev/null
  if [ $? -ne 0 ]; then
    errexit "Image BOOT filesystem appears corrupted"
  fi
  if [ "${ROOT_TYPE}" = "f2fs" ]; then
    mkfs.f2fs "${LOOP2}" > /dev/null
  else
    mkfs.ext4 -q -b ${BLKSIZE} "${LOOP2}" > /dev/null
  fi
  if [ $? -ne 0 ]; then
    errexit "Unable to create image ROOT filesystem"
  fi
  rmloop2
  rmloop1
# Initialise image PARTUUID
  fdisk "${IMGFILE}" <<EOF > /dev/null
p
x
i
0x${PTUUID}
r
p
w
EOF
# Create empty directories in image root partition
  mntimg
  mkdir "${MNTPATH}/dev/" "${MNTPATH}/media/" "${MNTPATH}/mnt/" "${MNTPATH}/proc/" "${MNTPATH}/run/" "${MNTPATH}/sys/" "${MNTPATH}/tmp/"
  if [ $? -ne 0 ]; then
    errexit "Unable to create image directories"
  fi
  chmod a+rwxt "${MNTPATH}/tmp/"
  umntimg
  echo ""
  echo "Starting full backup (for incremental backups, run: $0 ${IMGFILE})"
# END of create image/partitions
else

# Check existing Image
  if [[ ! "${IMGFILE}" =~ ^/mnt/.*$ && ! "${IMGFILE}" =~ ^/media/.*$ ]]; then
    errexit "${IMGFILE} does not begin with /mnt/ or /media/"
  fi
  if [ -d "${IMGFILE}" ]; then
    errexit "${IMGFILE} is a directory"
  elif [ ! -f "${IMGFILE}" ]; then
    errexit "${IMGFILE} not found"
  fi
  echo "Starting incremental backup to ${IMGFILE}"
fi

# rsync root partition
mntimg
sync
rsync -aDH --partial --numeric-ids --delete --force --exclude "${MNTPATH}" --exclude '/dev' --exclude '/media' --exclude '/mnt/*/*' --exclude '/proc' --exclude '/run' --exclude '/sys' \
--exclude '/tmp' --exclude 'lost\+found' --exclude '/etc/udev/rules.d/70-persistent-net.rules' --exclude '/var/lib/asterisk/astdb.sqlite3-journal' / "${MNTPATH}/"
if [[ $? -ne 0 && $? -ne 24 ]]; then
  errexit "Unable to create backup"
fi
sync
umntimg

-1

打开终端并输入'lsblk -f'。
这应该显示所有连接的存储设备。
然后输入“ dd if = / dev / [sd卡的名称] bs = 1M”。
这将需要一段时间,因此您可能需要在后台运行它。
这与在Linux中备份SD卡的方法完全相同。


这样可以备份所有内容,甚至可以备份不必要和不需要的文件。
IgorGanapolsky

3
这将导致备份不一致,因为在正在运行的系统上,备份期间发生了一些变化!
Ingo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.