python

Десятимиллионный скрипт резервного копирования

  • воскресенье, 6 июля 2014 г. в 03:10:42
http://habrahabr.ru/post/228787/

image
Это статья-мануал по скрипту резервного копирования, написанному мной. Скрипт написан на python для Linux. Кому интересно прошу под хабракат.


Возможности


  • Создание дифференциальных/полных копий папок
  • Создание дифференциальных/полных копий с файловой системы BTRFS
  • Создание дифференциальных/полных копий LVM томов
  • Создание снапшотов BTRFS
  • Ротирование бэкапов/снапшотов
  • Логгирование хода выполнения резервного копирования
  • Отправка email оповещений
  • Выполнение скриптов до/после резервного копирования


Установка


В /etc/apt/source.list добавить:
deb http://repo.nixdi.com/ubuntu/ precise soft

И выполнить в терминале:
apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 74C7B31B5F4E1715 && apt-get update && apt-get install py4backup

Обновление пакета выполняется командой:
apt-get update && apt-get upgrade py4backup

ИЛИ
Вручную скачать пакет командой:
wget http://repo.nixdi.com/ubuntu/py4backup_latest.deb

и установить его:
dpkg -i ./py4backup_latest.deb

ИЛИ
Для дистрибутивов, отличных от Ubuntu/Debian выполнить:
git clone https://github.com/larrabee/py4backup

И скопировать файлы ddd и py4backup в директорию с бинарными файлами (обычно /usr/bin), файл py4backup_lib.py в директорию библиотек python. Также потребуется поставить зависимости вручную. Необходим python 3.x, btrfs-tools (btrfs-progs), lvm2, rsync. В папке examples/ вы найдете примеры конфигурационных файлов. Их необходимо скопировать в /etc/py4backup/

Настройка


После установки необходимо скопировать конфигурационные файлы из примера. Для этого выполните:
mv /etc/py4backup/py4backup.conf.example /etc/py4backup/py4backup.conf
mv /etc/py4backup/jobs.conf.example /etc/py4backup/jobs.conf

И откройте файл py4backup.conf на редактирование текстовым редактором.
Для boolean параметров допустимо использование True/False, yes/no или 1/0.
Отделять параметр от его значения можно символами '=' или ':'.
Каждый параметр должен находится в своей секции. Название секции пишется перед набором параметров в квадратных скобках ('[]')
Порядок следования параметров в секции и секций не важен. Если параметр не указан в конфигурационном файле, то используется стандартное значение.
Пример конфигурационного файла:

[MAIL]
send_mail_reports = True
login = login@test.com
passwd = password
sendto = recipient@test.com
server = mail.test.com
port = 25
tls = True

[DD]
bs = 4M
ddd_bs = 4096
ddd_hash = md5


[LOGGING]
logpath = /var/log/py4backup.log
enable_logging = True
log_with_time = True
traceback = False
command_output = True

[OTHER]
temp_snap_name = py4backup_temp_snap
host_desc = My Description
pathenv = /sbin:/usr/sbin


Рассмотрим параметры подробней:
[MAIL]: здесь определяются параметры отправки уведомлений через email.
send_mail_reports: включает/выключает отправку email отчетов после выполнения задания.
login: логин для входа на smtp сервер.
passwd: пароль для входа на smtp сервер.
sendto: получатели уведомления. Можно вписать несколько адресов через пробел.
server: доменное имя или IP адрес smtp сервера.
port: порт smtp сервера.
tls: включает/выключает использование TLS шифрования.

[DD]: здесь указываются параметры создания резервных копий с помощью программ DD и DDD.
bs: размер блока для программы DD (Используется для создания полных копий LVM томов). Можно указывать размер в байтах, килобайтах (k) и мегабайтах (M). Влияет на скорость создания копии. Оптимальное значение- 32M.
ddd_bs: размер блока для программы DDD (Используется для создания дифференциальных копий LVM томов). Можно указывать размер в байтах. Чем больше размер, тем больше места занимает дифференциальная копия, но тем быстрее она создается. Оптимальное значение- 4096.
ddd_hash: алгоритм хеширования блоков. Возможен выбор между md5, crc32 и None. MD5 сильнее нагружает процессор, чем crc32 и занимает больше места, но в случае использования md5 намного меньше шанс коллизий.
None выключает создание чек сумм. Время создания резервной копии, ее размер и нагрузка на процессор минимальны, но в случае повреждения резервной копии вы не будете знать об этом. Не рекомендуется к использованию.

[LOGGING]: настройка ведения журнала заданий.
logpath: путь до журнала. Если вы используете не стандартное размещение журнала не забудьте поменять настройки logrotate.
enable_logging: включает/выключает ведение журнала.
log_with_time: включает/выключает добавление к каждой записи журнала даты и времени.
traceback: включает/выключает добавление traceback'ов в лог при ошибках. Полезно при отладке.
command_output: включает/выключает добавление в лог консольного вывода команд. Полезно при отладке.

[OTHER]: настройки, не вошедшие в другие разделы.
temp_snap_name: имя временных снапшотов. Используется при создании копии LVM томов или папок/файлов на файловой системе BTRFS. Рекомендуется не изменять без необходимости.
host_desc: текстовое описание хоста. Значение этого параметра будет добавлено в файл журнала и email отчет.
pathenv: значение этого параметра будет добавлено к переменной $PATH(если пройдет проверку). Если необходимо добавить несколько папок их необходимо разделить двоеточием (':') Например в Ubuntu для создания копий LVM томов при запуске py4backup через cron необходимо добавить в переменную $PATH папку /sbin. В данном случае путь указывается без последнего слеша (‘/’)

Задания


Общие сведения

Список заданий находится в файле /etc/py4backup/jobs.conf
Пример задания:

[mail-diff]
type = file-diff
sopath = server:/opt/
snpath =
dpath = /mnt/backup_dest/
dayexp = 30
prescript = bash /root/script1.sh
postscript = bash /root/script2.sh
include = test test2
exclude = tests*

Где:
[xxx]: уникальное имя задания.
type: тип задания. Подробности см ниже.
sopath: источник резервной копии. В типах file-full, file-diff в качестве источника можно указывать удаленные хосты.
snpath: где создать снапшот. Используется только типами btrfs-full, btrfs-diff и btrfs-snap
dpath: куда сохранять резервную копию. В типах btrfs-full, btrfs-diff, file-full, file-diff в качестве назначения можно указывать удаленные хосты.
dayexp: через сколько дней удалять старые резервные копии. Если установить -1 резервные копии не будут удаляться никогда.
prescript: скрипт, выполняющийся перед резервным копированием. Пайпы, конвейер и другие операторы bash не работают. Если требуется выполнять сложные команды сохраняйте их виде скрипта и запускайте его.
postscript: скрипт, выполняющийся после резервного копирования. Остальное аналогично параметру prescript.
include: что включать в резервную копию. Подробности см. в описании типов резервного копирования.
exclude: что исключать из резервной копии. Подробности см. в описании типов резервного копирования.
Внимание! Все пути должны заканчиваться '/'.

Типы резервного копирования

В каждом задании в параметре 'type' указывается тип резервной копии. Этот параметр влияет на схему рез. копирования и на функцию некоторых параметров.
Всего в py4backup есть 7 типов резервного копирования:
  • file-full
  • file-diff
  • btrfs-full
  • btrfs-diff
  • btrfs-snap
  • lvm-full
  • lvm-diff


Рассмотрим их поближе.

file-full

Создает резервную копию используя rsync. Создается резервная копия папки, указанной в sopath включая все папки, примонтированные глубже.
Особенности:
В переменной sopath и dpath можно указывать не только локальные папки, но и удаленные хосты. Например:
sopath = root@192.168.0.1:/home/admin/ или dpath = server:/home/admin. Во втором случае в файле ~/.ssh/config должна быть корректная запись. Используется авторизация по ключам (доп. инфо. см. в wiki вашего дистрибутива).
Нельзя указывать sopath и dpath удаленными хостами одновременно.
Значение указанное в include и exclude передаются rsync в виде опций --include= и --exclude=. Можно указывать несколько значений через пробел.

file-diff

Создает дифференциальную резервную копию от источника (sopath) и последней полной копией, найденной в папке назначения (dpath). Если полная копия не будет найдена выполнение задания завершиться ошибкой.
Список параметров аналогичен типу 'file-full'.

btrfs-full

Данный тип аналогичен типу 'file-full', но перед созданием резервной копии делается снапшот резервируемой директории и копия снимается уже со снапшота.
Для этого типа резервного копирования необходимо указание параметра snpath. В папке, указанной в snpath будет создан временный снапшот исходной папки (sopath). Причем указанный там путь должен находится на одной файловой системе с папкой, указанной в sopath. Обратите внимание, что копируется только содержимое данного subvolume файловой системы. Все примонтированные папки и вложенные subvolume будут проигнорированы. Список остальных параметров аналогичен типу 'file-full'.

btrfs-diff

При этом типе рез. копирования сначала с исходной папки (sopath) снимается снапшот, а затем создается дифференциальная копия от снапшота и последней полной копией, найденной в папке назначения (dpath). Если полная копия не будет найдена выполнение задания завершиться ошибкой.
Так же, как и для типа 'btrfs-full' необходимо, что бы папка для снапшота (snpath) находилась на одной файловой системе с исходной папкой (sopath).
Обратите внимание, что копируется только содержимое данного subvolume файловой системы. Все примонтированные папки и вложенные subvolume будут проигнорированы. Список остальных параметров аналогичен типу 'file-full'

btrfs-snap

Данный тип создает снапшоты от исходной папки, указанной в sopath в папку снапшотов, указанную в snpath.
Для данного типа не работают параметры exclude, include, dpath. Так же, как и для типа 'btrfs-full' необходимо, что бы папка для снапшотов (snpath) находилась на одной файловой системе с исходной папкой (sopath).
Обратите внимание, что копируется только содержимое данного subvolume файловой системы. Все примонтированные папки и вложенные subvolume будут проигнорированы.

lvm-full

Этот тип предназначен для создания полных копий LVM томов. Рассмотрим некоторые особенности данного типа. В параметре sopath указывается путь до Logical Volume Group (VG). Например:
sopath = /dev/main_vg/
По умолчанию скрипт сделает копию всех томов, находящихся в данном VG.
Параметр dpath указывает где сохранять резервную копию. Указывать удаленные хосты в качестве назначения резервной копии нельзя. Для того, что бы сделать копии только нужных томов можно использовать параметры include и exclude.
Параметр exclude указывает какие тома исключить из резервной копии. Кроме того он принимает кодовое слово all, обозначающее, что надо исключить все тома.
Параметр include указывает какие тома нужно включить в резервную копию. Имеет приоритет над exclude. Например:
exclude = all
include = mail root

сделает резервную копию только томов mail и root. А следующий пример сделает копию всех томов, кроме тома mail:
exclude = mail


lvm-diff

И последний (для версии 1.5) тип резервного копирования предназначен для создания дифференциальных копий LVM томов.
Скрипт ищет в папке назначения (dpath) последнюю полную резервную копию и если находит, создает дифференциальную копию между ней и снапшотом текущего состояния. В папке назначения при этом появятся 2 файла *-diff.dd и *-diff.ddm Они ОБА необходимы для восстановления.
Все параметры аналогичны типу lvm-full

Запуск

Запустить требуемые задания на выполнение очень просто.
Необходимо указать ключ –jobs (или -j) и после него указать имена необходимых заданий. Например:
py4backup --jobs backup_data backup_home backup_media
Все указанные задания выполнятся последовательно в порядке их следования в параметре --jobs. Также запуск скрипта возможен через cron, но помните, что переменное окружен cron может отличатся от пользовательского и может потребоваться указать пути до утилит rm, dd, rsync, btrfs, lvcreate, lvremove в переменной pathenv в конфигурационном файле.

Восстановление


Вот мы и подошли к самому интересному. Резервное копирование само по себе ничего не стоит, без возможности быстро восстановить резервную копию. В данном разделе я опишу типичные кейсы восстановления из резервных копий, созданных скриптом.

Файловые бэкапы

Написанное ниже относится как к полным, так и дифференциальным резервным копиям, сделанным заданиями типа btrfs-full, btrfs-diff, file-full, file-diff. Восстанавливать резервную копию необходимо rsync с ключами -aAX. Например:
rsync -aAX /mnt/backup/home/2014-06-21-full/ /home/

или
rsync -aAX /mnt/backup/home/2014-06-22-diff/ /home/

В обоих случаях в папке назначения вы получите полную копию данных, готовую к использованию.

Восстановление снапшотов

Снапшоты, создаваемые типом btrfs-snap можно восстанавливать несколькими способами.
  • Как файловый бэкап, скопировав данные rsync (слишком долго и не интересно)
  • Примонтировав снапшот, вместо папки, которую необходимо восстановить. Этот способ мы и рассмотрим ниже.

По умолчанию снапшоты создаются в режиме только для чтения. Соответственно вы не сможете напрямую писать в этот снапшот. Рассмотрим пример.
BTRFS используется в качестве корневой файловой системы. С помощь скрипта создаются снапшоты папки /home и складываются в /snapshots_home. И вот настал день, когда нам необходимо восстановить папку /home из снапшота.
Первым делом необходимо освободить папку /home (переименовать или удалить ее).
Далее мы выбираем нужный нам снапшот (пусть это будет снапшот, за 2014-06-19) и создаем снапшот от него (да, да, снапшот снапшота):
btrfs subvolume snapshot /snapshots_home/2014-06-19 /home

Таким образом мы во первых сделали наши данные доступными для записи и обезопасили их. Даже когда скрипт согласно ротации удалит снапшот от 2014-06-19 наш свежесозданный снапшот будет цел.

Восстановление полных бэкапов LVM

Тут все совсем просто.
Необходимо создать новый LVM том, размера равного или больше резервной копии и скопировать на него резервную копию с помощью dd.
Пример:
dd if=/backups/2014-06-19-old_volume-full of=/dev/main_vg/new_volume bs=32M


Восстановление дифференциальных бэкапов LVM

Для данного восстановления необходимо воспользоваться утилитой ddd, идущей в комплекте с py4backup.
Для восстановления ей необходимо указать опцию –restore, ключ -s с путем до файла БЕЗ РАСШИРЕНИЯ, ключ -r с указанием места восстановления (блочное устройство или файл). ddd запоминает путь до полной резервной копии, но если она была перемещена необходимо вручную указать ей новый путь. Сделать это можно с помощью ключа -f.
Пример:
В папке /backup находятся резервные копии:

root@virtserver / # ls /backup/
2014-06-18-volume-full
2014-06-19-volume-diff.dd
2014-06-19-volume-diff.ddm

И мы хотим восстановить резервную копию за 2014-06-19 на устройство /dev/main_vg/volume Для этого выполним команду:

ddd --restore -s /backup/2014-06-19-volume-diff -r /dev/main_vg/volume

Предположим полная копия была перемещена в папку /backup_old/:

ddd --restore -s /backup/2014-06-19-volume-diff -r /dev/main_vg/volume -f /backup_old/2014-06-18-volume-full

После восстановления ddd выведет список поврежденных блоков с указанием файла, где находится поврежденный блок. Запись full23 указывает на повреждение блока номер 23 в файле полной копии, а запись diff24 на повреждение блока 24 в дифференциальной копии.

Tips & Tricks


Здесь я расскажу о некоторых не очевидных момента и вариантах использования скрипта.
  • Если запустить скрипт без параметров выведется список доступных заданий.
  • ddd можно использовать отдельно от py4backup. Путь до полной копии указывается с ключом -f, путь до измененного файла указывается с ключом -s. Ключ -r указывает на место сохранения дифференциальной копии. Пример:
    
    ddd -f /backup/2014-06-18-volume-full  -s /dev/main_vg/volume_snapshot -r /backup/diff_backup_name
    
  • Если требуется проверить дифференциальную копию, созданную ddd, но не восстанавливать её можно в качестве назначения указать /dev/null


Заключение


Отказ от ответственности: автор скрипта не несет ответственности за действие или бездействие программы, повлекшее потерю или повреждение данных.
В скрипте ЕСТЬ ошибки и я буду благодарен за багрепорты (особенно с traceback’ами и консольным выводом команд).
Данный мануал актуален для версии 1.5.3.
Связаться со мной можно по email, адрес larrabee@nixdi.com или через хабр.
Исходный код на github.
Пакеты в репозитории.
Спасибо за прочтение и буду благодарен за комментарии.