Метка: cron

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

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

    Привет.

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

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

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

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

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

    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 # Размонтируем диск

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

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

    Дамп базы данных создаётся штатной утилитой 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 — …например, в этот файл

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

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

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

    gzip /home/mybigdatabase.sql

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

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

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

    Всё.

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

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

    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!

    Заключение

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

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

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

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