Skip to content

Instantly share code, notes, and snippets.

@HeroBrine1st
Last active June 1, 2022 18:43
Show Gist options
  • Save HeroBrine1st/5aa21e564141e45b7f37599411176f20 to your computer and use it in GitHub Desktop.
Save HeroBrine1st/5aa21e564141e45b7f37599411176f20 to your computer and use it in GitHub Desktop.
Как пользоваться BTRFS

Дисклеймер: Ваши данные - ваша забота и ответственность.
Дисклеймер 2: Данный текст не является абсолютно полным руководством (по размеру понятно). BTRFS не является файловой системой, которую можно изучить "на ходу". (впрочем, из-за этого этот текст и появился)

Я буду писать сюда то, что не смог найти в интернете сразу или считаю очень важным.

Создание и восстановление бекапов (снапшотов) корня файловой системы

Подготовка: создание файловой системы

  1. Создайте ФС
  2. В пустой ФС создайте subvolume, который будете использовать как корневой. Я предпочитаю @ и буду использовать это имя дальше, тогда команда: sudo btrfs subvolume create @
  3. Укажите subvol=@ в опциях монтирования в fstab (или в вашем менеджере точек монтирования)

Не указывайте созданный сабволюм как сабволюм по умолчанию (btrfs subsolume set-default). Так просто проще.

Создание снепшота

  1. Смонтируйте вашу файловую систему без опции subvol. Можно добавить запись в fstab без такой опции для удобства
  2. Перейдите в точку монтирования и (опционально) создайте там папку со снепшотами. Я использую snapshots и буду использовать это имя дальше.
  3. Сделайте снепшот btrfs subvolume snapshot исходный_сабволюм путь_к_снапшоту. Лучше всего добавить флаг -r, чтобы снапшот нельзя было изменить (read-only).

Восстановление снепшота в корень системы

  1. Запуститесь с другой ФС (с флешки, например)
  2. Смонтируйте систему без указания опции subvol
  3. Переименуйте @ в @_old (или во что угодно).
  4. (Если ваш бекап read-only) Создайте read-write снапшот со снапшота-бекапа: btrfs subvolume snapshot snapshots/my_backup @_new
  5. Переименуйте новый снапшот в @: mv @_new @
  6. Засуньте куда-нибудь старый снапшот (@_old) или удалите (rm -rf)
  7. Загрузитесь назад в систему

Резервное копирование на удалённый сервер

Этот способ только проходит тестированиe (отлично работает на компьютере разработчика). Скрипт разработан не полностью (хоть и готов к работе) и часть функционала подлежит изменению.

  1. Положите файл snapshot.sh в место, куда вы хотите складывать снепшоты (а так же chmod +x и т.д.). Переименовывать нельзя.
  2. Положите файл create-snapshot-and-backup.sh куда-нибудь и настройте константы в начале файла
  3. Настройте SSH конфиг у рутового пользователя (чтобы он мог подключиться к вашему серверу), импортируйте свой GPG ключ (им будет шифроваться копия)
  4. Для бекапа вызовите последний файл от рута с аргументами: тип копии (manual, automatic, там можно любое сочетание символов), ssh хост (как для команды ssh), удалённый путь на удалённом сервере, айди вашего ключа GPG, который доступен с вашего пользователя. Пример: sudo ./create-snapshot.sh manual RPi /mnt/basic/Backups/DESKTOP-IJK2GUG/snapshots EB32416FC83FF911339E786815C7CA7871925C20
BTRFS_ROOT="/btrfs_root" # Корневая папка btrfs
SNAPSHOT_DIR="$BTRFS_ROOT/snapshots/" # Место, где лежат снепшоты
BOOT_DIR="/boot/" # Ваша папка /boot (у меня он лежит на vfat, так что отдельно настроен)
BOOT_COPY_SUBVOLUME="$BTRFS_ROOT/@boot/" # Путь к сабволюму, в который будет делаться копия /boot
SUBVOLUMES=("@" "@home" "@boot") # Сабволюмы для копирования
BACKUP_FILE_SUFFIX=".btrfs.gpg" # Суффикс для файлов копий
###############################
set -o pipefail
reason=$1
ssh_host=$2
remote_path=$3
gpg_recipient=$4
tmp_dir="/tmp/$(openssl rand -hex 32)"
asuser="sudo -u ${SUDO_USER:-$USER}"
rsync --archive "$BOOT_DIR" "$BOOT_COPY_SUBVOLUME"
if [[ $? != 0 ]]; then
exit $?
fi
echo "$SNAPSHOT_DIR/snapshot.sh" "$reason" "$BTRFS_ROOT" ${SUBVOLUMES[@]}
output=$("$SNAPSHOT_DIR/snapshot.sh" "$reason" "$BTRFS_ROOT" ${SUBVOLUMES[@]})
if [[ $? != 0 ]]; then
echo $output
echo "Creating snapshots failed with code $?"
code=$?
if [[ -d $snapshot ]]; then
echo "Cleaning up..."
btrfs subvolume delete "$snapshot"/*
rm -rvf $snapshot
fi
exit $code
fi
snapshot=$(echo "$output" | head -1)
echo "Made snapshot $snapshot"
set -e
mkdir "$tmp_dir"
backup_path="$tmp_dir/$(basename $snapshot)"
sshfs "$ssh_host:$remote_path" "$tmp_dir"
mkdir $backup_path
checkBackups() {
path=$1
shift
for file in $@; do
if [[ ! -f "$path/$file$BACKUP_FILE_SUFFIX" ]]; then
return 1
fi
done
return 0
}
for old_snapshot in $(find "$SNAPSHOT_DIR" -maxdepth 1 ! -newer "$snapshot" -print | grep "snapshot-" | xargs ls -td | tail -n +2); do
name=${old_snapshot#$SNAPSHOT_DIR}
if [[ -d "$tmp_dir/$name" ]] && checkBackups "$tmp_dir/$name" ${SUBVOLUMES[@]}; then
parent=$old_snapshot
break
fi
done
if [[ -n "$parent" ]]; then
echo "Found parent backup: $parent"
else
echo "No parent backups found - will create the first one $parent"
fi
for subvolume in ${SUBVOLUMES[@]}; do
btrfs send \
${parent:+"-p"} ${parent:+"$parent/$subvolume"} \
"$snapshot/$subvolume" | \
pv | \
$asuser gpg --encrypt --recipient "$gpg_recipient" > "$backup_path/$subvolume$BACKUP_FILE_SUFFIX"
done
umount "$tmp_dir"
#!/bin/bash
set -e
if [ "$EUID" -ne 0 ]; then
echo "Please run as root"
exit
fi
path=$0
if [[ -f $path ]]; then
path=$(dirname $path)
fi
reason=$1
btrfs_root=$(realpath $2)
shift
shift
snapshot_dir="$path/snapshot-$(date +"%Y-%m-%dT%H-%M-%S")-$reason"
echo $snapshot_dir
mkdir $snapshot_dir
for subvolume in $@; do
btrfs subvolume snapshot -r "$btrfs_root/$subvolume" "$snapshot_dir/$subvolume"
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment