Резервирование данных никогда не было тривиальной задачей и решений соответственно несчетное количество. Каждый использует различные методы и реализации. Мне повезло: подвернулся большой проект, где резервирование получилось на удивление гибким. Разные данные резервировались с разной периодичностью и полнотой. Вся система резервирования разделена на несколько частей. Каждую попробую описать максимально подробно. Каждая из реализаций может понадобиться отдельно, но у меня они используются все сразу и являют собой совершенную систему резервирования. Это мне так кажется 🙂
Поехали.
Для начала попробуем просто сформулировать общие требования резервирования.
Первое и главное что бы мы имели понятную и логичную структуру расположения данных. Для этого будем все данные складывать в именные папки по дням.
Кроме того у нас будут данные поделены по периодичности: резервирование за каждый день и за каждый месяц. Соответственно в родительском каталоге у нас будет 2 папки: daily и monthly
В каждой из них было бы не плохо создавать каталоги которые означали бы дату резервирования, к примеру 2013-05-22 или 2013-05-23. Понятно, что тут хранятся данные за 22 мая 2013 года и за 23 мая 2013 года соответственно. Очень удобно, как мне кажется, не находите? А уже в них все, что нам нужно. Забегая чуточку вперёд скажу, что у меня несколько скриптов, в которых я использую одинаковые переменные.
Резервировать я планирую базы данных и просто файло скопом. Для удобства и их тоже разнесём внутри каждой папки в отдельные каталоги: DB для баз данных и sites для контента сайтов. Кроме того, у нас есть основная база, данные с которой мы будем складывать в отдельный каталог, и ещё несколько менее важных, но тем не менее резервировать их тоже будем но в общий каталог. И для этого в папке DB создадим каталог для основной базы. Пусть дальше она будет называться как maindb.
теперь немного попробуем реализовать это в виде скрипта. Сначала простая задача: скрипт для полного резервирования только базы maindb, учитывая наши требования.
#!/bin/bash ### Данные для соединения с MySQL сервером ### MUSER="root" # пользователь базы данных MPASS="mysqlrootpassword" # пароль пользователя MDB="maindb" # имя базы, которую будем резервировать ### Системные переменные ### DIR="/home/backup/daily" # Корневой каталог, куда будут резервирования DAY=$(date +"%Y-%m-%d") # переменная, которой присваиваем нынешнюю дату в формате гггг-мм-дд ### Сами команды ### mkdir -p $DIR/$DAY/DB/maindb # создаем каталог. Посмотрим на переменные понимаем, что он будет /home/backup/daily/гггг-мм-дд/DB/maindb. Ровно так, как нам и хотелось. Можно было указать и mkdir -p $DIR/$DAY/DB/$MDB - это ничего бы не изменило, переменные они такие. cd $DIR/$DAY/DB/maindb # переходим в этот каталог echo "Резервное копирование базы данных $MDB начато $(date +"%d %B %Y") года в $(date +"%H:%M:%S")" # ставим так называемый таймстамп. Грубо говоря время начала дампа. mysqldump -u$MUSER -p$MPASS $MDB > full.sql # сливаем сам дамп. Указываем переменные. Не зря мы их задавали в начале скрипта echo "Резервное копирование базы данных $MDB закончено $(date +"%d %B %Y") года в $(date +"%H:%M:%S")" # временная отметка окончания работы дампа.
сохраняем это дело в файл, к примеру, full_mysql_backup.sh
Пробуем запустить руками. У меня все прошло отлично и дамп создался. Иначе не писал бы сюда(: . Ну и ставим задание в крон. Я поставил на 22:00
0 22 * * * sh /root/full_mysql_backup.sh #Daily FULL MySQL maindb DB backup
С полным бекапом разобрались. Ничего сложного, подобных скриптов сотни и тысячи в интернете. Не удивлюсь, если абсолютно такой же уже у кого то есть.
Теперь немного усложним задачу. К примеру у нас есть таблицы, важность которых неимоверно зашкаливает. И нам каждый час необходимо делать их резервирование.
Не нарушая общей красоты будем каждый дамп с этими таблицами складывать в эту же папку и называть его в зависимости от времени по шаблону чч-мм-сс.sql. То есть 10-00-01.sql, 11-00-01.sql и т.д.
Поехали, скрипт не на много сложнее:
#!/bin/bash ### Данные для соединения с MySQL сервером ### MUSER="root" # пользователь базы данных MPASS="mysqlrootpassword" # пароль пользователя MDB="maindb" # имя базы, которую будем резервировать ### Системные переменные ### DIR="/home/backup/daily" # уже знаем DAY=$(date +"%Y-%m-%d") # уже знаем TIME=$(date +"%k-%M-%S") # установка временного штампа шаблона чч-мм-сс ### Сами команды ### mkdir -p $DIR/$DAY/DB/maindb cd $DIR/$DAY/DB/maindb echo "Резервное копирование чудо таблиц начато $(date +"%d %B %Y") года в $(date +"%H:%M:%S")" mysqldump -u$MUSER -p$MPASS $MDB --tables table1 table2 table3 table4 table5 table6 tableN > $TIME.sql # Дамп необходимых таблиц, перечисляем через пробел. С именем все понятно. echo "Резервное копирование чудо таблиц закончено $(date +"%d %B %Y") года в $(date +"%H:%M:%S")"
Назовем его, допустим, hourly_mysql_backup.sh Ничего нового. Те же переменные, что и в предыдущем скрипте. Кроме того
man mkdir -p, —parents
Make any missing parent directories for each directory argument.The mode for parent directories is set to the umask modified by ‘u+wx’. Ignore arguments corresponding to existing directories.(Thus, if a directory /a exists, then ‘mkdir /a’ is an error, but ‘mkdir -p /a’ is not.)
что означает создание родительских директорий. Если таковые уже имеются — просто идем дальше. То есть если один скрипт создал необходимый нам каталог второй не будет его перезаписывать а просто использовать. Опять таки запускаем скрипт руцями дабы проверить на наличие корявостей.
Ну и запихиваем с крон и указываем необходимые параметры. Делать только в рабочие часы в каждую первую минуту.
1 9-18 * * * sh /root/hourly_mysql_backup.sh #Hourly dump of some awesome tables
Ну и что бы домучать уже базы данных сделаем резервирование всех остальных баз, кроме нашей mainbd
Сразу скрипт:
#!/bin/bash ### Данные для соединения с MySQL сервером ### MUSER="root" MPASS="mysqlrootpassword" MYSQL="$(which mysql)" # размещение mysql MYSQLDUMP="$(which mysqldump)" # размещение mysqldump DEST="/home/backup/daily" DAY=$(date +"%Y-%m-%d") DBS="" # Список баз. Пока что пустой, дальше заполнится # Пропускаем эти базы данных. 2 системные, одна у нас уже делается. EXCLUDE="information_schema maindb performance_schema" # Получаем список баз DBS="$($MYSQL -u$MUSER -p$MPASS -Bse 'show databases')" for db in $DBS do skipdb=-1 if [ "$EXCLUDE" != "" ]; then for i in $EXCLUDE do [ "$db" == "$i" ] && skipdb=1 || : done fi if [ "$skipdb" == "-1" ] ; then echo "Резервное копирование базы данных $db начато $(date +"%d %B %Y") года в $(date +"%H:%M:%S")" $MYSQLDUMP -u $MUSER -p$MPASS $db > $DEST/$DAY/DB/$db.sql echo "Резервное копирование базы данных $db закончено $(date +"%d %B %Y") года в $(date +"%H:%M:%S")" fi done
Мускул домучали. Все у нас прекрасно резервируется и красиво сложено по папочкам и знаем сколько занимает каждый отдельный бекап.
Теперь осталось только сливать контент. Изобретать велосипед не будем, заtarим всё это дело и сложим красиво по папкам.
Я назвал этот скрипт full_sites_backup.sh и засунул его в крон
30 22 * * * sh /root/full_sites_backup.sh #Daily matter content backup
Код скрипта:
#!/bin/bash ### Переменные ### SITE1="/srv/http/site1/" SITE2="/srv/http/site2/" SITE3="/srv/http/site3/" SITE4="/srv/http/site4/" TAR="$(which tar)" DAY=$(date +"%Y-%m-%d") SITEDIR="/home/backup/daily/$DAY/sites" ### Сама процедура ### mkdir -p $SITEDIR cd $SITEDIR #site1 backup echo "Резервное копирование директории $SITE1 начато $(date +"%d %B %Y") года в $(date +"%H:%M:%S")" $TAR -czf $SITEDIR/site1.tar.gz $SITE1 echo "Резервное копирование директории $SITE1 закончено $(date +"%d %B %Y") года в $(date +"%H:%M:%S")" #site2 backup echo "Резервное копирование директории $SITE2 начато $(date +"%d %B %Y") года в $(date +"%H:%M:%S")" $TAR -czf $SITEDIR/site2.tar.gz $SITE2 echo "Резервное копирование директории $SITE2 закончено $(date +"%d %B %Y") года в $(date +"%H:%M:%S")" #site3 backup echo "Резервное копирование директории $SITE3 начато $(date +"%d %B %Y") года в $(date +"%H:%M:%S")" $TAR -czf $SITEDIR/site3.tar.gz $SITE3 --exclude=logs echo "Резервное копирование директории $SITE3 закончено $(date +"%d %B %Y") года в $(date +"%H:%M:%S")" #site4 backup echo "Резервное копирование директории $SITE4 начато $(date +"%d %B %Y") года в $(date +"%H:%M:%S")" $TAR -czf $SITEDIR/site4.tar.gz $SITE4 --exclude=tmp --exclude=temp --exclude=logs echo "Резервное копирование директории $SITE4 закончено $(date +"%d %B %Y") года в $(date +"%H:%M:%S")"
Всё это мы уже проходили, пояснения не требуются. Меняйте значения на свои, добавляйте и убирайте директории.
В итоге мы имеем хорошо организованный бекапчик который ещё и имеет очень понятную структуру хранения данных.
Для резервирования данных раз в месяц специально ничего не писал, так как скрипты будут те же абсолютно, только в cron-е нужно сменить время выполнения.
Дополнение. Что бы можно было спать спокойно очень долгое время я решил немного эти бекапы подчищать. Все, которые недельной или больше давности удалять. И место освобождается и актуальность прошлогодних данных стремится к нулю. Для этого в крон добавил ещё строку
45 23 * * * find /home/backup/daily/ -ctime +7 -type d | xargs rm -rf #Remove old data older then 7 days
в 23:45 запускается поиск, который ищет каталоги, дата создания которых больше 7 дней назад (-ctime +7 как можно было догадаться) и удаляет их (xargs rm -rf)
данные из папки /home/backup/monthly не трогаю. Они там раз в месяц делаются. Через год посмотрю, что и как. Сомневаюсь, что вообще любой из них пригодится.
И теперь завершающий штрих. Дабы не хранить все яйца в одной корзине после завершения резервирования всех данных будем отправлять их на удаленный ФТП-сервер. У меня уже есть статья, как это сделать для Windows. Но там отправляется один заранее созданный файл-архив. А у нас тут немного сложнее будет. Ну ничего, глаза боятся, а руки пишут скрипты.
Самый неприятный момент заключается в том, что ftp не может просто так взять и отправить рекурсивно всё на удаленный сервер. Благо у нас все данные структурированы и гуманно распределены по каталогам. По-этому нам придётся лишь малосто наговнокодить. Зато работает!
Методом проб и ошибок пришёл к вот такому коду:
#!/bin/bash DAY=$(date +"%Y-%m-%d") WORKDIR="/home/backup/daily/$DAY" FTP="ftp.server.com" USER="ftp_user" PASS="ftp_password" ftp -n $FTP <<EOF user $USER $PASS binary prompt cd /DB/maindb lcd $WORKDIR/DB/maindb mput *.sql cd .. lcd $WORKDIR/DB mput *.sql cd ../sites lcd $WORKDIR/sites mput *.tar.gz quit EOF
Внимание:
1) На удаленном FTP необходимо единоразово создать нужные нам каталоги что бы не создавать каждый раз при запуске скрипта и избежать ошибок. По аналогии с имеющейся структорой архивирования создадим DB и sites. В DB создадим папку maindb
2) по неизвестным причинам конструкция
cd /DB/maindb mput $WORKDIR/DB/maindb/*.sql
не сработала. Вот и пришлось писать такие извращения.
Теперь немного описания, что же тут что
DAY — В виду того, что это нынешняя дата, то рекомендую скрипт этот в крон устанавливать незадолго до окончания суток и САМОЕ ГЛАВНОЕ что бы все предыдущие скрипты успели завершиться. Нарушение целостности резервных данных — хуже не придумаешь.
binary — устанавливаем бинарный метод передачи
prompt — отключаем запросы типа «Вы ходите скопировать файл $FILE на удаленный сервер?» Раз мы написали такой скрипт то конечно хотим, что бы оно все копирывалось
cd — переход в указанный каталог на FTP сервере.
lcd — (local cd) Переход на локальном сервере в папку, откуда будем вытаскивать файлы
mput — (multiply put) массовое копирование. В нашем случае по расширению.
Ну и собственно у нас таких блоков три. По путям не сложно разобраться что куда копируется.
В итоге имеем точно такую же иерархию файлов на удаленном FTP-сервере. Каждый день файлы перезаписываются(напомню, без запроса)
По правде говоря FTP очень не безопасный метод передачи данных. Это решение является временным. Да и как говорят опыт лишним не бывает.