Пользователь
0,1
рейтинг
22 декабря 2013 в 10:05

Разработка → Резервное копирование веб-проектов на Яндекс.Диск без ООП и натурщиц из песочницы

PHP*
Позавчера хабраюзер vasiatka в посте №206752 поделился с хабрасообществом, а следовательно и со всем остальным миром, продуманным и весьма развитым классом для работы с Яндекс.Диском. Некоторое время назад я тоже стал использовать этот сервис для хранения там бэкапов. Хочу поделиться значительно более коротким вариантом скрипта на php, который делает архивы базы данных и файлов сайта, и заливает их по WebDAV. Возможно кому-то и он придётся по душе.

Если кто-то хочет сразу взглянуть на полный листинг — он внизу.

Описание


Скрипт, который я предлагаю вашему вниманию, довольно лаконичный. Если исключить задание параметров и вывод отчета, то останется что-то около тридцати строк. И хотя параметры и их названия говорят сами за себя, я всё же опишу их с комментариями.

С какими облаками работает этот скрипт


Так как загрузка файлов происходит по протоколу WebDAV, то скрипт должен уметь работать с любыми хранилищами такого рода.
Я протестировал только Яндекс и Mail.Ru, загрузка происходит успешно, но папку загрузки нужно создать на облаке заранее. На 2013.12.22 поддержка этого протокола у Mail.Ru нигде не афишируется, и заявлена только как работающая в тестовом режиме. Используйте с осторожностью.

Настройки


Логин и пароль, и путь на облаке

Логин указывается в полной форме, т.е. с собачкой и доменом. Пароль указывается как и любой уважающий себя пароль — целиком.
$WebDAV = [
  'login'=>'',
  'password'=>'',
  'url'=>'https://webdav.yandex.ru/backups/sites/',//для Яндекса
  'url'=>'https://webdav.cloud.mail.ru/backups/sites/',//для Mail.Ru
];
Путь к облаку и путь к целевой папке надо указывать целиком. Папку, куда вы планируете складывать свои драгоценные архивы, стоит создать самостоятельно заранее. Я проверил, и это точно, что она не сможет появиться автоматически. Если этого не сделать, загрузка всё равно будет идти. В случае Яндекса, судя по весу результата, загруженные файлы просто склеиваются в один большой. В случае Mail.Ru не появится ничего. Обязателен закрывающий слеш.

Локальный путь для создания архивов

Хотя архивацию и заливку на сервис можно сделать одной строкой, в моём скрипте прежде чем загрузить файл, требуется его где-то создать. Путь для создания архивов задаётся в этой переменной.
$backupPath

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

Список баз данных

Записывайте в массив $databases те базы, которые вы хотите архивировать. Вероятно стоит создать пользователя в БД без прав изменения и записи для такого использования.
$databases = [['login' => '', 'password' => '', 'dbname' => '']];


Список сайтов

Стандартный вид одного из членов массива $sites — списка архивируемых сайтов.
    'name' => 'site1.ru',
    'path' => '/var/www/site1.ru',
    'exclude' => []

Первый параметр — название сайта. Просто строка, которая будет приписываться к имени архива вашего сайта.
Второй — путь к файлам сайта.
Третий — список директорий, которые стоит исключить из архивации. Исключение отдельных файлов я не предусмотрел, но вы можете легко это добавить самостоятельно. Если вы создаёте архивы внутри одного из своих сайтов, то можете исключить это хранилище.
Пример:
    'exclude' => [
      $backupPath, //Исключить папку с бэкапами, если она находится внутри одного из сайтов
      '/var/www/site2.ru/temp' //Исключим что-нибудь еще
    ]


На этом конфигурация скрипта заканчивается, и он готов трудиться во благо революцииобщества… вебадмина.

Если база данных небольшая, а файлы весят не слишком много, то можно вызывать скрипт кроном хоть каждый час. Файлы с одинаковыми именами будут перезаписываться и в локальном хранилище, и на Яндекс.Диске. На следющий день будет создаваться файл с новой датой и примерно таким именем:
site1.ru.2013-12-20.zip
Если же вы хотите давать архивам уникальное имя каждый раз, то стоит заменить строку
$date = date('Y-m-d');
на
$date = date('Y-m-d-H-i-s');
и тогда дата будет с указанием времени вплоть до секунд.

Слушайте, и не говорите, что не слышали


Хочу предупредить, что этот скрипт не претендует на надёжный метод бэкапа часто меняющихся сайтов, сайтов с тяжелой базой данных, или иных взрослых и серьёзных веб-проектов. Но для сайтов-визиток — самое то.
Важное замечание: ваш логин и пароль будет виден в списке запущенных процессов во время закачки.

Собственно всё


Спасибо, что уделили внимание и прочли этот пост. Надеюсь, кому-то моя поделка облегчит жизнь, душевное спокойствие, и даже возможно спасёт в тяжелую минуту. Удачи!

UPD: Спасибо lolipop за упоминание Mail.Ru. Изменил скрипт так, чтобы он работал и с их терабайтным хранилищем. Теперь будет, чем его забить! И хотя в ходе поисков поначалу я встречал информацию, что поддержки WebDAV у них нет, после я обнаружил упоминание нужного адреса тут. Пока только в тестовом режиме. Жаль, что это выяснилось только сейчас — у меня топик уже уплыл из списка новых. И те, кто его уже прочел, наверное об этом не узнают. А в свете акции с целым терабайтом места это весьма актуально.

UPD: Спасибо vk2 за правильное замечание, что ваш пароль и логин будет виден на сервере в списке процессов.

UPD: Спасибо kosenka, указал, что обязателен закрывающий слеш в пути WebDAV. Действительно, это же папка. Упустил я, но теперь исправил и в примере, и в скрипте.
Так же подсказал, что если curl ругается на сертификат, ему надо указать ключ -k, что позволит ему подключаться к сайтам без или с неправильным сертификатом.
Еще он справедливо заметил, что zip есть далеко не на каждом хостинге. Постараюсь добавить в ближайшее время строчку для кошерного gzip.

Полный листинг


$WebDAV = [
  'login'=>'',
  'password'=>'',
  //url включает в себя и путь к папке на облаке, в которую вы хотите залить свои файлы
  //'url'=>'https://webdav.yandex.ru/backups/sites/',//Папку обязательно нужно создать руками заранее, иначе загрузит всё в один файл.
  'url'=>'https://webdav.cloud.mail.ru/backups/sites/',//Папку обязательно нужно создать руками заранее, иначе загрузки не будет вообще.
];

$backupPath = 'path to backups'; //папка, куда складывать бэкапы локально на сервере, прежде чем залить по WebDAV
$databases = [['login' => '', 'password' => '', 'dbname' => '']];
$sites = [
//конфиг обычного сайта:
  [
    'name' => 'site1.ru',
    'path' => '/var/www/site1.ru',
    'exclude' => []
  ],
//конфиг с демонстрацией возможных вариантов
  ['name' => 'site2.ru',
    'path' => '.', //Вы можете использовать 'path' => '.' чтобы архивировать сайт, внутри которого лежит данный скрипт.
    'exclude' => [ //Если требуется исключить какие-то папки, то это можно сделать сдесь. Исключение отдельных файлов я не предусмотрел, но вы можете легко это добавить самостоятельно.
      $backupPath, //Исключить папку с бэкапами, если она находится внутри одного из сайтов
      '/var/www/site2.ru/temp' //Исключим что-нибудь еще
    ]
  ],
];
// Конфигурационная часть на этом заканчивается.
/////////////////////////////////////////////////////////////////////////////////////////
//Если не прописать в дату часы и минуты, то имена файлов будут совпадать, и файл на Яндекс.Диске будет перезаписан.
//Соотв. можно делать бэкап каждый час, при этом файлы не будут излишне плодиться.
//На следующий день будет создан новый файл.
$date = date('Y-m-d');
$errors = [];
$success = [];
$files_to_send = [];

foreach ($databases as $db) {
  $filename = "$backupPath/bases/{$db['dbname']}.$date.sql.gz";
  $output = `mysqldump --user={$db['login']} --password={$db['password']} {$db['dbname']} | gzip -f > $filename`;
  if (!file_exists($filename)) {
    $errors[] = 'Dump ' . $db['dbname'] . ' failed: ' . $output;
  } else {
    $success[] = 'DB ' . $db['dbname'] . ' dumped';
    $files_to_send[] = $filename;
  }
}

foreach ($sites as $site) {
  $filename = "$backupPath/files/{$site['name']}.$date.zip";
  $exclude = '';
  if ($site['exclude']) {
    $exclude = '-x ' . implode('\* -x ', $site['exclude']) . '\*';
  }
  $cmd = "zip -r \"$filename\"  {$site['path']} $exclude";
  echo $cmd . "<br>\n";
  $output = `$cmd`;
  if (!file_exists($filename)) {
    $errors[] = 'Site backup ' . $site['name'] . ' failed: ' . $output;
  } else {
    $success[] = 'Site ' . $site['name'] . ' saved';
    $files_to_send[] = $filename;
  }
}

foreach ($errors as $e) {
  echo 'Ошибка: ' . $e . "<br>\n";
}
echo "<br>\n";

foreach ($success as $s) {
  echo 'ОК: ' . $s . "<br>\n";
}
echo "<br>\n";

echo "Следующие файлы будут загружены:<br>\n";
foreach ($files_to_send as $f) {
  echo $f . "<br>\n";
}
echo "<br>\n";

if (!empty($files_to_send)) {
  foreach ($files_to_send as $file) {
    echo shell_exec("curl --user {$WebDAV['login']}:{$WebDAV['password']} -T \"$file\" {$WebDAV['url']}") . "<br>\n";//если ругается на сертификат, можно добавить ключ -k
  }
}

К началу поста ↑
@igordata
карма
2,0
рейтинг 0,1
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (48)

  • –1
    Теги как раз для чтения.
  • –2
    каких только велосипедов не изобретут, чтобы git не изучать
    • +7
      А медиа файлы вы тоже в git предлагаете хранить?

      Исходники надо хранить в системе контроля версий. Тут я с вами полностью согласен. Но если у меня, к примеру, сайт-галлерея, то хранить изображения в git будет неправильно.
      • 0
        Для сайтов визиток ничего страшного. Если сильно напрягают нюансы, то можно тут прочитать как что оптимизировать.
        habrahabr.ru/post/173453/
      • +2
        да, а что тут ужасного?
      • 0
        Если у вас сайт-галерея, то хранить изображения в корне вместе с сайтом уже неправильно. А так, ничего криминального в использовании контроля версий на изображения нет. Это конечно большая тайна, но все же: системы контроля версий используют не только для исходного кода.
        • +4
          Залили файл на 200 мегабайт, оказался ненужным, удалили. А репозиторий разбух на эти 200 мегабайт.
          • +1
            Если бы наши дизайнеры удаляли файлы из гита и они действительно исчезали, то наверное их хватила бы кондрашка.
    • +1
      А где вы предлагаете устроить удаленный репозитарий git? К тому же бесплатно, без смс.
      • +2
        BitBucket?
        • +2
          Да, но, если я не ошибаюсь у них бесплатого места 1 гиг.

          Для исходников это дохрена.

          Для бэкапов с медиа и пользовательскими файлами, с БД, да еще и за некоторый период, пару месяцев, например, этого может быть маловато. Даже если заморочиться с каими-нибудь инкрементными бэкапами.
      • 0
        их можно делать несколько и где угодно, хотя б даже в том месте, куда делаете бэкап
    • +2
      в статье идет речь о резервном копировании веб-проектов. я вообще не понимаю, причем здесь git?
      то что должно храниться в git (исходники) в бэкап по-хорошему можно и не включать, так что это совершенно разные задачи.

      да и вообще, как вы предлагаете использовать git для бэкапов продакшн версии сайта со всеми пользовательскими данными и БД?
      • 0
        Что значит «должно»? Моисей взошел на гору, и ему там сказано было «не храни в гите ничего, кроме исходников»?

        Да, я храню в гите продакшн версию сайтов (в т.ч. крупнейший интернет-магазин, например) со всеми потрохами, включая десятки тысяч картинок, и не вижу в этом ничего постыдного.
        • 0
          нет, Моисей здесь не причем, я руководствуюсь общепринятыми практиками и здравым смыслом

          полагаю, что под картинками имеете ввиду аватары пользователей, или какие то другие пользовательские картинки (в противном случае не вижу смысла дискутировать дальше, т.к. в предыдущем комменте говорил именно о пользовательских данных)

          как же они попадают в репозиторий? у вас на продакшн сервере при каждой загрузке на сайт этих самих картинок, делается коммит и затем периодически это все пушится на удаленный репозиторий?

          должен признать, это довольно интересный способ бэкапа, хоть и извращенный. как минимум этот способ не всем подойдет, хотя бы с точки зрения экономии места на дисках (как писали выше).

          а с БД вы как поступаете? и ее в git?
          • +1
            Ну в целом примерно так и есть, с некоторыми нюансами. Не при каждой загрузке, разумеется — точно так же, по расписанию. Какой-нибудь фотохостинг я бы не стал так бэкапить, наверное, а магазин/форум/вордпресс/простосайт (т.е. 99.9% сайтов) — почему нет?

            Не вижу никакого извращения, честно. Стереотипы — не всегда хорошо.
  • +3
    После того, как у хостера опять случилась проблема, и пришлось восстанавливать бекапы 3-дневной давности, этот способ кажется очень простым и актуальным.
    Спасибо за скрипт!
    • 0
      Пожалуйста. Пользуйтесь на здоровье!
  • +1
    В свете последних событий, было бы неплохо такой же скрипт для стороджа от мейл.ру.
    • 0
      Спасибо за мысль! Обновил топик, немного изменил код, и «добавил поддержку» (нашел нужный адрес) Mail.Ru. Жаль, что не знал об этом, когда писал.
      • 0
        Ничего отправлять в mail.ru не хочется с такой строчкой в лицензионном соглашении:
        «5.10. Лицензиат, размещая на Сервисе Контент, предоставляет Лицензиару, его партнерам и Конечным пользователям (при условии получения доступа к Персональному дисковому пространству Лицензиата) на условиях безвозмездной, неисключительной лицензии право использования данного Контента в течение всего срока действия исключительного права на соответствующий Контент на территории всего мира любыми способами, включая, но не ограничиваясь, доведение до всеобщего сведения, просмотр, воспроизведение, перевод и переработку.»
        • 0
          ну так, encfs спасёт отца русской демократии

          Хотя и неудобно прикручивать шифрование поверх сервиса, но через webdav уже сносно. А если уж через webdav получится папки там создавать — так и вообще всё прекрасно.
  • 0
    Тему копирования файлов на Яндекс диск можно развить немного в другую сторону. В проектах, предоставляющих файлы для скачивания, эти самые файлы можно заливать на Яндекс диск, расшаривать и давать пользователям ссылку на файл в Яндекс диске. При этом если немного постараться, то файлы можно заливать на несколько разных аккаунтов. И теоретически места будет бесконечно много (10Гб * количество задействованных аккаунтов на Яндексе).
    Не знаю только, как к этому сам Яндекс отнесется…
    • 0
      Будет только рад посетителям, по всей видимости.
  • +11
    Не знаю, какой у вас хостинг, но если его пользователи по команде 'ps -axw' видят процессы других пользователей, то они будут рады узнать ваш логин и пароль от Яндекс.Диска.
    • 0
      Вы несомненно правы. Если кто-то из людей, имеющих доступ в консоль, вдруг узнает пароль и логин от вашего личного хранилища, и еще вдруг обнаружит там фотки вашей обнаженной жены, или не дай боже, не вашей — быть беде.
      Добавлю в пост.
      • +2
        Ну зачем этот сарказм? Я легко могу представить хостинг, в котором пользователи не имеют доступа к файлам друг друга, но имеют доступ к списку процессов (путь даже не в шелле, а через какой-нибудь system/exec в php). А в хранилище, на секундочку, вы кладете не фотографии жены, а дампы баз, в которых наверняка есть пароли на администраторский интерфейс сайта. Иронизируйте дальше.
        • +1
          Эх, если б это был сарказм…
          • +4
            Шелдонам, которые думают, что я правда подшучиваю над этой темой, уточняю:
            Эта мысль мне в голову не пришла, и я благодарен за отклик тов. vk2. Насколько я знаю людей, несмотря на то, что по-уму для таких дел надо бы заводить отдельный аккаунт на облаке, уверен, что многие эту идею проигнорируют из лени или на авось. И в таком случае действительно случайные люди, включая работников хостинга, могут получить доступ к поистине кладези персональных данных, а в случае с указанными в посте Яндексе и Mail.Ru — не только к файлам, но и ко всей переписке в почте.
            И одно дело, когда там хранится сайт-визитка, с его контентом, и так доступным всем и каждому на сайте. И совсем другое — личные файлы и почта.

            Так как я не пользуюсь почтой яндекса, и не пользуюсь его хранилищем кроме как для для пары своих сайтов, мне эта мысль в голову не пришла, и я считаю это большим упущением. Это очень опасный момент. И я сразу, как прочел сообщение vk2, включил упоминание об этом в пост в двух местах.
  • +5
    Если исключить задание параметров и вывод отчета, то останется что-то около тридцати строк

    Зашёл в пост, чтоб увидеть комментарий про бекап в 30 строк кода, а его тут нет :(
  • 0
    Спасибо за идею. Наконец руки дошли сделать бэкап, благодаря статье.
    Только на мой взгляд проще использовать скрипт на bash.
  • 0
    Вот как бы средствами Я-диска как-то версии отслеживать — цены бы не было!

    Т.е. пишем в одно и то же место один и тот же файл раз в сутки (по сути, перезаписываем), но можно восстановить его состояние на момент «неделя назад». Если это было бы в функционале Я-диска, была бы «тайм-машин из коробки», и милое решение для ненавороченных бекапов.

    Может, Яндекс однажды такое реализует?
    • 0
      При просмотре содержания директории, содержащей ежедневные копии проекта — представьте себе что Вы уже оказались в описанном интерфейсе, благодаря нажатию по такой-же описанной Вами кнопке «Восстановить состояние».

      В чем проблема?
      • 0
        На ежедневные копии проекта потратится куда больше места, чем на хранение только изменений во всем дереве папок. Скажем, типичный веб или програмистский «проект» — это сотни файлов и файликов, из которых в сутки (или как часто Вы делаете бекап?) обычно меняются, сами понимаете, штук 10-15. Если в локальной копии можно выкрутиться hardlink-ами, то в удаленной — увы.

        Не исключаю (и даже верю), что на стороне Я-диска дедубликация имеет место быть (правда, блоковая), но, во-первых, Вы заливаете бинарный архив (который, надо думать, заметно меняется, даже если 10-15 файлов из пары сотен поменяются, особенно если измененные не лежат в одном месте архива), во-вторых, дедубликация вряд ли учитывается при подсчете занятого места. Ну и время заливания 100 Мб архива, например, если в нем изменились 10 файлов общим весом в 15 кб — тоже можно учесть, мало ли по какому каналу придется работать?
        • 0
          Ну и сохраняйте только изменения в новые архивы. А при необходимости потом можно восстановить историю. Наверняка для этого есть готовые решения.
          • 0
            Вам не кажется, что Вы несколько странно отвечаете: я пишут, что было бы здорово хранить историю версий, т.к. сохранять по полной копии архива раз в сутки, чтобы сохранить, по сути, изменения, десятка файлов, не хочется, и вообще выглядит как из пушки по воробьям, а Вы мне на это отвечаете, что «Ну и сохраняйте только изменения в новые архивы». Это, поверьте, я и так могу догадаться сделать, вопрос в оптимальности такого подхода.
          • 0
            Я, собственно, к тому, что, существуй версии на Я-диске, задача отслеживания изменений падала бы на Я-систему, зато юзеру не пришлось бы ни о чем думать.
        • 0
          Ну для случаев, когда доступен только ftp или что-то подобное — да, отличный скрипт.
          На нормальном хостинге лучше конечно использовать какой-нибудь бэкапер, хотя-бы простейший BackupManager.

          Кстати спасибо за урл webdav для майла, как раз думал — а не бэкапить ли туда.

          Кстати, а если дедуплицировать на клиенте? obnam, attic и все в таком духе?
          Просто с учетом лиц соглашения майла его как основной бэкап использовать боязно — только на encfs
          А яндекс — ну что, 10 гигов не найдется на серваке…
  • 0
    Спасибо за скрипт, переделаю таки скрипты бэкапа для Box и задумаюсь о копии на Я.Диск.
    Сейчас делаю так: монтирую хранилище через DavFS и натравливаю rsync. Минусы только в том, что не везде можно использовать DavFS.
    Думал также использовать lftp, он якобы может работать с WebDAV, но природная лень не позволяет посмотреть, так ли это :)
    • 0
      Пожалуйста!
  • +1
    Чтобы не «светить» пароль рекомендую воспользоваться модулем curl который по-умолчанию включен в PHP
    Плюс нет необходимости запускать внешнюю команду — они могут быть заперщены, программа curl может не стоять на сервере, результат работы passthru нужно ещё парсить, в то время как библиотека curl даёт errordescription.
    Достаточно нескольких команд:

    curl_setopt($this->curl,CURLOPT_URL,«webdav.yandex.ru/backup/»);
    curl_setopt ( $this->curl, CURLOPT_POSTFIELDS, array(«file»=>"@/path/to/file.zip"));
    curl_setopt ( $this->curl,CURLOPT_USERPWD,«user:password»);
    • 0
      Спасибо. Займусь. Я вообще не думал, что это может иметь такие последствия. Теперь обязательно подправлю.
  • 0
    Неплохо бы доработать скрипт для получения ссылки для скачивания (в случае расшаренной папки) — тогда можно рассылать команде разработчиков письмо с темой «Архив %sitename%_YYYYMMDD доступен для скачивания по сслыке...»
    • 0
      Честно говоря, не знаю, как эту ссылку получать в рамках данного скрипта. WebDAV у всех как минимум стандартный, а API для ссылочек у всех может быть разным. Может быть вам подойдет указанный в шапке класс товарища vasiatka? Он довольно навороченый.
      • 0
        В случае с Яндекс.Диском можно использовать следующее обращение (http://api.yandex.ru/disk/doc/dg/reference/publish.xml), что касается Облака Mail.ru, на сколько я понимаю (http://habrahabr.ru/post/191392/#comment_6650952) – сегодня это невозможно.

        Прошу прощения за вид этих ссылок.
  • +1
    А с mail.ru сейчас работает? Мне отдает 405 на все, не пойму, то ли лыжи не едут…
    • +1
      выключили уже, вроде как.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.