Типичная для многих админов ситуация:
- есть виртуальный/невиртуальный сервер;
- есть несколько клиентов, которых вы поддерживаете с момента создания вами сайта;
- есть проблема бэкапов сайтов ваших клиентов.
И почему бы не организовать бэкапы на Я.Диски наших клиентов? Бэкапы не только с файлами, но и с дампами баз данных? И проинструктировать их:
Т.к. мы говорим о Яндексе, мы конечно же делегировали наши домены, на нашем сервере, на Яндекс. Зачем грузить свой сервер? Пусть с почтой, по протоколу smtp, работает Яндекс. А заодно пусть Яндекс и управление NS-ами на себя возьмет. Удобно, на самом деле.
Что нам нужно для организации бэкапов на разные аккаунты Я.Диска:
- установить и настроить ssmtp на ваш сервер;
- установить и настроить нужный клиент Я.Диска;
- создать и настроить bash скрипт для бэкапа;
- прописать задания в cron.
Ну и конечно у нас есть заветная почта вида root@your-site.ru созданная в Яндексе. От имени root@your-site.ru нам будут приходить письма о бэкапах.
Предложенный в статье алгоритм протестирован на сервере с Ubuntu 16.04 на борту.
Устанавливаем, настраиваем, тестируем ssmtp на нашем сервере
Если вы уже отправляете себе письмо с сервера, то пропускаем этот пункт, иначе читаем.
Т.к. пользователей у нас на сервере много, работать придется под суперпользователем root. Тем более, придется прописывать в файл пользователей БД и пароли БД, и пусть все эти данные хранятся в каталоге /root.
Мы же закрыли ssh-коннект для рута? У нас же только один, самый доверенный пользователь может выполнить su?
Выполняем в консоли:
su
cd /root
apt update && apt upgrade
apt install ssmtp
Мы перешли в каталог /root и установили ssmtp. Все дальнейшие команды также будем выполнять в этой директории.
Редактируем конфиг ssmtp:
nano /etc/ssmtp/ssmtp.conf
Приводим его к виду:
root=root@your_site.ru
mailhub=smtp.yandex.ru:465
AuthUser=root@your_site.ru
AuthPass=your_passwd
AuthMethod=LOGIN
FromLineOverride=YES
UseTLS=YES
Hostname=your_site.ru
Конфиг очевиден, комментировать нечего.
Всё! Этого достаточно для решения нашей задачи.
Создадим в /root шаблон письма о наших бэкапах, и проверим работу ssmtp
nano rsync_email_orig
Приведем его к виду:
To: your_email@gmail.com
From: root@your-site
Subject: Backup sites
Info on backup:
Отправляем себе письмо:
cp rsync_email_orig rsync_email_orig_test
echo 'Test msg' >> rsync_email_orig_test
ssmtp your_email@gmail.com < rsync_email_orig_test
rm -f rsync_email_orig_test
Проблем быть не должно, настройка элементарна.
Устанавливаем, настраиваем клиент Я.Диска
Я использую для работы клиент Я.Диска от разработчика Anton Batenev: github.com/abbat/ydcmd
Почему был выбран именно этот клиент:
- можно работать с разными аккаунтами Яндекса;
- можно синхронизировать только то, что нужно.
Для установки ydcmd выполним команды:
sudo add-apt-repository ppa:abbat/ydcmd
sudo apt update
sudo apt install ydcmd
Рассматриваемый нами клиент, есть не что иное как скрипт на python — ydcmd.py, скопированный в /usr/bin. Для работы нам нужно получить токены для Яндекс аккаунтов наших клиентов.
Описывать процесс получения токенов смысла нет. Подробности на Гитхабе: github.com/abbat/ydcmd, или в мануале Яндекса.
После получения токенов создадим разные конфиги для наших клиентов. У меня два разных токена, один общий для всех сайтов, второй для отдельного клиента, с которым заключен договор на обслуживание. Поэтому в статье речь пойдет о двух конфигах: для всех пользователей, и для пользователя vet.
Создаем конфиг по умолчанию, в /root
nano /root/.ydcmd.cfg
Приводим его к виду:
[ydcmd]
token = 1234567890
По аналогии создаём конфиг для пользователя vet, сайты этого пользователя будут бэкапиться на другой Я.Диск.
nano /root/.ydcmd_vet.cfg
Также приведем его к виду:
[ydcmd]
token = any_token
Проверим работу ydcmd для обоих конфигов:
ydcmd stat
ydcmd --config=.ydcmd_vet.cfg stat
В обоих случаях должно быть выведено что-то вроде этого:
name: disk
exif: {}
resource_id: 21239186:e9065863c345ergdfghjfgy51da3c5e06bc12345afeb14158ddcaae
created: 2012-04-04T20:00:00+00:00
modified: 2012-04-04T20:00:00+00:00
path: disk:/
comment_ids: {}
type: dir
revision: 1354646733351472
Как обычно работает:
ydcmd --help
Создадим папки для бэкапов на Я.Дисках:
ydcmd mkdir backup
ydcmd --config=.ydcmd_vet.cfg mkdir backup
На этом с подготовительной частью — всё.
Работает ssmtp, создан шаблон письма о бэкапах, настроен клиент Я.Диска для разных аккаунтов. Напоминаю, что работаем в папке /root.
Готовим bash скрипт для бэкапов
Собственно ничего нового в bash скрипте нет. Компиляция из разных источников, которые я уже не помню. Скрипт работает через день, сохраняются три последние копии. Почему так? Потому что мне так удобнее. Каждый может доработать его под себя.
#!/bin/bash
DATE=`/bin/date '+%d.%m.%Y'`
USER=$1
DIR=/var/www
DIR_SITES=public_html
EMAIL=your_email@gmail.com
TEMPLATE_EMAIL=/root/rsync_email
D=`date +%j`
# Четный или нечетный день в году. Остаток от деления на 2 в bash. Можно выставить или == 1, или == 0. Т.е. скрипт будет отрабатывать через день
if [ $((10#$D % 2)) == 1 ];then
case $USER in
vet)
FILE=user_list_vet
cnf=.ydcmd_vet.cfg
DIR_BACKUP=/root/backup_vet
;;
*)
FILE=user_list_all
cnf=.ydcmd.cfg
DIR_BACKUP=/root/backup
;;
esac
else
exit 1
fi
if ! [[ -d $DIR_BACKUP ]]; then
mkdir $DIR_BACKUP
fi
cp rsync_email_orig rsync_email
echo "$DATE" >> $TEMPLATE_EMAIL
echo "==============================================" >> $TEMPLATE_EMAIL
while read line;do
IFS=";"
set -- $line
USER=$1
SITES=$2
DB=$3
DB_USER=$4
DB_PASSWD=$5
FILE_NAME=$DIR_BACKUP/backup_"$SITES"_"$DATE".zip
cd $DIR/$USER/$DIR_SITES
mysqldump -u$DB_USER -p$DB_PASSWD $DB > $DB.sql
zip -r9 $FILE_NAME $SITES $DB.sql > /dev/null
rm ./$DB.sql
zip -T $FILE_NAME >> $TEMPLATE_EMAIL
echo "==============================================" >> $TEMPLATE_EMAIL
chown $USER:$USER $FILE_NAME
done < /root/$FILE
find $DIR_BACKUP/ -mtime +4 -exec rm -f {} \;
/usr/bin/ydcmd --config=/root/$cnf put --rsync $DIR_BACKUP/ disk:/backup >> $TEMPLATE_EMAIL
/usr/sbin/ssmtp $EMAIL < $TEMPLATE_EMAIL
exit 0
Собственно осталось прописать свои переменные здесь:
#home dir
DIR=/var/www
#sites dir
DIR_SITES=public_html
EMAIL=your_email@gmail.com
И здесь, в case:
if [ $((10#$D % 2)) == 1 ];then
case $USER in
vet)
FILE=user_list_vet
cnf=.ydcmd_vet.cfg
DIR_BACKUP=/root/backup_vet
;;
*)
FILE=user_list_all
cnf=.ydcmd.cfg
DIR_BACKUP=/root/backup
;;
esac
else
exit 1
fi
Только непонятно, что такое переменная FILE
FILE=user_list_vet
Переменная FILE не что иное как csv файл, вида:
user1;site1.ru;DB_name1;user_DB_1;passwd_DB_1
user1;site2.ru;DB_name2;user_DB_2;passwd_DB_2
user2;site3.ru;DB_name3;user_DB_3;passwd_DB_3
...
Для каждого бэкапа создаем свой csv файл с своими данными:
имя_системного_пользователя; домен; БД_для_дампа; имя_пользователя_БД; пароль_БД
Естественно имя системного пользователя совпадает с названием домашнего каталога этого пользователя. Пользователь vet — домашний каталог тоже vet.
Создадим нужные csv-файлы для наших бэкапов, отслеживая отсутствие лишних пустых строк в файле! Проверки на пустые строки в скрипте нет!
Т.е. когда создаем csv-файл — курсор, при сохранении, должен находиться в конце последней строки.
Всё готово к пробному запуску.
Еще раз всё проверяем:
- есть шаблон письма rsync_email_orig и работает ssmtp
- настроен клиент Я.Диска ydcmd для разных конфигов
- отредактированы все переменные в rsync.sh
- созданы разные csv файлы для разных бэкапов
Работаем в папке суперпользователя root. Пробуем запустить скрипт для пользователя с небольшим объемом данных, у меня это тестовый пользователь vet, в качестве параметра передаем скрипту имя пользователя:
./rsync.sh vet
Поскольку всё прозрачно и просто, то проблем быть не должно.
На ошибку:
mysqldump: [Warning] Using a password on the command line interface can be insecure.
Внимания не обращаем.
Но может быть и засада: скрипт может просто прекратить работу и ничего не сделать.
Смотрим строки:
# Четный или нечетный день в году. Остаток от деления на 2 в bash. Можно выставить или == 1, или == 0. Т.е. скрипт будет отрабатывать через день
if [ $((10#$D % 2)) == 1 ];then
И меняем 1 на 0, и опять пробуем запустить.
Что должно произойти:
- в указанной в скрипте папке для бэкапов должен быть создана архив с бэкапом вида: backup_your_site.ru_date.zip
- папка для бэкапа будет синхронизирована с Я.Диском для нужного аккаунта
- мы получим письмо вида:
Info on backup: Fri May 12 07:49:15 MSK 2017 ============================================== test of /root/backup_vet/your-site_date.zip OK ==============================================
Собственно всё. Осталось прописать задания в cron.
У меня это выглядит так:
su
crontab -e
00 01 * * * /bin/bash /root/rsync.sh all
00 03 * * * /bin/bash /root/rsynс.sh vet
Как мы видим cron отрабатывает в час ночи и в три часа ночи для разных бэкапов. Сам скрипт будет работать через день.
На этом всё, жду конструктивной критики, замечания и уточнения.
Комментарии (50)
kuraga333
14.05.2017 14:54Я прошу прощения за оффтоп, но уж не знаю, куда обратиться.
У меня при закачке на Яндекс.Диск через WebDAV много времени занимает старт (или окончание) закачки файла. То есть, даже пустой файл (каждый) закачивается около секунды. И в Linux, и в Windows…
Хуже того, аналогичное — в браузере. При закачке 300 файлов (папкой) происходит более 700 запросов к сайту, что есть уйма времени. И в Linux, и в Windows…
Поддержка пока путное не ответила. У всех так?proctoleha
14.05.2017 15:04Сочувствую. Если под разными OS лагает, то возможно проблема в провайдере интернета. Могу порекомендовать только попробовать что-либо загрузить под другим провайдером, в идеале в другой географической точке. Если проблема повторится — пинать ТП Яндекса.
kuraga333
14.05.2017 15:13Спасибо за ответ! Нет, смена МГТС на Yota ничего не дает. Правда, я еще не пробовал менять аккаунт…
AVX
14.05.2017 21:24Это может быть связано с DNS, провайдерами, и/или различными блокировками (от роскомнадзора, например).
Могу только предположить, что на каждую операцию происходит проверка сертификата сервера, или что-то подобное. А это могут быть несколько дополнительных DNS запросов. Тут только мониторить чем-нибудь, чтобы отловить где задержки.
Для проверки вот только запустил копировать 800 файлов, общим объёмом 20 МБ — половина уже скопировалось, за пару минут. Но долго получается, да. Проще всё в архив в один файл (разбивать по 4ГБ) и архивы отправлять.kuraga333
14.05.2017 21:38Это все ведь и сайта и WebDAV касается, да? Через WebDAV, кстати, еще медленнее…
AVX
15.05.2017 06:49Качал через webdav. Клиент — линуксовый dolphin.
DarkTiger
16.05.2017 01:34Мне кажется, лагает сама реализация WebDAV на ЯДиск. Ну как бы и логично — скорость файловых операций никто не обещал, обещали объем, а использовать ЯДиск для внешнего хранилища мелких файлов типа логов не есть то, для чего ЯДиск создавался. Может и специально тормозят.
Попробуйте закачать на ЯДиск пару-тройку сотен маленьких файлов (по килобайту или около того) в архиве, раскрыть архив и посмотреть, как они удаляются в Midnight Commander. У меня были СТРАШНЫЕ тормоза год назад, когда я забыл включить удаление одиночных time-lapse фоток с камеры наблюдения после сборки в видео. Один файл удалялся секунд 15. Как сейчас — не знаю.
bravosierrasierra
14.05.2017 15:19+1обязательно прикрутите контрольные суммы для содержимого бэкапов и регулярно их проверяйте: достаточно быстро получите много неожиданных эффектов и откажетесь от этого решения. Самым восхитительным эффектом у яндекс-диска является загрузка линуксовой машинки, которая не загружалась пару месяцев: вместо накатки на эту машину всех обновлений из облака происходит обратная ситуация: облако приводится к состоянию этой древней копии. Про множество внезапных неожиданностей на рабочих копиях git- и svn-репозиториев, которые размещены в яндекс-диске, тоже стоит знать. У яндекс-диска есть 2 неоспоримых преимущества: скорость заливки и дешевизна за гигабайт, однако на этом преимущества заканчиваются. Dropbox — наше всё, хоть он и тормоз.
Erelecano
14.05.2017 22:51+1WebDAV и duplicity решают эти проблемы гораздо проще.
И не нужно придумывать велосипеда с одним треугольным и одним квадратным колесом.
Ну и, да, не используйте su, используйте sudo.proctoleha
15.05.2017 06:59По поводу duplicity: потребуется клиенту архив с данными, и он, наверное будет очень рад тому, что они зашифрованы, а админа под боком нет.
По webdav: пробовал. Показалось, что работает медленнее. Возможно неправ.
По su и sudo. Вообще работать под рутом — плохая практика. Очень плохая. Но если мне всё-таки нужен именно рут, тогда я использую su и всегда помню что под рутом. При использовании sudo — я всегда помню, что я работаю от имени пользователя с повышенными привилегиями.Erelecano
15.05.2017 15:21> По поводу duplicity: потребуется клиенту архив с данными, и он, наверное будет очень рад тому, что они зашифрованы, а админа под боком нет.
man duplicity
/--no-encryption
> По webdav: пробовал. Показалось, что работает медленнее. Возможно неправ.
apt-cache search davfs2
> По su и sudo.
Пока вы админите свой локалхост вы, конечно, можете использовать su, но когда вы начнете работать, то вам придется использовать sudo. Потому лучше послушайте человека с опытом администрирования в 20 лет и привыкайте делать правильно.
Supme
15.05.2017 00:49+1Удаление старых бэкапов нужно делать не только по времени, но и по количеству оставшихся файлов, можно остаться совсем без единого.
proctoleha
15.05.2017 07:01-1А письма о бэкапах зачем я себе отправляю?
Supme
15.05.2017 10:08+2Наверное, чтобы их читать, но к этому должна быть большая любовь, особенно когда их штук по 100 приходит.
proctoleha
15.05.2017 11:19Да… тогда ой. Но я всё-таки читаю, пока пью утренний кофе, т.е. в первую очередь. Не из любви к эпистолярному жанру, конечно, а для спокойствия душевного. Но и по сто писем мне не приходит. А если бы приходило, то складывал бы все письма о бэкапах в отдельный каталог, и все равно пробегал бы их глазами. Автоматизация рутинных действий дело хорошее, но и приглядывать надо.
Supme
15.05.2017 13:35Можно что-то типа такого использовать для удаления:
cd /mnt/backup && rm -Rf `ls -t --full-time | awk '{if (NR > 4)printf("%s ",$9);}'`
4 это количество бэкапов плюс 1proctoleha
15.05.2017 18:27Но количество файлов нам заранее неизвестно. В статье рассмотрен один тестовый пользователь с 3-мя бэкапами. Но в др. csv файле записано i пользователей, следовательно кол-во бэкапов в папке = $i * $n (кол-во бэкапов для одного пользователя).
Как вам такой алгоритм:
в цикле считаем кол-во пользователей -> если в папке для бэкапов архивов <= чем $i * $n, то ничего не делаем, иначе удаляем, тем или иным способом, старые архивы?
Supme
15.05.2017 19:49Каждый пользователь в отдельной папке, каждый бэкап в архиве, так можно.
proctoleha
16.05.2017 00:28Думать надо, хотя вроде всё и просто. У одного пользователя может быть больше одного домена. К меня так, например. Куча служебных сайтов на одном, моем пользователе. И все они в одной папке, по три копии. Раскидывать не вижу смысла.
proctoleha
16.05.2017 06:24Более того совсем не факт, что количество сайтов у отдельно взятого пользователя есть константа. Гулять может в обе стороны. Так что у себя я ничего менять не буду, и буду ориентироваться, при удалении, на время создания файлов.
Лучшее — враг хорошего
Работает? Не трогай!
negasus
Мне кажется все-таки правильнее распорядок запуска скрипта настроить в cron, а не прописывать условие в самом скрипте. А в целом наверняка кому-то будет полезно.
Даешь больше мотивов для организации бекапов!
proctoleha
Спасибо за комментарий! Был бы очень признателен, если бы вы подсказали как это сделать. Я, лично, не знаю.
AslanKurbanov
Есть команда test в консоли, она возвращает true и false:
# test $(((`date +%j` % 2))) != 0 && ls
# test $(((`date +%j` % 2))) == 0 && ls
Наверное можно прописать в крон, только 4 строки придется писать, под четные и нечетные…
proctoleha
Идея хорошая, но вот только не работает в кроне test
В консоли да, в кроне нет.
Prototik
Потому что вы пытаетесь выполнить команды шелла из обычного exec вызова. Оберните всё это в sh -c, тогда заработает.
proctoleha
Пример можно? Туплю, увы
AslanKurbanov
Если обернуть, то както так:
5 * * * * /usr/bin/sh -c "/usr/bin/test $(((`date +%j` % 2))) != 0 && touch /root/test"
proctoleha
Спасибо огромное. Буду пробовать.
proctoleha
Попробовал. В логах ошибка с выражением date +%j:
Т.е. как бы я не изощрялся после + команда обрывается.
Был бы благодарен за любые мысли по исправлению ошибки.
AslanKurbanov
Первое что приходит в голову -экранировать слэшом
AslanKurbanov
Эквивалентная запись без обратных апострофов(если обратные апострофы разрывают команду в двойных кавычках)
#test $((($(date +%j) % 2))) == 0 && ls
Одно и тоже:
echo $(ls) = echo `ls`
proctoleha
/bin/sh -c "/usr/bin/test $((($(/bin/date +%j) % 2))) == 0 && touch /var/www/alex/tessst"
тоже самое в логе: (alex) CMD (/bin/sh -c "/usr/bin/test $((($(/bin/date +)
AslanKurbanov
Другой вариант ничего не оборачивать, а использовать bash вместо sh, тогда там не нужны кавычки.
/usr/bin/bash /usr/bin/test…
proctoleha
/bin/bash — это один файл со своими командами, /usr/bin/test — совсем другой. И пробовать с помощью баша запустить test бессмысленно.
AslanKurbanov
Наверное, только у меня так:
[root@serv1 ~]# printenv SHELL //смотрим переменную окружения
/bin/bash
[root@serv1 ~]# type test //входит в состав оболочки
test is a shell builtin
Вообще в кроне можно явно указать оболочку:
SHELL=/bin/bash
AslanKurbanov
Нагуглил такой вариант без test:
# нечетные:
0 0 1-31/2 * * /path/to/script
# четные:
0 0 0-30/2 * * /path/to/script
Еще такая фишка, что если запуск скрипта .sh не от рута, то нужно прописывать имя пользователя перед командной строкой.
proctoleha
Чуть ниже в комментах я писал:
AslanKurbanov
Какая интересная задачка.
Похоже всё дело в процентах, как я уже писал, надо их экранировать:
You can not use % in the command area. They will need to be escaped and if used with command substitution like the date command you can put it in backticks. Ex. `date +\%Y-\%m-\%d`. Or use bash's command substituion $().
https://www.pantz.org/software/cron/croninfo.html
AVX
Ну, например:
вместо одной строки 00 03 * * * /bin/bash /root/rsynс.sh vet
станет несколько, по дням недели:
00 03 * * 1 /bin/bash /root/rsynс.sh vet
00 03 * * 3 /bin/bash /root/rsynс.sh vet
00 03 * * 5 /bin/bash /root/rsynс.sh vet
00 03 * * 7 /bin/bash /root/rsynс.sh vet
Пятый параметр в crontab — день недели. И не нужно никаких шаманств в самом скрипте. Пусть он выполняется всегда, если уж его запустили. Даже в плане тестирования удобнее.
Эти строки нужно сделать для каждого бэкапа (я привёл только для vet пример).
proctoleha
Отлично! Спасибо большое!
proctoleha
Только вот как бы окончательно уточнить: отсчет дней недели точно от 1?
AVX
https://ru.wikipedia.org/wiki/Cron
А чтобы ТОЧНО выяснить и ТОЧНО запомнить, можно в крон поставить команду rf -rm /
(шутка)
proctoleha
Хорошая шутка. Оценил. Но с днями недели не всё так просто. Мне надо через день. Если отталкиваться от дней недели: 1-ый, 3-ий, 5-ый, 7-ой, 1-ый, 3-ий, 5-ый? Не получается строго через день, т.к. в неделе 7 дней
G1K
Вы можете очень гибко управлять настройками запуска cron заданий
Например:
Будет выполняться каждые 2 дня. И должно решить вашу задачу.
Больше примеров например в той же wiki на которую ссылаются чуть выше.
proctoleha
Приведенная вами команда привязана к дню месяца и будет работать по чётным дням месяца. Т.е. если в месяце 31 день, то она отработает 30 и 2-го. Условие через день, как хочу я, нарушено.
ozonar
Вот так например.
proctoleha
Спасибо огромное за ссылку. Но пользователей несколько, бэкапов тоже несколько. И разносить их задания по минутам не комильфо.
Т.е для пользователя А я запускаю крон в 0 часов, для B — в 2 часа ночи, для C в 4 часа утра и т.д.
И всё это должно работать через день.
cjitkul33
А не проще вместо 4-х строчек как-то так:
Не тестил, правда, но по логике должно работать.
proctoleha
Спасибо!