
Бэкапы — это вечная проблема: то объемы данных сумасшедшие, то вообще забываешь про них. В своем предыдущем проекте dvice.ru (он пока закрыт, так что без ссылки) я допустил досадную ошибку в самом начале его запуска. Я написал небольшой крон, который в 12 часов ночи удалял всех неактивированных пользователей, зарегистрировавшихся больше 24 часов назад. Но я ошибся в запросе и потерял данные пользователей, которые зарегистрировались до первого запуска этого запроса. Слава Богу, у меня были все данные в сессиях, поэтому я восстановил всех тех, кто логинился и ставил галку на чекбоксе — запомнить меня. Обидно и глупо, но на ошибках учатся. Поэтому перед запуском своего крайнего проекта — inwhite.ru, я сделал систему бэкапов.
Бэкапы делались и копились, старые удалялись, и все было отлично. Пока внезапно я не понял, что страдаю полной фигней. Бэкапы-то я делаю, а вот забирать — не забираю. Объемы данных, лежащих на моем VPS, не такие большие, но по почте тоже особо не наотправляешься. Покупать еще один VPS и складывать туда все по FTP/SVN/CVS/SSH не особо хочется, хотя тоже вариант интересный.
До вчерашнего дня я был озадачен мыслью: а как все же забирать эти данные? И вот вечером меня осенило.
Я являюсь счастливым пользователем сервиса Dropbox. Хоть и с бесплатным аккаунтом, но благодаря inwhite.ru у меня есть еще 2.5ГБ места сверху, т.е. не начальные 2ГБ, а уже аж целых 4.5ГБ. И еще на 500МБ место может увеличиться. А это уже нормальная площадка для создания бэкапов средних объемов данных.
На данный момент у меня файлов в архивах бэкапится на 200МБ, баз данных, которые я поставил бэкапиться ежедневно — 2МБ в день, а бэкапов базы данных inwhite.ru, которая бэкапится каждый час (от греха подальше), за неделю набирается пока что примерно на 215МБ.
Я не стал искать книжку «КАК НАСТРОИТЬ БЭКАПЫ С ВАШЕГО СЕРВЕРА ЧЕРЕЗ ДРОПБОКС ЗА 24 ЧАСА». Покопавшись в больших интернетах, я нашел статью, в которой говорится о том, как настроить и заставить работать Dropbox при отсутствии GUI. Это было самое главное, т.к. если у вас не Windows сервер, то вряд ли у вас на сервере есть X.
Статья находится по этому адресу, и я не буду приводить ее здесь, т.к. сам процесс довольно прост и описан достаточно подробно. Скажу лишь пару вещей:
- Если вы начали настраивать Dropbox, то HOST_ID зависит от папки, откуда запустили Dropbox
- Если вы из обсуждаемого в статье расположения файлов ~/.dropbox-dist перенесете файлы, например, как я, в /usr/local/dropbox (я привык там хранить программы), то вам надо будет по-новой получать HOST_ID
- dbreadconfig у меня так и не заработал, и что-то мне подсказывает, что у вас тоже просто так не заработает, только если вы не исправите этот скрипт, написанный на Python
- Следствие из п.3 — запаситесь sqlite3
- Скрипт запуска Debian/Ubuntu у меня тоже не особо пожелал работать, поэтому я вам тут его в исправленном виде выложу
adduser --home /home/dropbox --shell /bin/false --disabled-login dropbox
Обратите внимание на то, что в папке пользователя вам надо будет сделать еще одну папку — «Dropbox», т.к. именно в нее по умолчанию Dropbox будет записывать файлы.
Т.е. по сути должно получиться что-то вроде:
/home
/home/dropbox
# эту папку вам создаст сам Dropbox после первого правильного запуска
/home/dropbox/.dropbox
# а вот эту папку вам надо будет сделать самому, т.к. Dropbox этого почему-то не делает
/home/dropbox/Dropbox
Сам скрипт запуска
USERS="dropbox"<br>DAEMON=/usr/local/dropbox/dropbox<br>LAUNCH=/usr/local/dropbox/dropboxd<br><br>start() {<br> echo "Starting dropbox..."<br> for dbuser in $USERS; do<br> HOMEDIR=`getent passwd $dbuser | cut -d: -f6`<br> if [ -x $DAEMON ]; then<br> HOME="$HOMEDIR" start-stop-daemon -b -o -c $dbuser -S -u $dbuser -x $LAUNCH<br> fi<br> done<br>}<br> <br>stop() {<br> echo "Stopping dropbox..."<br> for dbuser in $USERS; do<br> if [ -x $DAEMON ]; then<br> start-stop-daemon -o -c $dbuser -K -u $dbuser -x $DAEMON<br> fi<br> done<br>}<br> <br>status() {<br> for dbuser in $USERS; do<br> HOMEDIR=`getent passwd $dbuser | cut -d: -f6`<br> USERPID=`cat $HOMEDIR/.dropbox/dropbox.pid`<br> if [ -z $USERPID ] ; then<br> echo "Dropbox for USER $dbuser: not running."<br> else<br> echo "Dropbox for USER $dbuser: running (pid $USERPID)"<br> fi<br> done<br>}<br> <br>case "$1" in<br> start)<br> start<br> ;;<br> <br> stop)<br> stop<br> ;;<br><br> restart|reload|force-reload)<br> stop<br> start<br> ;;<br> <br> status)<br> status<br> ;;<br> <br> *)<br> echo "Usage: /etc/init.d/dropbox {start|stop|reload|force-reload|restart|status}"<br> exit 1<br> <br>esac<br> <br>exit 0
Но написать статью про то, как воспользоваться какой-то там другой статьей — это мне кажется бредом, поэтому я решил поделиться не просто такого рода вариацией на тему «Как забэкапить данные с сервера без лишних телодвижений», а еще и скриптами самого процесса.
Я уже говорил, что бэкап у меня происходит с разным постоянством для разных сайтов, но напомню еще раз:
- inwhite.ru – каждый час
- все остальное — каждые сутки
Итак.
Бэкапим базы данных
#!/bin/bash<br><br>DATE=`date "+%Y-%m-%d"`<br>TIME=`date "+%H-%M"`<br><br># в Dropbox я создал специальную папку Backup, т.к.<br># у меня там хранятся еще и другие файлы и папки<br># если же вы создадите себе отдельный аккаунт на Dropbox<br># для этого, то, конечно же, вам эта папка будет не нужна<br>HOME="/home/dropbox/Dropbox/Backup"<br><br># массив таблиц, которые скрипт должен игнорировать<br># я старался максимально упростить жизнь, и сделать так, чтобы можно<br># было меньше залезать на сервер при добавлении новых баз<br># мне кажется, что проще залезть и добавить базу, которую не надо <br># бэкапить, чем каждый раз добавлять новые базы, которые надо бэкапить<br>SKIP=("information_schema" "mysql")<br><br>HOST="127.0.0.1" # хост, на котором расположена база данных<br>USER="" # укажите здесь имя пользователя для соединения с базой данных<br>PASSWORD="" # пароль этого пользователя<br><br># получаем список доступных баз данных<br>DBS="$(mysql -h$HOST -u$USER -p$PASSWORD -Bse 'show databases')"<br><br># небольшая функция, для проверки на вхождение записи в массив<br># я не буду объяснять, как она работает, если вы захотите, то сами<br># разберетесь, т.к. тут нет ничего сложного<br>in_array() {<br> haystack=("$@")<br> needle=$1<br> <br> unset haystack[0]<br> <br> for i in "${haystack[@]}"; do<br> if [ "$needle" == "$i" ]; then<br> return 1<br> fi<br> done<br> <br> return 0<br>}<br><br># HERE GOES THE MAGIC <br>for DB in $DBS; do<br> # есть ли текущая база в списке игнорируемых<br> in_array $DB "${SKIP[@]}"<br> <br> # если нет<br> if [ "$?" == 0 ]; then<br> # создаем полный путь до папки, куда мы положим бэкап<br> # это будет выглядеть примерно так:<br> # /home/dropbox/Dropbox/Backup/DB/inwhite<br> mkdir -p $HOME/DB/$DB<br><br> # делаем дамп базы данных<br> mysqldump -h$HOST -u$USER -p$PASSWORD $DB > /tmp/db-$DB-$DATE-$TIME.sql<br> # сжимаем TAR'ом<br> tar -Pcf /tmp/db-$DB-$DATE-$TIME.tar /tmp/db-$DB-$DATE-$TIME.sql<br> # сжимаем GZIP'ом. обратите внимание на то, что стоит максимальное сжатие,<br> # поэтому на больших объемах данных процесс может происхоть долго<br> gzip -c9 /tmp/db-$DB-$DATE-$TIME.tar > $HOME/DB/$DB/$DATE.tar.gz<br><br> # удаляем ненужные файлы, оставшиеся после создания бэкапа<br> rm -f /tmp/db-$DB-$DATE-$TIME.tar<br> rm -f /tmp/db-$DB-$DATE-$TIME.sql<br><br> # удаляем файлы, которым больше 7 дней<br> find $HOME/DB/$DB/* -type d -mtime +7 -exec rm -rf {} \;<br> fi<br>done<br><br>exit 0;
А теперь бэкапим файлы
По сути это два примерно одинаковых процесса с небольшими различиями в плане получения данных.
#!/bin/bash<br><br>HOME="/home/dropbox/Dropbox/Backup"<br><br>DATE=`date "+%Y-%m-%d"`<br>TIME=`date "+%H-%M"`<br><br># массив доменов, которые мы не бэкапим<br>SKIP=("test1.ru" "test2.ru" "test3.ru")<br><br># получаем список папок, в которых расположены файлы наших доменов<br># у меня файлы расположены в папке /home/apache<br># если у вас они расположены, например, в /var/www, то строка ниже<br># должна выглядеть примерно так:<br># DOMAINS=`find /var/www -maxdepth 1 -type d | sed 's/\/var\/www//' | sed 's/\///'`<br>DOMAINS=`find /home/apache -maxdepth 1 -type d | sed 's/\/home\/apache//' | sed 's/\///'`<br><br>in_array() {<br> haystack=("$@")<br> needle=$1<br> <br> unset haystack[0]<br> <br> for i in "${haystack[@]}"; do<br> if [ "$needle" == "$i" ]; then<br> return 1<br> fi<br> done<br> <br> return 0<br>}<br><br>for DOMAIN in $DOMAINS; do<br> in_array $DOMAIN "${SKIP[@]}"<br><br> if [ "$?" == 0 ]; then<br> mkdir -p /$HOME/FS/$DOMAIN<br><br> tar -Pcf /tmp/fs-$DOMAIN-$DATE-$TIME.tar /home/apache/$DOMAIN<br> gzip -c9 /tmp/fs-$DOMAIN-$DATE-$TIME.tar > /$HOME/FS/$DOMAIN/$DATE.tar.gz<br><br> # в случае с файлами мы удаляем только TAR, т.к. SQL файла<br> # у нас тут не производится<br> rm -f /tmp/fs-$DOMAIN-$DATE-$TIME.tar<br><br> find /$HOME/FS/$DOMAIN/* -type d -mtime +7 -exec rm -rf {} \;<br> fi<br>done;<br><br>exit 0;
У меня они называются database и filesystem соответственно, так что после установки их в /etc/cron.daily вам надо сделать:
chmod +x /etc/cron.daily/database
chmod +x /etc/cron.daily/filesystem
Это, конечно же, простейший вариант. Можно все сделать более «правильно» и положить эти скрипты куда-нибудь, а задачу добавить через crontab -e. Но я единоличный пользователь своего сервера, и доступа к нему нет ни у кого другого, поэтому я вправе сделать так, как мне проще и удобнее.
Обращаю также внимание на то, что пользователю, от имени которого вы будете делать бэкапы базы данных достаточно таких прав:
SELECT, SHOW DATABASES, LOCK TABLES, SHOW VIEW
Вот, в общем-то, и все, что тут можно рассказать. Спасибо за внимание, и пользуйтесь на здоровье. Надеюсь, кому-то это все же пригодится.