Резервное копирование базы данных на Cron с выгрузкой в облако на примере Яндекс.Диск. Версия 1.

Этот пост был опубликован мной более года назад. Информация, описанная ниже, уже могла потерять актуальность, но всё ещё может быть полезна.

Привет.

Тут я расскажу о самом простом способе создания бекапов БД на сервере, о выгрузке их в Яндекс.Диск. Я написал скрипт, который всё это выполняет.

Он приведён поэтапно, можешь скопировать пункты 4.1-4.5, убрав оттуда заголовки, и получишь готовый скрипт. Или можешь скачать, ссылка будет в конце. А лучше прочитать пост и вникнуть в суть происходящего.

ОС на сервере — CentOS 6.7
Версия СУБД — MySQL 5.5 (да, знаю, старая)

Скрипт будет вертеться на сron, конечно же. Это чтобы само всё. Но это в конце.

1. Монтирование Я.Диска в систему.

1.1 Ставим davfs2 систему:

yum install davfs2

1.2 Создаём папку, в которую у нас будет монтироваться удалённое хранилище:

mkdir /mnt/yadisk

1.3 В файл /etc/davfs2/secrets пишем:

/mnt/yadisk полныйадресemail@yandex.ru пароль

1.4 В файле /etc/fstab указываем настройки монтирования хранилища (в одну строку, конечно):

https://webdav.yandex.ru/ /mnt/yadisk davfs user,rw,_netdev,file_mode=600,dir_mode=700 0 1

В конце файла должна быть пустая строка.

Разгадку этого заклинания можно разобрать вот тут: rus-linux.net (отлично всё расписано)

1.5 Тестируем:

mount /mnt/yadisk # монтирование
df -h /mnt/yadisk # проверка свободного места

В результате должно отобразиться свободное место на Яндекс.Диске, типа того:

[root@server ~]# mount /mnt/yadisk
[root@server ~]# df -h /mnt/yadisk
Filesystem            Size  Used Avail Use% Mounted on
https://webdav.yandex.ru/
                       10G  1,8G  8,3G  18% /mnt/yadisk

По факту мы имеем обычную с виду директорию, куда можно писать всякое.

umount /mnt/yadisk # Размонтируем диск

Три-четыре. Закончили упражнение.

2. Делаем дампы базы данных

Дамп базы данных создаётся штатной утилитой mysqldump, которая идёт в комплекте с MySQL. Подробнее о ней можно прочесть здесь: MySQL.ru. Я же покажу здесь только необходимое мне самому.

Синтаксис всей команды, например, таков:

mysqldump --host=localhost --user=root --password=1234 -q --default-character-set=utf8 mybigdatabase > /home/mybigdatabase.sql

Разберёмся с этой командой:

—user — Имя пользователя БД
—password — Его пароль
—host — Адрес базы данных (скорее всего это localhost)
—default-character-set — Кодировка текста дампа (скорее всего, это будет utf8)
-q — Вывод текста дампа непосредственно на стандартный вывод stdout без буферизации запроса. Вообще, этот ключик нам необязателен, ибо у нас будет прямая запись вывода в файл. Но без него оно нафиг не надо, а с ним пусть будет.
mybigdatabase — Имя базы данных
> — Перенаправление вывода с перезаписью
/home/mybigdatabase.sql — …например, в этот файл

Можешь поиграться, чего-нибудь подампить, если есть чего.

3. Упаковка дампа

Используем команду gzip таким простым образом:

gzip /home/mybigdatabase.sql

В этой же папке образуется файл mybigdatabase.sql.gz, который меньше исходного на порядки.

Можно использовать tar, но придётся вносить правки в скрипт. Если хочешь заморочиться — пожалуйста, но особой разницы не будет.

tar -czf mybigdatabase.tar.gz /home/mybigdatabase.sql

Всё.

4. Объединение вышеописанного

Теперь определяемся что и зачем будет делать наш скрипт. Всё очень просто:

  1. Инициализация — готовим переменные, параметры, пути, адреса;
  2. Создание дампа — собственно, бекапа БД;
  3. Упаковка дампа — для экономии места;
  4. Отправка бэкапа в облака — на случай смерти локального файла:
    1. Монтирование хранилища;
    2. Копирование файла в хранилище;
    3. Размонтирование хранилища;
  5. Завершение — выводим сообщения с результатами и итогами работы скрипта.

Дальше я буду описывать это уже самими командами.

Создаём файл db_backup.sh там, где душе угодно. Мне угодно в домашней папке рута.
Пишем в него нижеследующее.

4.1 Инициализация:

#!/bin/bash

# Данные для работы с БД
DBHOST=localhost # Адрес БД
DBUSER=root # Имя пользователя базы данных
DBPASSWD=1234 # Пароль от базы данных
DBNAME=mybigdatabase # Имя базы данных для резервного копирования
DBCHARSET=utf8 # Кодировка базы данных (utf8)

# Даты
DATE=`date +%F` # Префикс для структурирования бекапов (формат: 2017-05-22)
DATETIME=`date +%F-%H-%M-%S` # Полная текущая дата и время (формат: 2017-05-22-12-23-34)

# Локальное хранилище
LOCALDIR=/root/db_backup # Полный путь к каталогу, где будут храниться резервные копии
LOCALPATH=$LOCALDIR/$DATE # Полный путь к папке за сегодня
LOCALFILE=$LOCALPATH/$DBNAME-$DATETIME.sql # Полный путь к файлу дампа
LOCALFILEGZ=$LOCALFILE.gz # Полный путь к архиву дампа
# Путь к бекапу будет выглядеть так:
# /root/db_backup/2017-05-22/mybigdatabase-2017-01-01-12-23-34.sql.gz

# Облачное хранилище
CLOUDUSE=1 # Копировать ли в облако? Закомментировать строку, если не надо
CLOUDMNT=/mnt/yadisk # Точка монтирования облака относительно корня
CLOUDDIR=db_backup # Папка в облаке, куда будут лететь файлы (внутри папки CLOUDMNT, т.е. без / в начале)
CLOUDPATH=$CLOUDMNT/$CLOUDDIR/$DATE # Полный путь к папке текущей даты в облаке относительно корня
CLOUDFILE=$CLOUDPATH/$DBNAME-$DATETIME.sql # Полный путь к файлу дампа в облаке
CLOUDFILEGZ=$CLOUDFILE.gz # Полный путь к архиву в облаке

# Путь к бекапу на примонтированном хранилище будет выглядеть так:
# /mnt/yadisk/db_backup/2017-05-22/mybigdatabase-2017-01-01-12-23-34.sql.gz

# Начало процесса
echo "[-----------------------------[`date +%F-%H-%M-%S`]-----------------------------]"
echo "[`date +%F-%H-%M-%S`] Starting backup"

4.2 Создание дампа:

if ! [[ -d $LOCALPATH ]]; then # Если нет папки за сегодня
 mkdir $LOCALPATH 2> /dev/null # создаём её, ошибки игнорируем
fi
echo "[`date +%F-%H-%M-%S`] Generate a database dump: '$DBNAME'..."
mysqldump --user=$DBUSER --host=$DBHOST --password=$DBPASSWD -q --default-character-set=$DBCHARSET $DBNAME > $LOCALFILE
if [[ $? -gt 0 ]]; then
 # если дамп сделать не удалось (код завершения предыдущей команды больше нуля) - прерываем весь скрипт
 echo "[`date +%F-%H-%M-%S`] Dumping failed! Script aborted."
 exit 1

4.3 Упаковка дампа:

else # иначе - упаковываем его
 echo "[`date +%F-%H-%M-%S`] Dumping successfull! Packing in GZIP..."
 gzip $LOCALFILE # Упаковка
 if [[ $? -ne 0 ]]; then # Если не удалась
  echo "[`date +%F-%H-%M-%S`] GZipping failed! SQL-file will be uploaded."
  GZIP_FAILED=1 # Создаём флажок, что упаковка сорвалась
 else
  echo "[`date +%F-%H-%M-%S`] Result file: $LOCALFILEGZ"
 fi

4.4 Отправка в облака:

if [[ $CLOUDUSE -eq 1 ]]; then # Если задано копирование в облако - делаем всякое такое 
 mount | grep "$CLOUDMNT" > /dev/null # Проверяем примонтировано ли уже у нас облако (вывод не важен) 
 if [[ $? -ne 0 ]]; then # Если нет 
  mount $CLOUDMNT # значит монтируем 
 fi 
 if [[ $? -eq 0 ]]; then # если монтирование успешно - копируем туда файл 
  echo "[`date +%F-%H-%M-%S`] Cloud: successfully mounted at $CLOUDMNT" 
  echo "[`date +%F-%H-%M-%S`] Cloud: copying started => $CLOUDFILEGZ" 
  if ! [[ -d $CLOUDPATH ]]; then # Если в облаке нет папки за сегодня 
   mkdir $CLOUDPATH 2> /dev/null # создаём её, ошибки игнорируем 
  fi 
  if [[ -f $LOCALFILEGZ && GZIP_FAILED -ne 1 ]]; then # Если у нас архивирование выше не сорвалось 
   cp -R $LOCALFILEGZ $CLOUDFILEGZ # Копируем архив 
  else 
   cp -R $LOCALFILE $CLOUDFILE # Иначе - копируем большой тяжёлый дамп 
  fi 
  if [[ $? -gt 0 ]]; then # Если не скопировался - просто сообщаем 
   echo "[`date +%F-%H-%M-%S`] Cloud: copy failed." 
  else # Если скопировался - сообщаем и размонтируем 
   echo "[`date +%F-%H-%M-%S`] Cloud: file successfully uploaded!" 
   umount $CLOUDMNT # Размонтирование облака 
   if [[ $? -gt 0 ]]; then # Сообщаем результат размонтирования (если необходимо)
    echo "[`date +%F-%H-%M-%S`] Cloud: umount - failed!" 
   fi # Конец проверки успешного РАЗмонтирования 
  fi # Конец проверки успешного копирования 
 else # если монтирование НЕуспешно - сообщаем 
  echo "[`date +%F-%H-%M-%S`] Cloud: failed to mount cloud at $CLOUDMNT" 
 fi # Конец проверки успешного монтирования 
fi # Конец проверки необходимости выгрузки в облако

4.5 Завершение

fi # Конец проверки успешного выполнения mysqldump
echo "[`date +%F-%H-%M-%S`] Stat datadir space (USED): `du -h $LOCALPATH | tail -n1`" # вывод размера папки с бэкапами за текущий день
echo "[`date +%F-%H-%M-%S`] Free HDD space: `df -h /home|tail -n1|awk '{print $4}'`" # вывод свободного места на локальном диске
echo "[`date +%F-%H-%M-%S`] All operations completed!"
exit 0 # Успешное завершение скрипта

5. Устанавливаем задачу в cron

Из под root запускаем crontab -e. Подробности по работе с crontab можно найти здесь: Codenet.ru | Ubuntu.ru

В окне запустившегося редактора пишем нижеследующее:

# в качестве командного интерпретатора использовать /bin/sh
SHELL=/bin/sh
# результаты работы отправлять по этому адресу
MAILTO=example@site.com
# Запуск скрипта дампа боевой БД и запись в лог
0 8,20 * * * bash ~/db_backup.sh >> ~/db_backup/db_backup.log

Важна последняя команда:

  • 0 — в ноль минут
    8,20 — в 8 часов утра и 20 часов вечера (без пробела)
    * — каждый день
    — каждой недели
    * — каждого месяца;
  • запускается интерпретатор bash;
  • который получает путь к скрипту ~/db_backup.sh (а равно /root/db_backup.sh, если всё происходит под рутом);
  • выполняет его;
  • а вывод дописывается (>>) в файл лога ~/db_backup/db_backup.log

Сразу разъясню простой момент с угловыми скобками:

  • > (равносильно 1> ) — перенаправление всего вывода в указанное место с перезаписью. Существующий файл перезаписывается полностью, отсутствующий — создаётся.
  • 2> — перенаправление вывода ошибок stderr в указанное место.
    Если отправить в /dev/null, то сообщений об ошибках в выводе на экране не окажется.
  • >> — перенаправление вывода без перезаписи.
    Если файла нет — он создаётся, если файл есть — результат дописывается в его конец. Подробнее о перенаправлениях выводов можно прочесть здесь: OpenNET.ru. Там очень богатый функционал и синтаксис.

Пример конечного лога

[-----------------------------[2017-01-01-17-40-50]-----------------------------]
[2017-01-01-17-40-50] Run the backup script...
[2017-01-01-17-40-50] Generate a database backup: 'mybigdatabase'...
[2017-01-01-17-44-17] Dumping successfull! Packing in GZIP...
[2017-01-01-17-47-36] Result file: /root/db_backup/2017-01-01/mybigdatabase-2017-01-01-17-40-50.sql.gz
[2017-01-01-17-47-38] Cloud: successfully mounted at /mnt/yadisk.
[2017-01-01-17-47-38] Cloud: copying started => /mnt/yadisk/db_backup/2017-01-01/mybigdatabase-2017-01-01-17-40-50.sql.gz
[2017-01-01-17-48-58] Cloud: file successfully uploaded!
[2017-01-01-17-48-58] Stat datadir space (USED): 666M   /root/db_backup/2017-01-01
[2017-01-01-17-48-58] Free HDD space: 100500G
[2017-01-01-17-48-58] All operations completed!

Заключение

На этом пока всё.

В планах сделать лимиты на объём папок с бекапами. При превышении заданного лимита — удалять старые бекапы.

Это будет во второй версии. Когда-нибудь.

Принимаю инфу об ошибках, критику и предложения.

Скачать готовый скрипт

db_backup.sh (7 kb): Google Drive

Обрати внимание! davfs2 хранит тяжёлый кеш!

1 Комментарий

  1. День добрый, Скажите может у вас есть решения для меня?
    у меня делаются бекапы каждый день по дате
    mkdir -p /путь/&data/. туда падают .mp3 файлы с разных магазинов. Так вот как бы мне отслеживать что файлы скопировались из всех магазинов вчерашней датой?
    echo `date +%s`|awk ‘{print strftime(«%d.%m.%Y», $1-86400)}’
    этим получаем вчерашнею дату.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *