В качестве высокодоступной СХД (Системы Хранения Данных) отлично подходит Яндекс Диск. Данный сервис досутпен по протоколу WebDAV, а также REST API. Его возможно использовать как репликатор, что также упрощает доставку резервных копий в места их постоянного хранения.
При выборе протокола загрузки копий на диск выбор пал на REST API. Он более прост в использовании, не требует специальной настройки сервера.
REST API возможно использовать только после регистрации приложения на OAuth сервере Яндекса. О том, как это сделать вы можете прочитать в отдельной статье Регистрация приложения на сервере OAuth.yandex.
Рассмотрим сценарий создания резервных копий приложения:
Резервное копирование баз данных будет рассмотрено на примере PostgreSQL.
В набор инструментов PostgreSQL входит pg_dump и pg_restore - первый из них позволяет создать резервную копию, второй - восстановить из неё базу данных. Всего существует 3 типа резервных копий:
Воспользуемся форматом Plain text (Формат используемый по умолчанию) и в целях экономии места заархивируем копии:
#!/bin/bash
#Папка в которой будут сохраняться резервные копии:
BACKUP_DIR=/backup/
#SQL запрос для получения списка баз данных кластера PostgreSQL:
DB_NAMES_SELECT_QUERY="select datname from pg_database where datistemplate=false and datname != 'postgres'"
#С помощью встроенной утилиты psql запрашиваем список баз данных:
DB_NAMES=$(psql -U postgres postgres -t -c "${DB_NAMES_SELECT_QUERY}");
for DB_NAME in $DB_NAMES;
do
echo "Dumping $DB_NAME"
#Выполняем резервное копирование и архивацию:
pg_dump -U postgres $DB_NAME|gzip -9 > $BACKUP_DIR/$DB_NAME-$(date +'%Y%m%d-%H%M')-db.gz
done
Разберем команду создания резервной копии pg_dump: -U postgres - выполнить запрос от имени пользователя postgres $DB_NAME - переменная в которой хранится название базы данных
И команду архивации gzip: -9 - Уровень компрессии (от 0 до 9) > $BACKUP_DIR/$DB_NAME-$(date +'%Y%m%d-%H%M')-db.gz - Полный путь сохранения сжатой копии ($BACKUP_DIR - ссылка на папку, $DB_NAME-$(date +'%Y%m%d-%H%M')-db.gz - название архива. Например, для базы с именем test-db в 04:30 1 января 2016 года получится /backup/test-db-20160101-0430-db.gz)
Значимой частью файловой системы являются все файлы так или иначе используемые приложением. Это могут быть файлы, загруженные на сервер пользователями, конфигурационные файлы, ssl сертификаты, логи и т.д.
Все значимые файлы находятся в одной директории /home/<Название приложения>. Для создания копии воспользуемся архиватором tar:
BACKUP_DIR=/backup/
#С помощью встроенной утилиты psql запрашиваем список баз данных:
DB_NAMES=$(psql -U postgres postgres -t -c "${DB_NAMES_SELECT_QUERY}");
for DB_NAME in $DB_NAMES;
do
echo "Dumping $DB_NAME"
#Выполняем архивацию значимых файлов:
tar -czvf $BACKUP_DIR/${DB_NAME}-$(date +'%Y%m%d-%H%M')-files.tar.gz -C /home/$DB_NAME/ .
done
Разберем команду архивации tar: -c, --create - создать новый архив -z, --gzip - применить сжатие gzip -v, --verbose - Показывать добавленные файлы -f, --file - Название файла архива -C, --directory - Изменить кореневую директорию внутри архива * Для удобства при настройке приложения были использованы одинаковые названия базы данных и папки, в которой будут храниться значимые файлы
Чтобы воспользоваться REST API Яндекс Диска необходимо иметь токен авторизации (Выдается приложению в результате акцепта запроса на доступ к аккаунту). В качестве примера вместо токена будет использоваться ключевое слово YOUR_TOKEN.
О том, как получить токен авторизации обратитесь к статье Регистрация приложения на сервере OAuth.yandex.
Пара слов о REST API Яндекс диска:
Загрузка файла осуществляется в два этапа: Запрос URL для загрузки, Загрузка файла на полученный URL.
Формат запроса URL для загрузки:
https://cloud-api.yandex.net/v1/disk/resources/upload ?
path=<путь, по которому следует загрузить файл>
[& overwrite=<признак перезаписи>]
[& fields=<нужные ключи ответа>]
Пример ответа:
{
"href": "https://uploader1d.dst.yandex.net:443/upload-target/...",
"method": "PUT",
"templated": false
}
Загрузка файла на полученный URL:
https://uploader1d.dst.yandex.net:443/upload-target/20240424T101447.217.utd.52csloukwvq67nab1yc84a3xw-k1d.6625
Пример ответа:
HTTP/1.1 201 Created
Content-Length: 0
Все резервные копии находятся в папке /backup/. Для каждой копии необходимо получить URL для загрузки и послать POST запрос с файлом на этот URL. Воспользуемся для этого утилитой curl:
#Каждый http запрос должен содержать токен авторизации:
YA_DISK_HEADER='Authorization: OAuth YOUR_TOKEN'
BACKUP_DIR=/backup/
#Все файлы из директории backup, за исключением папки bin, должны быть сохранены на диске:
for FILE in $(ls -d ${BACKUP_DIR}/*|grep -v "bin");
do
echo "Transfering " $FILE;
#Получаем базовое имя файла:
FILE_BASENAME=$(ls $FILE|xargs -n1 basename)
#Формируем URL для загрузки:
YA_DISK_REQUEST_URL='https://cloud-api.yandex.net:443/v1/disk/resources/upload?path=app:/'${FILE_BASENAME}'&overwrite=false'
#Выполняем запрос URL
REST_OUTPUT=$(curl -s -H "${YA_DISK_HEADER}" ${YA_DISK_REQUEST_URL});
echo "YA REST_OUTPUT: ${REST_OUTPUT}"
#Формируем URL для загрузки:
UPLOAD_URL=$(echo $REST_OUTPUT| sed 's/.*"href":"\|",".*//g');
echo "UPLOAD_URL: $UPLOAD_URL"
#Выполняем загрузку файла:
curl -s -T $FILE -H "{YA_DISK_HEADER}" ${UPLOAD_URL};
done
Разберем команду загрузки файла curl: -s, --silent - Не отображать прогресс и другие сообщения -T, --upload-file - Передает указанный файл на сервер. В случае http запроса будет использован PUT запрос -H, --header - Заголовок который необходимо включить в запрос http при отправке на сервер
Хранить на сервере все резервные копии достаточно накладно и на практике неоправданно. В целях экономии места на диске лучше всего оставлять последние n копий.
Все резервные копии приложений хранятся внутри одной папки /backup и друг от друга отличаются только названием и датой. Чтобы правильно ротировать копии каждого из них воспользуемся командой:
ls -d /backup/* |xargs -n1 basename|sed -r "s/-[0-9]{8}.*//g"|uniq
ls -d /backup/* - отобразит список файлов, исключая директории; xargs -n1 basename - получит базовое имя файла; sed -r "s/-[0-9]{8}.*//g" - уберет из имени файла дату и расширение (Например, для app-20160227.tar.gz получится app); uniq - уберет из списка дублирующиеся строки.
Результатом выполнения данной команды будет список имен приложений, резервные копии которых храняться в папке /backup/. Таким образом, для каждого приложения, можно удалить все файлы старше n копий (В данном случае старше 3 копий):
#Получаем список приложений:
ROTATE_LIST=$(ls -d /backup/* |xargs -n1 basename|sed -r "s/-[0-9]{8}.*//g"|uniq)
for APP_NAME in $ROTATE_LIST;
do
echo "Rotating $APP_NAME"
#Удаляем все копии приложения старше 3:
ls /backup/${APP_NAME}* |sort -r|tail -n +3 |xargs rm -v
done
Разберем команду ротации: ls /home/core/backup/${APP_NAME}* - отобразит все файлы, название которых начинается с названия приложения sort -r - отсортирует список по релевантности tail -n +3 - уберет из списка первые три файла xargs rm -v - удалит все оставшиеся в списке файлы
Полный сценарий достаточно компактен и в то же время выполняет все базовые требования к созданию резервных копий:
#!/bin/bash
YA_DISK_HEADER='Authorization: OAuth YOUR_TOKEN'
BACKUP_DIR=/backup/
DB_NAMES=$(psql -U postgres postgres -t -c "select datname from pg_database where datistemplate=false and datname != 'postgres'");
for DB_NAME in $DB_NAMES;
do
echo "Dumping $DB_NAME"
pg_dump -U postgres $DB_NAME|gzip -9 > $BACKUP_DIR/$DB_NAME-$(date +'%Y%m%d-%H%M')-db.gz
tar -czvf $BACKUP_DIR/${DB_NAME}-$(date +'%Y%m%d-%H%M')-files.tar.gz -C /home/core/$DB_NAME/ .
done
for FILE in $(ls -d ${BACKUP_DIR}/*|grep -v "bin");
do
echo "Transfering " $FILE;
FILE_BASENAME=$(ls $FILE|xargs -n1 basename)
YA_DISK_REQUEST_URL='https://cloud-api.yandex.net:443/v1/disk/resources/upload?path=app:/'${FILE_BASENAME}'&overwrite=false'
REST_OUTPUT=$(curl -s -H "${YA_DISK_HEADER}" ${YA_DISK_REQUEST_URL});
echo "YA REST_OUTPUT: ${REST_OUTPUT}"
UPLOAD_URL=$(echo $REST_OUTPUT| sed 's/.*"href":"\|",".*//g');
echo "UPLOAD_URL: $UPLOAD_URL"
curl -s -T $FILE -H "{YA_DISK_HEADER}" ${UPLOAD_URL};
done
ROTATE_LIST=$(ls -d ${BACKUP_DIR}/*|xargs -n1 basename|sed -r "s/-[0-9]{8}.*//g"|uniq)
for APP_NAME in $ROTATE_LIST;
do
echo "Rotating $APP_NAME"
ls ${BACKUP_DIR}/${APP_NAME}* |sort -r|tail -n +3 |xargs rm -v
done