Бэкап 7z+Dropbox

Материал из Webko Wiki
Перейти к навигации Перейти к поиску

В последнее время особую популярность обретают различные «облачные» сервисы, в том числе резервное копирование. В интернете можно найти десяток сайтов представляющих подобные услуги, в том числе на платной основе.

Рассмотрим настройку автоматического бекапа веб-сервера с помощью архиватора 7z и сервиса Dropbox в Debian/Ubuntu Linux.

Способ 1 (не инкрементный бэкап)

Создадим непривилегированного пользователя из-под которого будет запущен клиент Dropbox.

root@localhost:~$ useradd backupus

Далее в соответсвии с официальной вики устанавливаем консольного клиента, для созданного ранее пользователя

backupus@localhost:~$ cd ~/
backupus@localhost:~$ wget -O dropbox.tar.gz http://www.dropbox.com/download/?plat=lnx.x86
backupus@localhost:~$ tar -zxof dropbox.tar.gz

Запускаем клиента

backupus@localhost:~$ ~/.dropbox-dist/dropboxd

Через несколько секунд в консоле появится ссылка для привязки данного компьютера к вашему аккаунту.

Изменяем каталог за которым следит демон Dropbox на «/backup»:

backupus@localhost:~$ cp ~/.dropbox/dropbox.db dropbox.db.backup
backupus@localhost:~$ wget http://dl.dropbox.com/u/119154/permalink/dropboxdir.py
backupus@localhost:~$ chmod +x dropboxdir.py
backupus@localhost:~$ mv ~/Dropbox /backup
backupus@localhost:~$ ./dropboxdir.py --setfolder=/backup

Добавим скрипт автозапуска /etc/init.d/dropbox

# dropbox service
DROPBOX_USERS="backupus"
DAEMON=.dropbox-dist/dropbox
start() {
   echo "Starting dropbox..."
   for dbuser in $DROPBOX_USERS; do
       HOMEDIR=`getent passwd $dbuser | cut -d: -f6`
       if [ -x $HOMEDIR/$DAEMON ]; then
           HOME="$HOMEDIR" start-stop-daemon -b -o -c $dbuser -S -u $dbuser -x $HOMEDIR/$DAEMON
       fi
   done
}
 stop() {
   echo "Stopping dropbox..."
   for dbuser in $DROPBOX_USERS; do
       HOMEDIR=`getent passwd $dbuser | cut -d: -f6`
       if [ -x $HOMEDIR/$DAEMON ]; then
           start-stop-daemon -o -c $dbuser -K -u $dbuser -x $HOMEDIR/$DAEMON
       fi
   done
}
status() {
   for dbuser in $DROPBOX_USERS; do
       dbpid=`pgrep -u $dbuser dropbox`
       if [ -z $dbpid ] ; then
           echo "dropboxd for USER $dbuser: not running."
       else
           echo "dropboxd for USER $dbuser: running (pid $dbpid)"
       fi
   done
}
case "$1" in
 start)
   start
   ;;

 stop)
   stop
   ;;

 restart|reload|force-reload)
   stop
   start
   ;;

 status)
   status
   ;;

 *)
   echo "Usage: /etc/init.d/dropbox {start|stop|reload|force-reload|restart|status}"
   exit 1
 esac
exit 0

Делаем исполняемым, разрешаем запуск при загрузке и запускаем:

root@localhost:~$ chmod +x /etc/init.d/dropbox
root@localhost:~$ update-rc.d dropbox defaults
root@localhost:~$ /etc/init.d/dropbox start

Синхронизация файлов с онлайн-хранилищем работает, перейдем непосредственно к бекапу:

#!/bin/sh
DATE=`/bin/date +%Y%m%d`
BACKUP_DIR="/backup/${DATE}"
SZ_CMD="/usr/bin/7za"
SZ_PSW="secret"
SZ_OPT="a -t7z -m0=lzma -mx=9 -mfb=64 -md=32m -ms=on -p${SZ_PSW}"
SITES="example.org site.com blog.me"
MYSQL_USR="backup"
MYSQL_PSW="secret"
MYSQL_DBS="mysql example_org site_com blog_me"
# Create Dirs
/bin/mkdir ${BACKUP_DIR}
/bin/mkdir ${BACKUP_DIR}/db
/bin/mkdir ${BACKUP_DIR}/www
# Installed packages
/usr/bin/dpkg --list | $SZ_CMD $SZ_OPT -si ${BACKUP_DIR}/dpkg.list.7z
# Configs
$SZ_CMD $SZ_OPT ${BACKUP_DIR}/etc.7z /etc/
# Crontabs
$SZ_CMD $SZ_OPT ${BACKUP_DIR}/crontabs.7z /var/spool/cron/crontabs/
# Sites
for SITE in $SITES; do
$SZ_CMD $SZ_OPT ${BACKUP_DIR}/www/${SITE}.7z /srv/www/${SITE}/
done
# MySQL
for DB in $MYSQL_DBS; do
 /usr/bin/mysqldump --opt -u $MYSQL_USR -p${MYSQL_PSW} $DB | $SZ_CMD \
 $SZ_OPT -si ${BACKUP_DIR}/db/${DB}.sql.7z
done

Итак, что делает это скрипт:

Слепок установленных пакетов Резервную копию конфигов и кронтабов

Резервную копию сайтов example.org, site.com и blog.me

Резервную копию mysql баз mysql, example_org, site_com и blog_me

Архивы в данном скрипте шифруются с помощью пароля $SZ_PSW, так как доверять никому не стоит

Способ 2 (инкрементный бэкап)

Шаг 1 — создание приложения

Заходим на страницу App Console, нажимаем кнопку «Create app», выбираем тип «Dropbox API app», выбираем пункт «Files and datastores», так как мы собираемся работать с файлами, а в следующем пункте отвечаем «Yes — My app only needs access to files it creates», это означает, что ваше приложение будет ограничено только своей отдельной подпапкой в папке App, к другим файлам у него не будет доступа. Придумываем название своему приложению и нажимаем «Create app». Перед вами предстанет целая страница настроек созданного приложения, но там не требуется ничего дополнительно настраивать. Но пока не закрывайте её.

Шаг 2 — скачивание и установка SDK

Чтобы писать приложения, которые будут работать с файлами в вашем Dropbox'е, надо зайти в раздел Core API. Там мы можем скачать нужные нам SDK, почитать документацию и пройти обучающие туры. Так как я считаю, что лучшим языком для скриптования для меня является Python, то я скачал себе его SDK и установил. Установка очень простая, всё ограничивается скачиванием, разархивированием самого SDK и установкой его с помощью команд "python setup.py install", или "pip install dropbox".

Шаг 3 — авторизация

Библиотека Core API использует OAuth v2, но Python SDK от Dropbox'а сам позаботится о том, как им пользоваться, так что вам не о чем волноваться и не придется писать всё с нуля. Настало время накодить небольшой скрипт:

# Включить Dropbox SDK
import dropbox
# Вставить настоящие app_key и app_secret со страницы созданного приложения из шага 1
app_key = 'INSERT_APP_KEY'
app_secret = 'INSERT_APP_SECRET'
flow = dropbox.client.DropboxOAuth2FlowNoRedirect(app_key, app_secret)
# На данном этапе юзер авторизуется
authorize_url = flow.start()
print '1. Перейдите по ссылке: ' + authorize_url
print '2. Нажмите "Allow"'
print '3. Скопируйте авторизационный код.'
code = raw_input("Вставьте авторизационный код сюда: ").strip()
# Если вы ввели правильный код, то дальше он отсылается на сервер и получается access_token, который нам нужен
access_token, user_id = flow.finish(code)
# Для проверки авторизации выведем информацию об аккаунте
client = dropbox.client.DropboxClient(access_token)
print 'linked account: ', client.account_info()
# А сам access_token вам надо сохранить в файл для дальнейшей работы скрипта
print 'access_token: ', access_token

Шаг 4 — создаём временную папку и файл токена

Сам скрипт backup.py у меня лежит в папке /root, в ней же есть временная папка backup и файл dropbox_token.txt. Вам тоже нужно создать их и в файл записать токен с предыдущего шага. Токен состоит из двух строк, в файле они именно так и лежат, с переносом строки.

Шаг последний — пишем скрипт бэкапа

#!/usr/bin/python
import os
import sys
import time
import string
from os.path import getsize
curDate = time.strftime("%d.%m.%Y", time.gmtime())
curDay = time.strftime("%d", time.gmtime())
backupDelay = time.time()-86400
if curDay == "01" or curDay == "15":
  backupDelay = 0
print "curDate:", curDate
# Include the Dropbox SDK libraries
from dropbox import client, rest, session
# Get your app key and secret from the Dropbox developer website
APP_KEY = 'ключ приложения'
APP_SECRET = 'секретный код приложения'
# ACCESS_TYPE should be 'dropbox' or 'app_folder' as configured for your app
ACCESS_TYPE = 'app_folder'
sess = session.DropboxSession(APP_KEY, APP_SECRET, ACCESS_TYPE)
oauth_token = 
oauth_token_secret = 
f = open("dropbox_token.txt",'r')
if f:
  oauth_token = string.strip(f.readline())
  oauth_token_secret = string.strip(f.readline())
  f.close()
print "oauth token found:", oauth_token, oauth_token_secret
if oauth_token ==  or oauth_token_secret == :
  request_token = sess.obtain_request_token()
  # Authorize the application on dropbox site
  url = sess.build_authorize_url(request_token)
  print "url:", url
  print "Please visit this website and press the 'Allow' button, then hit 'Enter' here."
  raw_input()
  # This will fail if the user didn't visit the above URL and hit 'Allow'
  access_token = sess.obtain_access_token(request_token)
  f = open("dropbox_token.txt","wb")
  f.write(access_token.key + '\n')
  f.write(access_token.secret)
  f.close()
else:
  sess.set_token(oauth_token, oauth_token_secret)
client = client.DropboxClient(sess)
print "linked account:", client.account_info()
def sync_dir(dir):
  rootdir = dir
  print "Syncing directory:", rootdir
  startTime = backupDelay
  for root, subFolders, files in os.walk(rootdir):
    for file in files:
      fname = os.path.join(root,file)
      if os.path.getmtime(fname)>startTime:
        #print root, file
        os.system("mkdir -p 'backup"+root+"'")
        os.system("cp '"+fname+"' 'backup"+fname+"'")
print "Making dump of MySQL databases..."
os.system("mysqldump --all-databases -uroot -pROOT_ПАРОЛЬ_MYSQL -r backup/backup.sql")
sync_dir("/var/www")
sync_dir("/var/spool/virtual")
sync_dir("/home/user")
backupName = 'backup_'+curDate+'.7z'
print "Creating archive with name", backupName
os.system("7z a -pПАРОЛЬ_АРХИВА "+backupName+" backup/* /etc")
f = open(backupName,'rb')
if f:
  fsize = getsize(backupName)
  uploader = client.get_chunked_uploader(f, fsize)
  print "Uploading file", fsize, "bytes..."
  while uploader.offset < fsize:
    try:
      upload = uploader.upload_chunked()
      print "."
    except rest.ErrorResponse, e:
      # perform error handling and retry logic
      print "error uploading file!"
  uploader.finish("/"+backupName)
  f.close()
  print "File uploaded successfully."
print "Deleting temp files..."
os.system("rm -r backup/*")
os.system("rm " + backupName);

Послесловие

Этот скрипт я добавил в crontab с запуском ежедневно в 4:00 утра. В скрипте есть три строки с вызовом функции sync_dir, точно так же вы сами можете настроить, какие папки вам надо бэкапить. Скрипт не удаляет файлы, которые были удалены из папки, если инкрементально разархивировать полный архив и последующие, то удалённые папки/файлы останутся. Удаление старых бэкапов в самом Dropbox я делать не стал, чищу папку сам когда вспоминаю об этом. Недавно взял себе еще пару VPS в других странах, а в скрипт добавил только префикс к названию бэкапов, таким образом все бэкапы сливаются в одну папку, но их можно отличить очень просто.