Резервное копирование и репликация c помощью rsync c возобновлением

Ранее я писал о инкрементальном бэкапе на удаленный ftp сервер.

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

Способ подходит для небольших сайтов, но если сайт больше 30 — 50 GB — уже начинаются проблемы. связанные с передачей полного бэкапа.

При синхронизации через rsync — имеется масса преимуществ:

1. Один раз передается полная копия — далее — только изменившиеся данные.

2. Резервная копия хранится распакованной — можно получить доступ к файлу сразу без необходимости скачивания всего архива (если на ftp сервере нет ssh доступа).

3. Копировать можно хоть каждый час — и иметь возможность получить доступ к копии сайта за любой момент времени.

4. На исходном сервере не требуется дополнительного места для создания архива  на бэкап-сервере дополнительное место занимается только дублированием структуры каталогов и копиями баз данных, дампы которых различны при каждом копировании. Это возможно благодаря тому, что rsync умеет в каждой новой резервной копии вместо создания дубликата файла — создавать хардлинк (ссылку файловой системы на имеющийся файл)

5. Копирование может быть запущено как на исходном (продакшен) сервере, так и на бэкап сервере. С точки зрения безопасности — желательно запускать именно с бэкап сервера.  Сервер запускающий процесс имеет доступ ко второму серверу. Злоумышленник с большей долей вероятности будет взламывать продакшен сервер, если на нем будут скрипты бэкапа — он сможет подключиться к бэкап серверу и уничтожить / модифицировать резервные копии.

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

Основные методы — это по паре логин — пароль (пароль вводится с клавиатуры в консоли) и авторизация по ключам (на сервере создается пара закрытый — открытый ключ и открытый передается на удаленный сервер). Первый способ никак не может быть использован в скриптах выполняемых по крону и необходимо настроить прозрачный вход.

Постановка задачи: У нас есть папка достаточно большого размера, которую необходимо забэкапить (все необходимые дампы БД уже сохранены). Интернет канал между серверами средней устойчивости (связь может порваться через несколько часов работы). Время копирования файлов может превышать интервал между запусками. К обоим серверам есть ssh доступ и настроена ssh авторизация по ключу с бэкап сервера на продакшен.
(если не пытаться обрабатывать разрыв связи и малый интервал запуска — скрипт можно сократить до нескольких строк)

На случай разрыва связи, чтобы использовать автовозобновление копирования при следующем запуске в файл конфигурации будем сохранять название папки, в которую была начата синхронизация.
На случай двойного запуска скрипта (например если в кроне выставлен интервал меньше времени работы) в файл конфигурации будем записывать номер процесса
Если скрипт отработал до конца — в файл конфигурации будем записывать соответствующий флаг и будем игнорировать записанные ранее номер и папку.
Файл конфигурации будет создаваться в папке скрипта автоматически и будет содержать данные только о выполнении скрипта. Настройки папок будут жестко прописаны в самом скрипте.

Ну и собственно скрипт с комментариями:


#!/bin/bash
# Дата - далее используется при формировании имен папок
date=`date "+%Y-%m-%d-%H%M%S"`
# адрес скрипта - в эту же папку будем записывать конфиг и лог
SCRIPT_PATH=$(cd $(dirname $0) && pwd);
# файл конфига
proccfg=$SCRIPT_PATH/back.cfg
# Исходная папка (на продакшене)
SRC=/home/user/www/sitename/
# папка назначения на бэкап сервере (в ней будут подпапки по датам)
DST=/home/user/back/www/sitename
# удаленный сервер (удаленным считаем продакшен если скрипт запущен на бэкапе)
SSHS=user@server.ru

echo $proccfg
# подключаем конфиг (если существует)
source $proccfg

# проверяем флаг успешного окончания процесса
if [ "$NEND" != "true" ]
then
# Если переменная не установленна, проверяем запущен ли процесс
echo "NEND var not Exists or not true"
# проверяем что установлен номер предыдущего процесса
if [ "$PROCN" != "" ]
then
echo "PROCN exists"
echo "test "$PROCN.*$0
# парсим список процессов на наличие копии с номером предыдущего запуска
if ps ax | grep -v grep | grep $PROCN.*$0
then
echo "script alredy running. exit"
#Процесс запущен и выполняется. Выходим.
exit
else
#Процесс не был корректно завершен проверяем папку для продолжения
echo "script not running"
if [ "$PROCDIR" != "" ]
then
echo "processing folder exists in config"
# папка прописана (переменная со старым адресом уже существует)
else
echo "processing folder not exists in config, try normal start"
# Переменной нет - стандартный старт. Устанавливаем название новой папки
PROCDIR=Processing-$date
fi
fi
else
# Номера нет стандартный старт (скорее всего и конфиг нет)
echo "process number not exists in config, try normal start"
PROCDIR=Processing-$date
fi
else
# установлен флаг нормального окончания
echo "normal start"
PROCDIR=Processing-$date
fi

# Заносим в переменную PROCN номер текущего процесса
PROCN=$$
# Заносим номер и папку в конфиг
echo "PROCDIR=$PROCDIR" > $proccfg
echo "PROCN=$$" >> $proccfg

echo Processing-dir: $PROCDIR
echo Process-id: $PROCN
echo start rsync

# Запускаем rsync
# флаг --link-dest=../Latest указывает на папку которая будет взята для основу для сравнения
# флаг --delete указывает, что удаленных на сервере файлов не будет в новой копии
# значение флагов -cxzrl ищите в man rsync
# по окончанию переименовываем папку
# удаляем старую и создаем символическую ссылку на созданную папку
# Записываем флаг успешного окончания
# (если связь порвалась во время копирования, то переименование и запись флага не будут выполнены)
rsync -cxzrl \
--delete \
--link-dest=../Latest \
$SSHS:$SRC $DST/$PROCDIR \
&& echo "rename dir" \
&& cd $DST \
&& mv $PROCDIR $date \
&& rm -f Latest \
&& ln -s $date Latest \
&& echo "NEND=true" >> $proccfg

Для запуска в crontab -e прописываем время запуска и путь ко скрипту с перенаправлением в лог для отладки.

PS: Я использую для бэкапов firstvds. У них дополнительное место по 2р за гб в месяц. Плюс минимальный сервер — выходит ~3р.

  • BuxarNET

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

    • VladimirDolgov

      В этой версии скрипта старые не удаляются.
      То что в новых — полный список — это нормально. Они записываются в папку хардлинками (ссылкой на имеющуюся версию файла)
      Если посчитаете место занимаемое папкой со всеми бэкапами, оно будет немногим больше последнего бэкапа.

      сам уже этот скрипт немного допилил, чтобы он оставлял копии за 3 последних дня, за три недели и три месяца, остальные удалял. если нужно — могу поделиться.

      • Диана

        Добрый день. Вы бы могли таки поделиться допиленным скриптом? Немного не понимаю, как именно должно происходить удаление старых бэкапов