Бэкап данных с btrfs и LVM bash скриптами

image

Уже было много постов о резервном копировании, особенно много для ОС Linux. Озаботился и я настройкой резервного копирования.
Требовалось создавать бэкапы системы, данных с примонтированного раздела и LVM томов (диски виртуальных машин). Были мысли использовать Bacula, т.к. знаком с ней, но поскольку дома только 1 компьютер клиент-серверная архитектура только создавала бы дополнительные сложности при восстановлении в случае повреждения системы. Значит систему и данные просто копируем, образ LVM раздела создаем с помощью dd. Хотелось делать резервную каждый день (хотя бы данных) и хранить минимум 14 дней. Но поиски готовых и простых решений, удовлетворяющих всем потребностям не увенчались успехом. А значит берем в руки bash и пишем свой велосипед. В этой статье я делюсь тем, что вышло.

Disclaimer: при написании скриптов не было цели написать монстров, которые делают все. Нужен был простой и надежный способ бэкапа. Буду благодарен за указание неточностей и узких мест скриптов (тех, где могут возникнуть ошибки). Статья рассчитана больше на новичков в Linux, которые ищут готовое решение и на лентяев, :) которым лень писать самим.

Исходные условия:

  • ОС: Arch Linux
  • Корневая файловая система (/): btrfs, копировать нужно все файлы.
  • Раздел с данными (/mnt/data/): btrfs.
  • LVM тома (/dev/virt_image_array/*).
  • Раздел для бэкапов (/mnt/backup/: etx4, сюда будут складываться резервные копии.
  • Необходимые утилиты (кроме входящих в базовый дистр): rsync, btrfs-progs (для управления btrfs).

Было решено раз в неделю делать полную копию всего и каждый день создавать снапшот разделов с btrfs. Можно также создавать снапшоты LVM томов, но для меня потеря данных за неделю не критична, поэтому хватит еженедельных копий.

Итак, скрипт № 1, создает копию файлов корневого раздела в /mnt/backup/root/«номер дня»/.

Скрипт №1
#!/bin/bash
set -e
echo "Date_start: `date +%Y-%m-%d-%H-%M-%S`"						
#Дата для удобного чтения логов

### Vars ###
#Количество дней от точки отсчета (1970 год)
day=$((`date +%s` / (60*60*24)))		
#Сколько дней хранить бэкапы
dayexp=21	
#Что копируем					
path="/" 		
#Откуда копируем (путь до снапшота)				
spath="/snapshots/backup_script/"	
#Куда копируем						
dpath="/mnt/backup/root/"
						

### Delete Old Backups ###	
#Удаляем старые бэкапы
find $dpath -maxdepth 1 -type d -mtime +$dayexp -exec rm -rf {} \;

### Check exist snapshot ###
#Проверяем наличие снапшота, удаляем, если находим						
if (( "`btrfs subvolume list $path | grep backup_script |wc -l`" > 0 ))
then
echo "Warning: Snapshot exist, deleting"
btrfs subvolume delete $spath
fi

### Create snapshot ###
#Делаем снапшот						
btrfs subvolume snapshot $path $spath

### Rsync ###						
#Копируем данные из снапшота
rsync -aAXv $spath $dpath$day

### Delete snapshot ###						
#Удаляем снапшот
btrfs subvolume delete $spath
echo "Backup succesful complete"
echo "Date_end: `date +%Y-%m-%d-%H-%M-%S`"
exit 0



Скрипт № 2, создает снапшоты корневой ФС (скрипт логикой очень похож на 1й, поэтому комментировать буду только отличия). Снапшот имеет имя auto_«номер дня».
Скрипт №2
#!/bin/bash
set -e
echo "Date_start: `date +%Y-%m-%d-%H-%M-%S`"

### Vars ###
day=$((`date +%s` / (60*60*24)))
dayexp=14								
path="/" 								
spath="/snapshots/"							

### Delete Old Backups / Check existing snapshot ###
#Старые бэкапы удаляем, если находим сегодняшний снапшот выходим 
btrfs subvolume list $path |grep auto |sed -e '1,$ s/.*_//g'| while read ONE_OF_LIST
do
if [[ "$ONE_OF_LIST" -lt "$day - $dayexp"  ]]
then
echo "remove: $spath"auto_"$ONE_OF_LIST"
btrfs subvolume delete $spath"auto_"$ONE_OF_LIST
fi
if [[ "$ONE_OF_LIST" -eq "$day" ]]
then
echo "Eroor: snapshot auto_$ONE_OF_LIST exist. Stop script execution."
exit 1
fi
done

### Create snapshot ###
btrfs subvolume snapshot $path $spath"auto_"$day

### End ###
echo "Snapshot succesful created"
echo "Date_end: `date +%Y-%m-%d-%H-%M-%S`"
exit 0



Скрипт № 3, создает копию LVM тома:

Скрипт №3
#!/bin/bash
set -e
echo "Date_start: `date +%Y-%m-%d-%H-%M-%S`"

### Vars ###
day=$((`date +%s` / (60*60*24)))
dayexp=21
#Имя копируемого LVM тома								
path="/dev/virt_image_array/win_home_system" 				
spath="/dev/virt_image_array/backup_lvm1"	      			
dpath="/mnt/backup/lvm1/"					

### Delete Old Backups ###
find $dpath -maxdepth 1 -type f -mtime +$dayexp -exec rm -rf {} \;

### Check exist snapshot ###
#Проверяем отсутвие снапшота, удаляем, если находим
if (( "`ls /dev/virt_image_array |grep backup_lvm1|wc -l`" > 0 ))
then
echo "Warning: Snapshot exist, deleting"
lvremove --autobackup y -f $spath
fi

### Create snapshot ###
#Создаем снапшот, если на том идет активная запись, то можно увеличить место на снапшоте. Я сделал 10 Gb.
lvcreate --size 10G --snapshot --name backup_lvm1 $path
#Если у вас при создании снапшота спрашивает подтверждения на вайп сигнатуры используйте вариант с echo
#echo "y"| lvcreate --size 10G -A y --snapshot --name backup_lvm1 $path
### DD ###
#Можно поиграться с bs, у меня быстрей всего работает с 16M
dd if=$spath of=$dpath$day bs=16M

### Delete snapshot ###
lvremove --autobackup y -f $spath

echo "Backup succesful complete"
echo "Date_end: `date +%Y-%m-%d-%H-%M-%S`"
exit 0



Поскольку скрипты, бэкапящие данные из /mnt/data аналогичны скриптам 1 и 2 думаю, нет необходимости их писать.

Добавляем в crontab и определяем, как часто создавать бэкапы (в моем примере бэкап создается раз в неделю, снапшоты раз в день).
20 01 * * 1 /usr/bin/backup_root.sh >> /var/log/backup_root.log 2>&1
50 01 * * 1 /usr/bin/backup_lvm1.sh >> /var/log/backup_lvm.log 2>&1
20 01 * * * /usr/bin/snapshot_root.sh >> /var/log/snapshot_root.log 2>&1


Что еще можно прикрутить:

  • Если нужно создавать больше бэкапов, а места нет, то можно поставить ФС с дедупликацией (например Opendedup), но снизится надежность хранения данных.
  • В качестве хранилища может выступать папка подключенная по NFS или sshfs.
  • Если нужно создавать бэкапы или снапшоты чаще, чем раз в день можно считать не дни с 1970 года, а часы (костыль, будет еще непонятней за какую дату бэкап).


UPD. По совету onix74 подправил скрипты.
Поделиться публикацией
Похожие публикации
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 14
  • 0
    Не совсем понял, какой смысл при наличии снапшотов в BTRFS притягивать LVM, бэкапы которого не отличаются надежностью и дают отличный способ отстрелить себе ноги.

    Кстати, BTRFS хоть раз Вас подвела? Не страшно ли ей данные доверять?

    Также вопрос по поводу «В качестве хранилища может выступать папка подключенная по NFS или sshfs» — а не нарушаете ли Вы этим основной принцип бэкапа — что бэкап НЕ должен быть доступен для удаления/правки с машины, с которой он выполнился?
    • +1
      Btrfs по заверению разработчиков и отзывам пользователей не совсем стабильна, поэтому полагаться только на снапшоты считаю не слишком разумным. У меня сбоев пока не было. Доверять данные страшновато, поэтому настроил бэкап. :)
      По поводу LVM- сначала планировал хранить образы на Btrfs, но в процессе экспатации выяснилась неприятная особенность. KVM плохо совместим с Btrfs, IO ужасный. В интернете пишут, что это особенность внутренней структуры Btrfs. Поэтому используется LVM.
      • 0
        А может просто не использовать btrfs в пользу ext4, например? :) Ведь довольно легко можно подождать еще лет 7-12, когда btrfs-таки допилят до стейбла.
        • 0
          Основной причиной использования Btrfs была ее отличная работа с raid10, работает быстрее mdadm. Ну и плюшки в виде снапшотов, да и посмотреть хотелось, что за зверь.
      • 0
        не нарушаете ли Вы этим основной принцип бэкапа — что бэкап НЕ должен быть доступен для удаления/правки с машины, с которой он выполнился?

        Примонтировать по NFS можно например NAS, который не может сам забрать бэкапы. Если есть другая линукс машина, то безусловно лучше забирать бэкапы бэкап-сервером. Но если есть сервер резервного копирования лучше использовать спец продукты (таже bacula).
        • 0
          Если забирать бэкапы бэкап сервером придется выдавать рута от всех машин бэкапу и компрометация бэкапа приведет к небольшой ядерной войне, ибо она будет иметь доступ везде. Да, Bacula вариант, но оно настолько монструозно, что временами становится страшно.
          • 0
            Не обязательно рута, можно делать бекап по крону, а бэкап сервер только забирает файл любым удобным способом.
        • 0
          > Кстати, BTRFS хоть раз Вас подвела? Не страшно ли ей данные доверять?

          Использую btrfs на домашнем сервере около 2х лет. Технологии можно сказать на острие атаки — Linux Arch, multi-deviсe btrfs (5 дисков разных эпох и размеров — от 500G до 4T), raid1, snapshots. Все работает как надо. Серьёзных проблем с btrfs не было. Для снапшотов использую тулзу snapper.

          Самые важные данные конечно же копирую на дополнительный диск, который хранится на работе.
        • 0
          Под Линукс есть еще ZFS.
          И в качестве менеджера бэкапов-снапшотов zfSnap.
          • 0
            Так не проще?
            > ### Delete Old Backups ###
            find $dpath -maxdepth 1 -type d -mtime +$dayexp -exec rm -rf {} \;

            • 0
              Да, спасибо, ваша конструкция выглядит лучше и читается легче.
            • 0
              > можно считать не дни с 1970 года, а часы (костыль, будет еще непонятней за какую дату бэкап).

              Сохранять дату кол-вом дней тоже не самый читабельный вариант ;)
              Мне кажется, если бекапы делаются не чаще раза в минуту, то достаточно универсальным и читабельным вариантом именования будет
              $ date +'%Y%m%d-%H%M'
              т.е.
              $ FILENAME=backup-`date +'%Y%m%d-%H%M'`.bak
              $ echo $FILENAME
              backup-20140210-1630.bak
              • 0
                Да, так тоже можно, но нужна будет функция, парсящая дату и вычисляющая сколько прошло дней. Сложнее, а выгода не большая при несложной иерархии архива.
                • 0
                  Ну сейчас в приведённых скриптах я не вижу использования данных из имени файла для вычиления сколько дней прошло. find сам вычисляет сколько прошло часов/минут/секунд. А чтобы не зависеть от времени, затраченного на создание/изменение файла, после окончания создания бекапа можно через touch устанавливать ему access/modif -time по его имени. Только тогда копировать/переносить файлы бекапов нужно будет утилитой, сохраняющей эти данные (например rsync), либо добавлять touch по окончании переноса, но это может быть единый механизм, ничего нового придумывать не придётся.

                  Да и перевести из читабельного варианта дату в timestamp для каких-либо вычислений не сложно. Но опять же, чтобы не делать лишних телодвижений, можно в имени файла хранить и «читабельную» дату и дату в том формате, который упростит эти самые вычисления.

                  Да и вообще зачем чего-то вычислять в данной ситуации для оперирования файлами? Вывести листинг диры с сортировкой по имени/времени, отрезать нужное кол-во «строк» при помощи head или tail — вот и готов список из определённого кол-ва самых свежих файлов, либо наоборот список из остальных менее свежих файлов.



                  У каждого свой подход. Но как по мне, то строковый формат даты гораздо удобнее. Если программно он не используется, то здесь даже вопроса не стоит как именовать привязанные к датам файлы. Если же нужно как-то работать с файлами программно, то лучше уж дописать пару строчек в скрипт, но избавить себя от необходимости вычисления даты по хитрому имени в случае просмотра списка файлов-бекапов. Конечно же, это только моё личное мнение :)

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое