Несколько лет назад передо мной встала задача резервного копирования конфигурационных файлов. Да не простого, а такого, чтоб в любой момент можно было бы просмотреть что и когда изменилось. Я знал о существовании csvbackup, но хотелось свой скрипт, с Subversion и без Perl'а.
Для тех, кто не знаком с Subversion, поясню. Subversion — это одна из систем управления версиями. Такие системы позволяют хранить несколько версий одного и того же файла. Это невероятно удобно. Представьте себе, что на фирме два админа — вы и ваша напарница. Понедельник. Вы первый день как вышли из отпуска, а ваша напарница ещё в субботу уехала в отпуск в заграницу, естественно, без телефона. И внезапно вы обнаруживаете, что один из сервисов, например, веб-сервер, неправильно работает. Но до вашего ухода в отпуск всё работало! Что изменилось? Имея резервную копию конфигурационных файлов в системе управления версиями вы не только сможете откатиться на рабочую версию конфигов, но и проследить все изменения, сделанные в конфигурационных файлах за период вашего отсутствия, фактически, выявить проблему.
Скрипт хотелось сделать простым, кросс-платформенным, если можно так выразится, и с минимальными зависимостями. В качестве интерпретатора был выбран sh, т.к. идёт в базовой системе FreeBSD. Для копирования файлов используется pax, потому что pax при копировании файла воссоздаёт полный путь к нему, то есть после команды
мы получим файл /var/tmp/etc/ssh/ssh_config. pax также идёт в базовой системе FreeBSD. Позднее, когда я пересел на Debian, для меня было неприятной неожиданностью, что pax надо устанавливать дополнительно.
Список файлов для резервного копирования будем задавать двумя способами — как результат работы команды find и как просто список файлов через пробел, что удобно для единичных файлов.
Переменные вынесем в файл настроек /usr/local/etc/svnconfbackup.conf:
В переменной LIST содержится список каталогов, в которых find будет искать файлы, а в переменной FILES — дополнительный список файлов для резервного копирования. Этот файл настроек можно рассматривать как полноценный sh-скрипт. Напомню, что внутри одиночных, или строгих, кавычек ' ' любой специальный символ (за исключением одинарной кавычки '), интерпретируется как простой символ. То есть, в переменной EXCLUDE_LIST звёздочки останутся звёздочками. А в переменной FILES мы в итоге получим список файлов в директории
А содержимое, заключённое между обратными кавычками ` `, будет заменено на результат выполнения команды, т.е. на её консольный вывод. Таким образом, вместо
Обратный слэш \ отменяет специальное значение символа перевода строки. Это делается на всякий случай.
Но вернёмся к скрипту. Он будет состоять из двух частей — инициализации репозитория Subversion и процедуры резервного копирования. Инициализацию необходимо выполнить вручную один раз, в самом начале. Само резервное копирование должно проходить без вмешательства пользователя.
Синтаксис команды:
Т.е.
Сначала необходимо подгрузить настройки и выполнить инициализацию локальных переменных
Дальше обработаем параметр команды, переданный в из командной строки
Процедура инициализации
Процедура резервного копирования не намного длиннее, однако хочу остановиться поподробнее на алгоритме. Чтобы скрипт был простым и понятным, я решил задачупо-индусски «в лоб».
Это, без сомнений, ресурсоёмкий алгоритм. Однако, на типичной системе его выполнение займёт всего несколько минут.
Новые файлы (п.3) можно найти легко — в выводе
Для получения имён файлов будем использовать конструкцию
Это значит, что вывод команды
Собственно, процедура резервного копирования.
Ссылка на архив svnconfbackup.googlecode.com/files/svnconfbackup.tar.gz
Использовать этот скрипт тоже просто. После распаковки архива поместите файл скрипта в
Теперь, при желании, можно первый бэкап сделать вручную.
Процесс займёт несколько минут и завершится выводом текста всех конфигурационных файлов на консоль, поскольку репозиторий ещё пуст. При дальнейшем использовании вывод будет содержать только изменения между версиями.
Настройка автоматического запуска будет несколько отличаться для Linux и FreeBSD.
Для Linux:
Для FreeBSD:
Теперь — о том, как пользоваться бэкапом. По большому счёту, ниже — очень краткое описание основных комманд svn.
Для определённости будем использовать настройки по умолчанию. Для получения последнего бэкапа выполните команду
В текущем каталоге будет создан каталог conf, в который и будет помещена копия последнего бэкапа. При восстановлении данных имейте в виду, что в каждом каталоге присутствуют служебные каталоги с именем .svn.
Для следующих операций необходимо перейти в каталог conf, который также называют рабочим каталогом svn.
Нам понадобятся всего две команды — update и diff. Обратите внимание, что команда update обновляет рабочий каталог svn, а не репозиторий. Таким образом, для отката на предыдущую версию нужно выполнить команду
Вообще, в качестве аргумента для ключа --revision или его короткой формы -r можно указать и номер ревизии и дату. Например, так:
В данном случае будет взят бэкап на 31 марта 2010 г. 00:00:00. Обратите внимание, что если у вас автоматический бэкап выполняется позже этого времени, например, в 4 часа утра, то в рабочем каталоге будет бэкап за 30 марта.
С помощью команды diff можно посмотреть изменения между двумя любыми версиями файлов. При этом можно в явном виде указать, версии каких файлов надо сравнивать. Например, команда
покажет, что изменилось с прошлого года. Кстати, ключ -r работает и с командой checkout! Подробнее о синтаксисе и ключах можно узнать из справки:
На всякий случай оставлю ссылку на сайт отличной книги по Subversion.
Желаю удачи! И помните — администраторы делятся на два типа — одни данные уже потеряли, а другие ещё потеряют!
UPD Перенёс в тематический блог
Для тех, кто не знаком с Subversion, поясню. Subversion — это одна из систем управления версиями. Такие системы позволяют хранить несколько версий одного и того же файла. Это невероятно удобно. Представьте себе, что на фирме два админа — вы и ваша напарница. Понедельник. Вы первый день как вышли из отпуска, а ваша напарница ещё в субботу уехала в отпуск в заграницу, естественно, без телефона. И внезапно вы обнаруживаете, что один из сервисов, например, веб-сервер, неправильно работает. Но до вашего ухода в отпуск всё работало! Что изменилось? Имея резервную копию конфигурационных файлов в системе управления версиями вы не только сможете откатиться на рабочую версию конфигов, но и проследить все изменения, сделанные в конфигурационных файлах за период вашего отсутствия, фактически, выявить проблему.
Скрипт хотелось сделать простым, кросс-платформенным, если можно так выразится, и с минимальными зависимостями. В качестве интерпретатора был выбран sh, т.к. идёт в базовой системе FreeBSD. Для копирования файлов используется pax, потому что pax при копировании файла воссоздаёт полный путь к нему, то есть после команды
pax -rw /etc/ssh/ssh_config /var/tmp
мы получим файл /var/tmp/etc/ssh/ssh_config. pax также идёт в базовой системе FreeBSD. Позднее, когда я пересел на Debian, для меня было неприятной неожиданностью, что pax надо устанавливать дополнительно.
Список файлов для резервного копирования будем задавать двумя способами — как результат работы команды find и как просто список файлов через пробел, что удобно для единичных файлов.
Переменные вынесем в файл настроек /usr/local/etc/svnconfbackup.conf:
#Каталог, где система хранит временные файлы
TMPDIR="/var/tmp"
#Название нашего каталога
REPDIR="conf"
#Сделаем название уникальным
DESTDIR=$REPDIR.`date +%s`
#Списки файлов и исключений
LIST="/etc /usr/local/etc /var/named"
EXCLUDE_LIST="-not -path /etc/'*'shadow'*' \
-and -not -path /var/named/internal/slave/'*' \
-and -not -name '*'.pem \
-and -not -name '*'.key \
-and -not -name '*'.crt \
-and -not -path /etc/ssh_host_rsa_key \
-and -not -path /etc/ssh_host_dsa_key"
FILES="/home/pgsql/data/*.conf"
#Попробем определить пути к командам самостоятельно
SVN=`which svn`
SVNADMIN=`which svnadmin`
#Корневой каталог репозитория
SVNROOT="/usr/local/svnconfbackuproot"
* This source code was highlighted with Source Code Highlighter.
В переменной LIST содержится список каталогов, в которых find будет искать файлы, а в переменной FILES — дополнительный список файлов для резервного копирования. Этот файл настроек можно рассматривать как полноценный sh-скрипт. Напомню, что внутри одиночных, или строгих, кавычек ' ' любой специальный символ (за исключением одинарной кавычки '), интерпретируется как простой символ. То есть, в переменной EXCLUDE_LIST звёздочки останутся звёздочками. А в переменной FILES мы в итоге получим список файлов в директории
/home/pgsql/data/
оканчивающихся на .conf
, поскольку символ "*" в данном случае является шаблоном имени файла.А содержимое, заключённое между обратными кавычками ` `, будет заменено на результат выполнения команды, т.е. на её консольный вывод. Таким образом, вместо
`date +%s`
мы получим количество секунд, прошедших с 00:00:00 1 янв 1970 г. (mktemp
был отвергнут по причине разного поведения в Linux и FreeBSD)Обратный слэш \ отменяет специальное значение символа перевода строки. Это делается на всякий случай.
Но вернёмся к скрипту. Он будет состоять из двух частей — инициализации репозитория Subversion и процедуры резервного копирования. Инициализацию необходимо выполнить вручную один раз, в самом начале. Само резервное копирование должно проходить без вмешательства пользователя.
Синтаксис команды:
svnbackupconfig init|update
Т.е.
svnbackupconfig init
запустит инициализацию репозитория, а svnbackupconfig update
запустит процедуру резервного копирования, другими словами — обновления данных в репозитории.Сначала необходимо подгрузить настройки и выполнить инициализацию локальных переменных
#!/bin/sh
. /usr/local/etc/svnconfbackup.conf
#Путь к репозиторию в формате URL
SVNROOTsvn="file://$SVNROOT"
Дальше обработаем параметр команды, переданный в из командной строки
case "$1" in
init)
#Здесь будет процедура инициализации
;;
update)
#Здесь будет процедура резервного копирования
esac
* This source code was highlighted with Source Code Highlighter.
Процедура инициализации
cd $TMPDIR
#Создать временный каталог
mkdir $DESTDIR
chmod 700 $DESTDIR
#Создать структуру репозитория
mkdir $DESTDIR/branch
mkdir $DESTDIR/tag
mkdir $DESTDIR/trunk
cd $DESTDIR
#Проверить существование корневой директории репозитория
#Создать при необходимости
if [ ! -d $SVNROOT ]; then
$SVNADMIN create $SVNROOT
fi
#Импорт в репозиторий
$SVN import $SVNROOTsvn/$REPDIR -q -m "Initial repository layout"
#Убрать за собой
cd ../
rm -rf $DESTDIR
* This source code was highlighted with Source Code Highlighter.
Процедура резервного копирования не намного длиннее, однако хочу остановиться поподробнее на алгоритме. Чтобы скрипт был простым и понятным, я решил задачу
- Загрузить предыдущий бэкап из репозитория во временный каталог.
- Скопировать туда все файлы, заданные в списках резервного копирования. Пусть Subversion сам разбирается, что изменилось.
- Добавить в репозиторий новые файлы, отсутствующие в предыдущем бэкапе.
- Проверить каждый файл из бэкапа. Если он отсутствует в системе, удалить его из репозитория
- Сохранить результат в репозиторий.
Это, без сомнений, ресурсоёмкий алгоритм. Однако, на типичной системе его выполнение займёт всего несколько минут.
Новые файлы (п.3) можно найти легко — в выводе
svn status
строки начинаются с символа "?":$ svn status
? some.file
Для получения имён файлов будем использовать конструкцию
svn status|grep ^?|awk '{print $2}'
Это значит, что вывод команды
svn status
передаётся на ввод команды grep ?
, которая вернёт строки с первым символом "?". Этот вывод, в свою очередь, передаётся на ввод команды awk '{print $2}'
, которая возвратит второй параметр, т.е. имя файла.Собственно, процедура резервного копирования.
cd $TMPDIR
#Создать временный каталог
mkdir $DESTDIR
chmod 700 $DESTDIR
#Получить из репозитория последний бэкап
$SVN checkout -q $SVNROOTsvn/$REPDIR/trunk $DESTDIR
cd $DESTDIR
#Построить список файлов, которые надо забэкапить
list=`eval find $LIST $EXCLUDE_LIST -and -type f|sort`
#Копировать файлы
for p in ${list} $FILES ; do
pax -rw $p . > /dev/null 2>&1
done;
#Построить список файлов, отсутствующих в предыдущем бэкапе
list=`eval $SVN status|grep ^?|awk '{print $2}'`
#Добавить эти файлы в репозиторий
for p in ${list} ; do
$SVN add -q $p
done;
#Построить список всех файлов в бэкапе
#за исключением файлов в служебных каталогах .svn
list=`eval find . -name .svn -prune -or -print`
#Проверить существование каждого файла из бэкапа в системе
#При необходимости удалить из репозитория
for p in ${list} ; do
if [ ! -e /$p ]; then
$SVN delete -q $p --force
fi
done;
#Напечатать список изменений
$SVN diff
#Загрузить изменения в репозиторий
$SVN commit -q -m "svnbackup automatic update"
#Убрать за собой
cd ../
rm -rf $DESTDIR
* This source code was highlighted with Source Code Highlighter.
Ссылка на архив svnconfbackup.googlecode.com/files/svnconfbackup.tar.gz
Использовать этот скрипт тоже просто. После распаковки архива поместите файл скрипта в
/usr/local/bin
, а файл настроек в /usr/local/etc
. Настройки приведите в соответствии с вашей ОС. Теперь необходимо инициализировать репозиторий. Выполните командуsvnbackupconfig init
Теперь, при желании, можно первый бэкап сделать вручную.
svnbackupconfig update
Процесс займёт несколько минут и завершится выводом текста всех конфигурационных файлов на консоль, поскольку репозиторий ещё пуст. При дальнейшем использовании вывод будет содержать только изменения между версиями.
Настройка автоматического запуска будет несколько отличаться для Linux и FreeBSD.
Для Linux:
echo -e "/usr/local/bin/svnconfbackup update\n" > /etc/cron.daily/svnconfbackup<br>
chmod 755 /etc/cron.daily/svnconfbackup
Для FreeBSD:
echo "/usr/local/bin/svnconfbackup update\n" > /usr/local/etc/periodic/daily/999.svnbackup<br>
chmod 755 /usr/local/etc/periodic/daily/999.svnbackup
Теперь — о том, как пользоваться бэкапом. По большому счёту, ниже — очень краткое описание основных комманд svn.
Для определённости будем использовать настройки по умолчанию. Для получения последнего бэкапа выполните команду
svn checkout file:///usr/local/svnconfbackup/conf/trunk conf
В текущем каталоге будет создан каталог conf, в который и будет помещена копия последнего бэкапа. При восстановлении данных имейте в виду, что в каждом каталоге присутствуют служебные каталоги с именем .svn.
Для следующих операций необходимо перейти в каталог conf, который также называют рабочим каталогом svn.
Нам понадобятся всего две команды — update и diff. Обратите внимание, что команда update обновляет рабочий каталог svn, а не репозиторий. Таким образом, для отката на предыдущую версию нужно выполнить команду
svn update --revision PREV
Вообще, в качестве аргумента для ключа --revision или его короткой формы -r можно указать и номер ревизии и дату. Например, так:
svn update -r "{2010-03-31}"
В данном случае будет взят бэкап на 31 марта 2010 г. 00:00:00. Обратите внимание, что если у вас автоматический бэкап выполняется позже этого времени, например, в 4 часа утра, то в рабочем каталоге будет бэкап за 30 марта.
С помощью команды diff можно посмотреть изменения между двумя любыми версиями файлов. При этом можно в явном виде указать, версии каких файлов надо сравнивать. Например, команда
svn diff -r "{2010-01-01}:HEAD"
покажет, что изменилось с прошлого года. Кстати, ключ -r работает и с командой checkout! Подробнее о синтаксисе и ключах можно узнать из справки:
svn help <имя комманды>
На всякий случай оставлю ссылку на сайт отличной книги по Subversion.
Желаю удачи! И помните — администраторы делятся на два типа — одни данные уже потеряли, а другие ещё потеряют!
UPD Перенёс в тематический блог