Утилита резервного копирования rdiff-backup

21.08.2020

Теги: LinuxPythonSSHUbuntu

Утилита резервного копирования rdiff-backup написана на Python, обладает большой гибкостью и довольно простая в использовании. Может работать как локально, так и поверх rsync/ssh, предоставляя возможность оптимально использовать каналы передачи данных в случае удалённых бэкапов. Можно выделить следующие основные возможности:

  • Инкрементное копирование. Позволяет восстановить любой файл, удаленный хоть год назад, при том что резервное копирование выполняется, скажем, два раза в день.
  • Сохранение всей информации о файлах. Восстанавливая файл из бэкапа, мы получаем его в таком виде, в каком он был на момент добавления, включая timestamp, права доступа, владельцев и т.п. При этом утилита корректно ведет себя в различных типах файловых систем.
  • Эффективное использование дискового пространства. Используя алгоритм rsync, утилита сохраняет только изменения файлов, а не все файлы целиком. БД весом в пару десятков гигабайт не будет каждый раз копироваться целиком, а только изменения.
  • Прозрачное хранение данных. Утилита хранит бэкапы в точно таком же виде, в каком файлы находились на диске, не прибегая к хитроумным форматам. Например, чтобы восстановить последний бэкап, можно просто скопировать нужные файлы обратно на место.
  • Автоматическое определение типов файловых систем позволяет утилите избавить от полного понимания всех тонкостей её настройки в зависимости от типа используемой файловой и операционной системы.

Установка утилиты

В современных Linux-дистрибутивах rdiff-backup имеется в репозиториях:

$ sudo apt install rdiff-backup

Синтаксис

$ rdiff-backup [опции] источник получатель

Локальное резервное копирование

Сперва рассмотрим самый простой способ запуска утилиты:

$ rdiff-backup /var/www/example.com/ /backup/www/example.com/

В качестве получателя необходимо указывать либо пустой, либо несуществующий каталог. Регулировать разговорчивость утилиты можно при помощи опции -v (или --verbosity). Опция может принимать значения от 0 до 9 (по умолчанию оно равно 3).

Удалённое резервное копирование

На удалённой системе должна быть установлена rdiff-backup и работать ssh-сервер. Выполним с веб-сервера example.com резервное копирование файлов на backup-сервер backup.com — из каталога /var/www/example.com в каталог /backup/www/example.com:

$ rdiff-backup -v 5 /var/www/example.com/ evgeniy@backup.com::/backup/www/example.com/

Эта команда установит соединение с сервером backup.com при помощи ssh, запустит на нем rdiff-backup в режиме сервера (опция --server) и выполнит передачу файлов.

То же самое будет работать и в обратном направлении. Выполним с backup-сервера backup.com резервное копирование файлов веб-сервера example.com — из каталога /var/www/example.com в каталог /backup/www/example.com:

$ rdiff-backup -v 5 evgeniy@example.com::/var/www/example.com/ /backup/www/example.com/

Если ssh-сервер работает на нестандартном порту, нужно использовать опцию --remote-schema, которая по умолчанию имеет значение

ssh -C %s rdiff-backup --server

Где вместо %s подставляется username@server.com. Чтобы использовать нестандартный порт, нужно изменить значение этой опции:

$ rdiff-backup -v 5 --remote-schema 'ssh -p 2222 -C %s rdiff-backup --server' \
> evgeniy@example.com::/var/www/example.com/ /backup/www/example.com/

Получение информации о результатах бэкапа

Обычно многословный вывод требуется в период отладки и написания сценариев. Для мониторинга результатов работы утилиты желательно иметь лишь краткую сводку. Для этого rdiff-backup предлагает отдельную опцию --print-statistics.

$ rdiff-backup --print-statistics /var/www/example.com/ /backup/www/example.com/
--------------[ Session statistics ]--------------
StartTime 1598082207.00 (Sat Aug 22 10:43:27 2020)
EndTime 1598082208.71 (Sat Aug 22 10:43:28 2020)
ElapsedTime 1.71 (1.71 seconds)
SourceFiles 10
SourceFileSize 8123909 (7.75 MB)
MirrorFiles 10
MirrorFileSize 8123909 (7.75 MB)
NewFiles 0
NewFileSize 0 (0 bytes)
DeletedFiles 0
DeletedFileSize 0 (0 bytes)
ChangedFiles 0
ChangedSourceSize 0 (0 bytes)
ChangedMirrorSize 0 (0 bytes)
IncrementFiles 0
IncrementFileSize 0 (0 bytes)
TotalDestinationSizeChange 0 (0 bytes)
Errors 0
--------------------------------------------------

Исключение из бэкапа файлов и каталогов

Часто в копируемых каталогах попадаются файлы, размер которых весьма немаленький, а смысла включать их в бэкап нет. Используя опцию --exclude, можно исключать отдельные файлы и каталоги.

$ rdiff-backup --exclude /var/www/example.com/cache \
> evgeniy@example.com::/var/www/example.com/ /backup/www/example.com/

Иногда проще указать, что надо включить в бэкап, чем перечислять, что из него надо исключить:

$ rdiff-backup --include /usr/local --include /var --exclude '**' \
> / evgeniy@example.com::/var/host/example.com/

Эта команда создаст резервную копию каталогов /usr/local и /var. И при этом исключит из резервной копии все остальные каталоги.

Утилита rdiff-backup позволяет использовать шаблоны: ** эквивалентно любому пути, а * — любому пути без завершающего слеша.

Восстановление из резервной копии

Восстановить локальную копию можно при помощи команды cp. Если резервая копия была создана командой

$ rdiff-backup /var/www/example.com/ /backup/www/example.com/

То восстановить ее можно при помощи команды

$ cp -a /backup/www/example.com/* /var/www/example.com/

При этом будет скопирован каталог rdiff-backup-data со служебной информацией утилиты. Так что его надо удалить:

$ rm -r /var/www/example.com/rdiff-backup-data/

Разумеется, утилита rdiff-backup умеет и сама восстанавливать файлы с помощью опции -r (или --restore-as-of). Например, восстановим утраченные файлы веб-сервера из последней версии архива (now) на backup-сервере:

$ rdiff-backup -r now evgeniy@backup.com::/backup/www/example.com/ /var/www/example.com/
$ rdiff-backup -r 10D \ # восстановить из архива, который был сделан 10 дней назад
> evgeniy@backup.com::/backup/www/example.com/ /var/www/example.com/

Посмотреть историю создания бэкапов и потом выбрать нужный можно с помощью опции -l (или --list-increments):

$ rdiff-backup -l evgeniy@backup.com::/backup/www/example.com/
Found 3 increments:
    increments.2020-08-21T15:55:57+03:00.dir   Fri Aug 21 15:55:57 2020
    increments.2020-08-21T16:55:57+03:00.dir   Fri Aug 21 16:55:57 2020 # версия для восстановления
    increments.2020-08-21T17:55:57+03:00.dir   Fri Aug 21 17:55:57 2020
Current mirror: Fri Aug 21 18:55:57 2020 # последняя версия архива
$ rdiff-backup -r 2B \ # восстановить из архива две версии назад
> evgeniy@backup.com::/backup/www/example.com/ /var/www/example.com/

Можно восстанавливать не весь архив, а отдельные каталоги или файлы. Смотрим существующие версии файла и восстанавливаем нужную:

$ rdiff-backup --list-increments \ # посмотреть существующие версии файла
> evgeniy@backup.com::/backup/www/example.com/index.php
Found 3 increments:
    index.php.2020-08-21T15:55:53+03:00.missing   Fri Aug 21 15:55:53 2020
    index.php.2020-08-21T16:55:53+03:00.diff.gz   Fri Aug 21 16:55:53 2020
    index.php.2020-08-21T17:55:53+03:00.diff.gz   Fri Aug 21 17:55:53 2020 # версия для восстановления
Current mirror: Fri Aug 21 18:55:53 2020 # последняя версия файла
$ rdiff-backup --force -r 1B \ # восстановить файл на одну версию назад
> evgeniy@backup.com::/backup/www/example.com/index.php /var/www/example.com/index.php

Если файл в приемнике уже существует, rdiff-backup откажется его заменять и предложит использовать опцию --force для принудительной перезаписи существующего файла:

Fatal Error: Restore target /var/www/example.com/index.php already exists, specify --force to overwrite.

Удаление старых файлов

Для удаления бекапов старше определенного времени используется опция --remove-older-than.

Удалим с backup-сервера бэкапы старше трех недель:

$ rdiff-backup --remove-older-than 3W evgeniy@backup.com::/backup/www/example.com/

Удалим с backup-сервера бэкапы старше одного месяца:

$ rdiff-backup --remove-older-than 1M evgeniy@backup.com::/backup/www/example.com/

Оставляем на backup-сервере только 10 последних версий:

$ rdiff-backup --remove-older-than 10B evgeniy@backup.com::/backup/www/example.com/
Для указания времени можно использовать символы s, m, h, D, W, M и Y — секунды, минуты, часы, дни, недели, месяцы и годы.

Прочие опции утилиты

Опция --list-at-time время показывает список файлов в архиве, которые существовали в указанный момент времени. Если указан путь к каталогу в архиве, будут показаны только файлы в этом каталоге.

$ rdiff-backup --list-at-time 7D /backup/www/example.com/

Опция --list-changed-since время показывает список файлов, которые были изменены с указанного момента времени. Если указан путь к каталогу в архиве, будут показаны только файлы в этом каталоге.

$ rdiff-backup --list-changed-since 1h /backup/www/example.com/

Опция --compare-at-time время позволяет сравнить текущее состояние каталога-источника с архивом на указанный момент времени. Позволяет проверить актуальность резервной копии. Сравниваются метаданные файлов — этот способ утилита использует и при резервном копировании.

Опция --compare является сокращением для --compare-at-time now, т.е. сравнивает текущее состояние каталога-источника с последней версией архива.

Опция --compare-hash-at-time время позволяет сравнить текущее состояние каталога-источника с архивом на указанный момент времени. Для каждого файла источника вычисляется контрольная сумма SHA1 и сравнивается с метаданными в архиве на указанный момент времени.

Опция --compare-hash является сокращением для --compare-hash-at-time now, т.е. сравнивает текущее состояние каталога-источника с последней версией архива.

Опция --compare-full-at-time время позволяет сравнить текущее состояние каталога-источника с архивом на указанный момент времени. Но при этом производится побайтовое сравнение файлов в источнике с файлами в архиве. Это самый медленный, но наиболее полный вариант сравнения.

Опция --compare-full является сокращением для --compare-full-at-time now, т.е. сравнивает текущее состояние каталога-источника с последней версией архива.

Опция --server обеспечивает переход в режим сервера утилиты на удаленном компьютере. Эту опцию нельзя указывать напрямую, она исключительно для того, чтобы утилита на локальном компьютере могла управлять утилитой на удаленном компьютере.

Опция --test-server позволяет проверить работу утилиты в режиме сервера на удаленном компьютере.

Скрипт резервного копирования

Допустим, у нас есть веб-сервер www.example.com и сервер для хранения резервных копий backup.example.com. Бэкап-сервер с некоторой периодичностью будет выполянять копирование файлов из каталога /var/www/example.com/ веб-сервера в локальный каталог /backup/www/example.com/.

#!/bin/bash

# ---------------------------------------------------------------
# Скрипт решает две задачи:
# 1. Выполняет резервное копирование файлов с сервера $REMOTE_SRV
#    из директории $REMOTE_DIR в локальную директорию $BACKUP_DIR 
# 2. Отправляет письмо на почту админу о результатах копирования
# ---------------------------------------------------------------

# путь к утилите бэкапа rdiff-backup на локальном сервере
BACKUP_RDF='/usr/bin/rdiff-backup'
# локальная директория, где будем хранить резервные копии
BACKUP_DIR='/backup/www/example.com/'
# сколько резервных копий хранить (0 — без ограничений)
BACKUP_CNT=0

# ip-адрес или домен удаленного сервера для ssh-соединения
REMOTE_SRV='www.example.com'
# имя пользователя на удаленном сервере для ssh-соединения
REMOTE_USR='sergey'
# дополнительные опции для ssh — порт или  путь к файлу ключа
REMOTE_SSH='-p 2222 -i /home/sergey/.ssh/id_rsa'
# путь к утилите бэкапа rdiff-backup на удаленном сервере
REMOTE_RDF='/usr/bin/rdiff-backup'
# директория на удаленном сервере, которую будем бэкапить 
REMOTE_DIR='/var/www/example.com/'

# программа для отправки почты через внешний SMTP-сервер
MAIL_SSMTP='/usr/sbin/ssmtp'
# адрес почты админа, куда будем отправлять отчет о бэкапе
MAIL_ADMIN='sergey-ivanov@yandex.ru'
# отправлять отчет всегда (1) или только в случае ошибок (0)
MAIL_ERROR=1

# директория, где размещен скрипт резервного копирования
current=$(dirname $(realpath $0))
# сохраняем результат прошлого резервного копирования
if [[ -f "$current/last-backup.stat" ]]; then
    cp "$current/last-backup.stat" "$current/prev-backup.stat"
fi

# -------------------------------------------
# Первая задача: резервное копирование файлов
# -------------------------------------------

# значение опции --remote-schema, чтобы передать утитите rdiff-backup
remote_schema="ssh $REMOTE_SSH -C %s $REMOTE_RDF --server"
# команда резервного копирования с использованием утилиты rdiff-backup
command="$BACKUP_RDF --print-statistics --remote-schema '$remote_schema' $REMOTE_USR@$REMOTE_SRV::$REMOTE_DIR $BACKUP_DIR"
# выполянем резервное копирование и сохраняем результат в файл
bash -c "$command &> $current/last-backup.stat"
# результат резервного копирования, потребуется несколько раз ниже
success=$?

# если резервное копирование прошло успешно — удаляем старые бэкапы
if [[ $success -eq 0 && $BACKUP_CNT -ne 0 ]]; then
    echo '' >> "$current/last-backup.stat"
    echo '--------------[ Remove old backups ]--------------' >> "$current/last-backup.stat"
    bash -c "$BACKUP_RDF --remove-older-than ${BACKUP_CNT}B $BACKUP_DIR &>> $current/last-backup.stat"
    echo '--------------------------------------------------' >> "$current/last-backup.stat"
fi

# если резервное копирование прошло успешно, а отправлять отчет
# нужно только в случае ошибок — то больше ничего не делаем
if [[ $success -eq 0 && $MAIL_ERROR -eq 0 ]]; then
    exit 0
fi

# ---------------------------------------
# Вторая задача: отправка отчета на почту
# ---------------------------------------

# создаем файл message.txt с заголовком письма и телом письма
if [[ $success -eq 0 ]]; then
    subject='Backup success'
else
    subject='Backup errors'
fi
echo "Subject: $subject" > "$current/mail.text"
echo '' >> "$current/mail.text"
cat "$current/last-backup.stat" >> "$current/mail.text"
# отправляем письмо администратору через внешний SMTP-сервер
bash -c "$MAIL_SSMTP $MAIL_ADMIN < $current/mail.text"
rm "$current/mail.text"

exit $success

Осталось только добавить регламентное задание, чтобы скрипт запускался в 8:00 и 20:00

# crontab -e
0 8,20 * * * /root/backup/backup.bash

Пример отчета, который будет приходить на почту

--------------[ Session statistics ]--------------
StartTime 1598277415.00 (Mon Aug 24 16:56:55 2020)
EndTime 1598277416.01 (Mon Aug 24 16:56:56 2020)
ElapsedTime 1.01 (1.01 seconds)
SourceFiles 10
SourceFileSize 8123909 (7.75 MB)
MirrorFiles 10
MirrorFileSize 8123909 (7.75 MB)
NewFiles 0
NewFileSize 0 (0 bytes)
DeletedFiles 0
DeletedFileSize 0 (0 bytes)
ChangedFiles 0
ChangedSourceSize 0 (0 bytes)
ChangedMirrorSize 0 (0 bytes)
IncrementFiles 0
IncrementFileSize 0 (0 bytes)
TotalDestinationSizeChange 0 (0 bytes)
Errors 0
--------------------------------------------------

--------------[ Remove old backups ]--------------
No increments older than Mon Aug 24 16:49:49 2020 found, exiting.
--------------------------------------------------

Поиск: Linux • Python • SSH • Ubuntu • rdiff-backup • Бэкап • Резервная копия

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.