如何在更新系统之前自动创建 Btrfs 快照?

如何在更新系统之前自动创建 Btrfs 快照?

我已经在 Btrfs 根分区上安装了 Ubuntu 20.04 以使用其快照功能。

为了尽可能简单,我想将 Btrfs 快照的创建集成到我的upgrade-alias 命令中,目前如下所示:

sudo apt update && sudo apt upgrade -y && sudo flatpak update -y && sudo snap refresh

我该如何在更新之前最好地添加快照,以便如果出现任何问题我可以回滚?

是否还可以同时删除旧快照?(我的根分区已填充不到 10%,因此我可以多次复制整个系统,但我想它会随着每周更新而很快填满?)

答案1

我将基于Ignacio Nunez Hernanz 的精彩剧本

#!/bin/bash

#
# Script that creates BTRFS snapshots, manually or from cron
#
# Usage:
#          sudo btrfs-snp  <dir> (<tag>) (<limit>) (<seconds>) (<destdir>)
#
# Copyleft 2017 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com>
# GPL licensed (see end of file) * Use at your own risk!
#
# Based on btrfs-snap by Birger Monsen
#
# More at https://ownyourbits.com
#

function btrfs-snp()
{
  local   BIN="${0##*/}"
  local   DIR="${1}"
  local   TAG="${2:-snapshot}"
  local LIMIT="${3:-0}"
  local  TIME="${4:-0}"
  local   DST="${5:-.snapshots}"

  ## usage
  [[ "$*" == "" ]] || [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]] && {
echo "Usage: $BIN <dir> (<tag>) (<limit>) (<seconds>) (<destdir>)

  dir     │ create snapshot of <dir>
  tag     │ name the snapshot <tag>_<timestamp>
  limit   │ keep <limit> snapshots with this tag. 0 to disable
  seconds │ don't create snapshots before <seconds> have passed from last with this tag. 0 to disable
  destdir │ store snapshot in <destdir>, relative to <dir>

Cron example: Hourly snapshot for one day, daily for one week, weekly for one month, and monthly for one year.

cat > /etc/cron.hourly/$BIN <<EOF
#!/bin/bash
/usr/local/sbin/$BIN /home hourly  24 3600
/usr/local/sbin/$BIN /home daily    7 86400
/usr/local/sbin/$BIN /home weekly   4 604800
/usr/local/sbin/$BIN /     weekly   4 604800
/usr/local/sbin/$BIN /home monthly 12 2592000
EOF
chmod +x /etc/cron.hourly/$BIN"
return 0
  }

  ## checks
  local SNAPSHOT=${TAG}_$( date +%F_%H%M%S )

  [[ ${EUID} -ne 0  ]] && { echo "Must be run as root. Try 'sudo $BIN'" ; return 1; }
  [[ -d "$SNAPSHOT" ]] && { echo "$SNAPSHOT already exists"             ; return 1; }

  mount -t btrfs | cut -d' ' -f3 | grep -q "^${DIR}$" || {
btrfs subvolume show "$DIR" | grep -q "${DIR}$" || {
  echo "$DIR is not a BTRFS mountpoint or snapshot"
  return 1
}
  }

  DST="$DIR/$DST"
  mkdir -p "$DST"
  local SNAPS=( $( btrfs subvolume list -s --sort=gen "$DST" | awk '{ print $14 }' | grep "${TAG}_" ) )

  ## check time of the last snapshot for this tag
  [[ "$TIME" != 0 ]] && [[ "${#SNAPS[@]}" != 0 ]] && {
local LATEST=$( sed -r "s|.*_(.*_.*)|\\1|;s|_([0-9]{2})([0-9]{2})([0-9]{2})| \\1:\\2:\\3|" <<< "${SNAPS[-1]}" )
LATEST=$( date +%s -d "$LATEST" ) || return 1

[[ $(( LATEST + TIME )) -gt $( date +%s ) ]] && { echo "No new snapshot needed for $TAG"; return 0; }
  }

  ## do it
  btrfs subvolume snapshot -r "$DIR" "$DST/$SNAPSHOT" || return 1

  ## prune older backups
  [[ "$LIMIT" != 0 ]] && \
  [[ ${#SNAPS[@]} -ge $LIMIT ]] && \
echo "Pruning old snapshots..." && \
for (( i=0; i <= $(( ${#SNAPS[@]} - LIMIT )); i++ )); do
  btrfs subvolume delete "$DIR/${SNAPS[$i]}"
done

  echo "snapshot $SNAPSHOT generated"
}

btrfs-snp "$@"

# License
#
# This script is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this script; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA  02111-1307  USA

相关使用信息:

# btrfs-snp
Usage: btrfs-snp <dir> (<tag>) (<limit>) (<seconds>) (<destdir>)

dir     │ create snapshot of <dir>
tag     │ name the snapshot <tag>_<timestamp>
limit   │ keep <limit> snapshots with this tag. 0 to disable
seconds │ don't create snapshots before <seconds> have passed from last with this tag. 0 to disable
destdir │ store snapshot in <destdir>, relative to <dir>

您的升级别名需要如下所示:

btrfs-snp / syschanges 3 600 && ...生成带有标签的快照系统变更,但/.snapshots如果过去 5 分钟内已经有一个,则不会,并且最多保留 3 个。

这为您提供了 5 分钟的时间来执行重复操作而不会造成混乱,例如,如果您想在一个安装步骤中从不同的 repos 或 ppa 安装,而不仅仅是升级。

然后,您可以按照 btrfs 最佳实践使用和恢复这些快照。

答案2

在 中制作快照非常容易btrfs

首先将包含文件系统的分区挂载btrfs到例如/mnt。我们假设它是/dev/sda1

sudo mount /dev/sda1 /mnt
cd /mnt

/如果您有一个带有at@/homeat 的标准 Ubuntu 安装@home,运行ls将显示两个项目:@@home

此外,如果您之前创建了快照,它们也会显示在那里。

要创建和的快照//home请运行以下命令:

sudo btrfs sub snap @ @-BACKUP && sudo btrfs sub snap @home @home-BACKUP

如果您想在创建新备份之前删除现有备份,命令如下:

sudo btrfs sub del @-BACKUP && sudo btrfs sub del @home-BACKUP

就如此容易。

完成后,通过以下方式卸载分区/mnt

sudo umount /mnt

此外,我还可以补充一点,您可以创建带有时间戳的快照或进行增量备份。但这有点超出了问题的范围。

您可以将这些命令组合成一个文本文件,例如backup.sh

例子:

#!/bin/sh
mount /dev/sda1 /mnt
cd /mnt
[ -d @-BACKUP ] && sudo btrfs sub del @-BACKUP #Checks is backup exists and deletes it
[ -d @home-BACKUP ] && sudo btrfs sub del @home-BACKUP
btrfs sub snap @ @-BACKUP
btrfs sub snap @home @home-BACKUP
cd /
umount /mnt

该脚本应该使用 来运行sudo

答案3

我有几个想法要告诉你。挑选并组合。

  1. 专门构建的现成软件,例如apt-btrfs-快照或者etckeeper在后台自动完成这些工作。这可能是最好的方法了。
  2. 要复制上述软件包的自定义版本,您需要接入 apt每次更新后运行脚本。上面的软件包当然可以帮助您了解如何做到这一点的最佳实践,并在另一个答案。但它也可能是同样简单的事情btrfs sub snapshot -r /mnt/btrfsroot/@/ /mnt/btrfsroot/snapshots/root-$(date +%y%m%d)
  3. 无论如何,虽然 apt/dpkg 有这样的钩子,但没有快照其他的我不太清楚。不过这应该不是什么问题,因为你首先按照你的问题运行了 apt。
  4. 您最终会得到许多快照,因此您还需要一种手动或自动删除过时快照的方法。为了让您了解如何做到这一点,假设您的自动快照具有以下路径,/mnt/btrfsroot/snapshots/@*-apthook-YYMMDDHH那么您将例如在每个月 12 日运行 cronjob 34 03 12 * * btrfs sub delete /mnt/btrfsroot/snapshots/@*-apthook-$(date --date='15 days ago' +\%y\%m)*。查看手册页man 5 crontabman date了解更多信息。

我希望这能让你朝着正确的方向前进。同样,我建议你直接照做apt-btrfs-snapshot就行了。请注意,截至目前,apt-btrfs-snapshot 假设您的根分区名为@。这是 Ubuntu 和许多其他发行版的默认设置。

如果有什么不清楚的话,请随时询问后续问题。

/PS:您是否了解(您正在运行的系统的根)和 btrfs-root之间的区别?

答案4

您可以使用此 shell 脚本轻松完成此操作。

创建一个包含以下内容的 shell 脚本:

# Directory for saving snapshots
SNAPDIR=/snapshots
export SNAPDIR

# Delete snapshots
sudo btrfs subvolume delete /mnt/btrfs/backup_*

# Ask user for the name of snapshot
echo -n "What will be the name of snapshot? "
read SNAPNAME

# Create the snapshot
sudo btrfs subvolume snapshot /mnt/btrfs/ $SNAPDIR/backup_$SNAPNAME

# Check if the snapshot created successfully, if not then exit
if [ $? -ne 0 ]
then
echo "Failed to create snapshot"
exit 1
fi

# Commands to execute after creating snapshot
sudo apt update && sudo apt upgrade -y && sudo flatpak update -y && sudo snap refresh

创建文件后,将 /snapshots 替换为所需的快照保存目录。然后将其放置在任何地方。

现在通过执行以下命令使其可执行:

chmod +x /path/to/shell/script.sh

现在更改升级别名的命令,以便它指向脚本。

现在运行您的别名将首先删除带有backup_前缀的快照,然后它将对以 开头的名称保存的文件系统进行快照 backup_

请注意,首次运行时可能会显示错误。但请忽略它,因为首次运行时没有备份,因此无需删除任何内容。另外,不要在将backup_保存备份快照的目录中创建名称以 at 开头的子卷或快照。这将导致在运行脚本时删除该目录。此外,快照不会包含来自其他快照、子卷和已安装分区的文件。

相关内容