СПО в российских школах

Команда ALT Linux рассказывает о внедрении свободного программного обеспечения в школах России
Декабрь 29, 2009

Управление каталогом /etc с использованием tar и git

Методические материалы, Опыт работы с ПСПО
Автор: ALT Linux

Команда разработчиков ALT Linux постоянно использует в своей работе git. Что это такое, и как это можно использовать рассказывает в своей статье авторизованный преподаватель ALT Linux, преподаватель УЦ R-Style Антон Чернышов. Нам кажется, его опыт поможет разобраться с этой программой.


Управление каталогом /etc с использованием tar и git

Директория /etc является одним из ключевых мест Linux-системы. Каждый, кто любит поэкспериментировать с системой, знает как часто бывает так: отредактируешь несколько конфигурационных файлов, а потом спустя день-два обнаруживается, что что-то перестало работать. И не каждый обладает такой прекрасной памятью, чтобы вспомнить какие файлы редактировал и что конкретно менял. Хорошая привычка сохранять оригинальные конфигурационные файлы перед их редактированием приходит обычно с опозданием :). Многие бывалые системные администраторы сталкивались с подобными проблемами. Поэтому назревает вопрос — а есть ли такое средство, которое позволяло бы отслеживать изменения сразу в нескольких файлах, вело бы своеобразную историю изменений? Как следует из одной хорошей поговорки (авторство, к сожалению, не помню) «если у вас появилась идея как решить какую-то проблему, то в мире Linux ее уже скорее всего решили или работают над этим» ;). В данной статье приведены принципы, которые позволяют достичь если не 100% надежности (таких гарантий не даст никто), то уж 90% проблем решат точно.

Первым таким средством является популярный инструмент разработчиков — git. Разработчикам часто приходится отслеживать изменения сразу во многих файлах, поэтому git умеет это делать легко и быстро. git написан самим Линусом Торвальдсом для ведения разработки ядра Linux (и уж конечно это не главное достоинство этой программы :) ). Раньше для этого использовалось коммерческая программа Bit Keeper, за что Торвальдса постоянно упрекали. Когда ему надоели эти упреки, он и написал git. В данной стать пойдет речь о том, как такое средство для разработки как git могут использовать обычные администраторы.

git превращает директорию с файлами в репозиторий, за состоянием которого он будет следить. git умеет:

  • отслеживать изменения во всех файлах своего репозитория (естественно, включая файлы во вложенных директориях);
  • выполнять «коммиты», т. е. подтверждения изменения файлов (от слова commit), т. е. вести контрольные точки внесения изменений в конфигурационные файлы;
  • вести несколько ветвей (branches), которые можно будет независимо редактировать и переключаться между ними по мере необходимости.

Далее я буду приводить все команды и их результат со своего домашнего компьютера, поэтому все изменения буду проводить над резервной копией директории /etc, находящейся в /root. Создание такой копии делается следующим образом:

[root@localhost ~]# mkdir /root/etc
[root@localhost ~]# cp -R /etc/* /root/etc/

Под термином «репозиторий» будет пониматься именно директория /etc, превращенная в репозиторий git.

Кстати говоря, ознакомление с работой git, я рекомендую делать на резервной копии /etc, до тех пор пока вы не поймете назначения команд и то, что они делают. И только с приходом уверенности — переносить все на «живой» каталог /etc !!! В противном случае возможны непредсказуемые изменения файлов внутри /etc, цунами, торнадо, землетрясения и прочие неприятные последствия.
Вы предупреждены!

Перед созданием репозитория нужно создать список исключений, то есть список файлов, изменения которых git будет игнорировать. Сделать это нужно потому, что при нормальной работе системы некоторые файлы внутри /etc меняются сами. Перечень таких файлов определяется обычно используемым дистрибутивом. Обычно это файлы blkid.tab и mtab. Первый содержит служебную информацию об изменениях состояния блочных устройств, а второй — актуальную информацию о смонтированных файловых системах. Создание списка исключений делается созданием файла .gitignore в директории нашего будущего репозитория:

[root@localhost:~/etc]# cat .gitignore
blkid.tab
mtab
*[bB]ak
asound.state

Строчкой *[bB]ak я исключаю из-под контроля git файлы резервных копий, которые могут заканчиваться на bak или Bak. Последний часто меняется при изменении настроек громкости звука. Названия прочих файлов, которые следует исключить из-под контроля git, подскажет опыт эксплуатации системы (такие файлы будут часто самостоятельно меняться).

Также можно проинформировать git о том, кто мы такие (необязательно), эта информация будет использоваться в качестве подписи при выполнении подтверждений изменений в директории. Для этого в своем домашнем каталоге нужно создать файл .gitconfig, следующего содержания:

[root@localhost:~]# cat .gitconfig
[user] name = Anton Chernyshov
email = anton@localhost

Для превращения какой-либо директории в репозиторий git, необходимо зайти в нее и выполнить следующие команды:

[root@localhost:~/etc]# cd /root/etc/
root@localhost:~/etc# git init
Initialized empty Git repository in /root/etc/.git/

Команда git init создает директорию .git в нашем репозитории, которая будет содержать всю служебную информацию обо всех изменениях. Поскольку мы будем работать над каталогом /etc, а он содержит приватную информацию (самой критичной является наличие хешей паролей пользователей системы), то хорошим тоном будет сделать этот каталог недоступным для рядовых пользователей системы:

[root@localhost:~/etc]# chmod o-rwx .git
root@localhost:~/etc# ls -al | grep git
drwxr-x--- 8 root root 4096 2009-12-27 17:57 .git

Если все сделано правильно, то в правах доступа на директорию последние три символа будут «-», показывая, что никто кроме пользователя root и членов группы root не сможет зайти в данную директорию. Понятно, что глупо делать это сейчас, ведь мы работаем над резервной копией /etc внутри каталога /root, который и так не доступен пользователям системы. Однако об этом стоит помнить при работе с реальным /etc.

[root@localhost:~/etc]# git add .

Команда git add . ставит в очередь на «коммит» все файлы, об изменении которых нет информации в репозитории. В данном случае это вообще все файлы, которые есть в /etc, включая все поддиректории со всеми их файлами, потому что репозиторий пока пуст. Коммитом (от английского слова commit) называется подтверждение изменений в файлах репозитория. Давайте выполним наш первый коммит:

[root@localhost:~/etc]# git commit

После ввода данной команды git попросит указать информацию о выполняемом коммите. Стесняться не стоит, нужно указать столько информации, сколько будет необходимо для того, чтобы потом вспомнить, какие именно изменения фиксирует данный коммит. Посмотреть информацию о коммитах можно выполнив следующую команду:

[root@localhost ~/etc]# git log
commit 0f97eb7b8f46bcdadf000bff1b12e5e29ac02a27
Author: Anton Chernyshov <anton@localhost>
Date: Sun Dec 27 17:57:38 2009 +0300  

Инициализация репозитория

В строке commit указан уникальный идентификатор коммита (это хеш, выполненный по алгоритму SHA). Далее указаны автор (тот, кто сделал коммит, см. выше про файл .gitconfig), дата и набранный на предыдущем шаге комментарий.

Следующей полезной возможностью git является использование ветвей. Ветвь (branch) — это независимое состояние контролируемого репозитория. Редактирование одной ветви никак не влияет на другую, что и будет проиллюстрировано ниже. Использование ветвей необходимо тем, кто любит поэкспериментировать с системой. Можно иметь гарантированно работающую ветвь (в качестве нее можно оставить ветвь по умолчанию master) и ветвь для экспериментов (и назвать ее working). А затем, ту конфигурацию, которая показала себя работоспособной, переносить в первую. Создадим ветвь working:

[root@localhost:~/etc]# git branch working
[root@localhost:~/etc]# git branch
* master
working

Команда git branch working создает ветвь working. git branch выводит список имеющихся ветвей. Звездочкой отмечена текущая ветвь master, которая автоматически создается при инициализации репозитория. Для переключения между ветвями нужно использовать команду git checkout:

[root@localhost ~/etc]# git checkout working
Switched to branch 'working'
[root@localhost ~/etc]# git branch
master
* working

Теперь звездочкой отмечена ветка working. Меняем один файл в директории и смотрим, что поменялось:

[root@localhost ~/etc]# git diff
diff --git a/exports b/exports
index e69de29..9a0f871 100644
--- a/exports
+++ b/exports
@@ -0,0 +1,2 @@
+/srv/data 192.168.0.0/255.255.255.0
+

Теперь при изменении любых файлов git нам будет показывать их изменения в таком вот виде, аналогичном выводу команды diff (за подробностями см. man diff). Для подтверждения данного изменения и внесения его в журнал нужно сделать следующее:

[root@localhost ~/etc]# git add exports

Этим мы ставим файл в очередь на коммит. Таким способом можно вносить информацию об изменениях отдельных файлов. Фиксируем информацию об изменениях:

[root@localhost ~/etc]# git commit
[working 4beda47] Экспортирование директории /srv/data через NFS 1 files changed, 2 insertions(+), 0 deletions(-)

Смотрим информацию об изменениях:

[root@localhost ~/etc]# git log
commit 4beda47f709af7d8695a73bd1f69d3e319d34ba7
Author: Anton Chernyshov <anton@localhost>
Date: Sun Dec 27 18:37:39 2009 +0300  

Экспортирование директории /srv/data через NFS  

commit 0f97eb7b8f46bcdadf000bff1b12e5e29ac02a27
Author: Anton Chernyshov <anton@localhost>
Date: Sun Dec 27 17:57:38 2009 +0300  

Инициализация репозитория  

Следующая команда скажет чуть больше об изменениях:  

[root@localhost ~/etc]# git show
commit 4beda47f709af7d8695a73bd1f69d3e319d34ba7
Author: Anton Chernyshov <anton@localhost>
Date: Sun Dec 27 18:37:39 2009 +0300  

Экспортирование директории /srv/data через NFS  

diff --git a/exports b/exports
index e69de29..9a0f871 100644
--- a/exports
+++ b/exports
@@ -0,0 +1,2 @@
+/srv/data 192.168.0.0/255.255.255.0
+

Мы видим здесь информацию аналогичную сделанной ранее git diff (см. выше). По-умолчанию, git show показывает информацию о последнем коммите. Естественно, что можно попросить ее сделать это и для любого другого. Для этого в качестве параметра ей нужно передать идентификатор commit’а. Да, да, тот самый длинный SHA-хэш. Но не пугайтесь, его не нужно набирать целиком (эту же программу делали умные люди для таких же умных людей 😉 ). Достаточно указать первые 5-6 символов (по сути достаточно, чтобы указанный идентификатор коммита был уникальным).
Проиллюстрируем это примером. Меняем еще один файл:

[root@localhost ~/etc]# vim ~/etc/hosts

Смотрим изменения:

[root@localhost ~/etc]# git diff
diff --git a/hosts b/hosts
index 2556c5d..97d301d 100644
--- a/hosts
+++ b/hosts
@@ -3,3 +3,5 @@
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 localhost 192.168.0.254 rivendale  

+192.168.0.35 rohan
+

Подтверждаем изменения:

[root@localhost ~/etc]# git add hosts
[root@localhost ~/etc]# git commit
[working 63468c4] Добавление в сеть новых хостов
1 files changed, 2 insertions(+), 0 deletions(-)
[root@localhost ~/etc]# git log
commit 63468c4c541603e23d69dfb5ac43525f3eaab040
Author: Anton Chernyshov <anton@localhost>
Date: Sun Dec 27 18:51:32 2009 +0300  

Добавление в сеть новых хостов  

commit 4beda47f709af7d8695a73bd1f69d3e319d34ba7
Author: Anton Chernyshov <anton@localhost>
Date: Sun Dec 27 18:37:39 2009 +0300  

Экспортирование директории /srv/data через NFS  

commit 0f97eb7b8f46bcdadf000bff1b12e5e29ac02a27
Author: Anton Chernyshov <anton@localhost>
Date: Sun Dec 27 17:57:38 2009 +0300  

Инициализация репозитория

Теперь команда git show покажет нам информацию о последнем коммите:

[root@localhost ~/etc]# git show

commit 63468c4c541603e23d69dfb5ac43525f3eaab040
Author: Anton Chernyshov <anton@localhost>
Date: Sun Dec 27 18:51:32 2009 +0300  

Добавление в сеть новых хостов  

diff --git a/hosts b/hosts
index 2556c5d..97d301d 100644
--- a/hosts
+++ b/hosts
@@ -3,3 +3,5 @@
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 localhost 192.168.0.254 rivendale  

+192.168.0.35 rohan
+

Посмотреть информацию о предыдущем коммите, можно следующей командой:

[root@localhost ~/etc]# git show 4beda47
commit 4beda47f709af7d8695a73bd1f69d3e319d34ba7
Author: Anton Chernyshov <anton@localhost>
Date: Sun Dec 27 18:37:39 2009 +0300  

Экспортирование директории /srv/data через NFS  

diff --git a/exports b/exports
index e69de29..9a0f871 100644
--- a/exports
+++ b/exports
@@ -0,0 +1,2 @@
+/srv/data 192.168.0.0/255.255.255.0
+

Выделение здесь мое, им я показал использованную часть идентификатора коммита.

Как вариант, посмотреть и оценить общее состояние репозитория можно командой git status (я предварительно изменил файл resolv.conf):

[root@localhost ~/etc]# git status
# On branch working
# Changed but not updated:
# (use "git add ..." to update what will be committed)
# (use "git checkout -- ..." to discard changes in working directory)
#
# modified: resolv.conf
#
no changes added to commit (use "git add" and/or "git commit -a")

Здесь мы видим, что git показывает нам изменившийся файл и говорит, что нужно сделать для выполнения коммита.

Теперь про отмену изменений. На всякий случай еще раз напомню, что git — очень мощный инструмент и пользоваться им нужно аккуратно, поэтому прежде чем выполнять приведенные ниже операции, перед реальным применением рекомендую «потренироваться на кошках».

Для отмены изменений которые еще не внесены в репозиторий (коммит не выполнялся и в репозитории есть измения), нужно выполнить команду git checkout <имя_файла>. Команда git checkout . (с точкой) отменит вообще все изменения и репозиторий вернется в состояние, которое зафиксировал последний коммит (этот пример — продолжение предыдущего):

[root@localhost ~/etc]# git checkout .
[root@localhost ~/etc]# git status
# On branch working
nothing to commit (working directory clean)

Как мы видим, изменения, внесенные мной в файл resolv.conf, бесследно пропали.

Изменения можно также откатывать и до определенных точек, включая предыдущие. Чтобы было понятнее, я отредактирую два файла (resolv.conf и fstab) и выполню для этих изменений по коммиту:

[root@localhost ~/etc]# git commit -m "Новый сетевой ресурс" fstab
[working dc18de9] Новый сетевой ресурс
1 files changed, 2 insertions(+), 0 deletions(-)
[root@localhost ~/etc]# git commit -m "Добавление в сеть нового DNS сервера" resolv.conf
[working 02bc892] Добавление в сеть нового DNS сервера
1 files changed, 1 insertions(+), 8 deletions(-)
[root@localhost ~/etc]# git log
commit 02bc892b5fd8a40a063c06248268511ac3d1efb5
Author: Anton Chernyshov
Date: Tue Dec 29 23:04:36 2009 +0300

Добавление в сеть нового DNS сервера

commit dc18de9b99ec7ab661a3352491cd111c0a35e6ef
Author: Anton Chernyshov
Date: Tue Dec 29 23:04:26 2009 +0300

Новый сетевой ресурс

commit 63468c4c541603e23d69dfb5ac43525f3eaab040
Author: Anton Chernyshov
Date: Sun Dec 27 18:51:32 2009 +0300

Добавление в сеть новых хостов
<---------вывод команды обрезан для краткости----------->

Далее выполняем следующую команду:

[root@localhost ~/etc]# git reset -p 63468

Такая команда заставляет git сгенерировать патчи для возврата к указанному состоянию (цифра — это идентификатор коммита, к которому нужно вернуть состояние репозитория) и наложить их на соответствующие файлы. Говоря простым языком — git просто вернет содержимое данных файлов к указанному состоянию. Причем, перед каждым наложением патча git будет спрашивать подтверждение в таком вот виде:

diff --git b/resolv.conf a/resolv.conf
index 3142a1a..55117a9 100644
--- b/resolv.conf
+++ a/resolv.conf
@@ -1,2 +1,9 @@
-nameserver 192.168.0.254
+# Generated by NetworkManager

+
+# No nameservers found; try putting DNS servers into your
+# ifcfg files in /etc/sysconfig/network-scripts like so:
+#
+# DNS1=xxx.xxx.xxx.xxx
+# DNS2=xxx.xxx.xxx.xxx
+# DOMAIN=lab.foo.com bar.foo.com
Apply this hunk to index [y,n,q,a,d,/,s,e,?]? y

Здесь опять указаны исходный и измененный файлы в формате вывода команды diff. Последней строчкой git спрашивает подтверждения указанной операции. «y» означает «yes», то есть «да», «n» — «no»/«нет». Знак вопроса выведет полную справку по возможным вариантам ответа.
А вот вывод команды git log достаточно интересен в данном случае:

[root@localhost ~/etc]# git log
commit 02bc892b5fd8a40a063c06248268511ac3d1efb5
Author: Anton Chernyshov
Date: Tue Dec 29 23:04:36 2009 +0300

Добавление в сеть нового DNS сервера

commit dc18de9b99ec7ab661a3352491cd111c0a35e6ef
Author: Anton Chernyshov
Date: Tue Dec 29 23:04:26 2009 +0300

Новый сетевой ресурс

commit 63468c4c541603e23d69dfb5ac43525f3eaab040
Author: Anton Chernyshov
Date: Sun Dec 27 18:51:32 2009 +0300

Добавление в сети новых хостов
<---------вывод команды обрезан для краткости----------->

Ничего не изменилось! Вывод команды git status восстанавливает статус-кво:

[root@localhost ~/etc]# git status
# On branch working
# Changes to be committed:
# (use "git reset HEAD ..." to unstage)
#
# modified: fstab
# modified: resolv.conf
#
# Changed but not updated:
# (use "git add ..." to update what will be committed)
# (use "git checkout -- ..." to discard changes in working directory)
#
# modified: fstab
# modified: resolv.conf
#

Что же произошло? git модифицировал содержимое каталога /etc на указанное состояние, но не изменил журнал коммитов. А это дает нам возможность вернуть все обратно. Делается это командой:

[root@localhost ~/etc]# git checkout -p 02bc892

Дальше git нас спросит способом аналогичным выше о вносимых изменениях и если мы на все согласимся, то репозиторий вернется к состоянию, аналогичному до выполнения команды git reset:

[root@localhost ~/etc]# git status
# On branch working
nothing to commit (working directory clean)

Как вариант можно было бы вместо 02bc892 указать HEAD. Данное значение всегда указывает на самый последний коммит. Аналогичным способом можно отменить любой другой коммит на выбор. При этом git также не модифицирует свой журнал, позволяя нам выполнять отмену отмены коммитов :) (это полезно при поиске ошибок).

Интересно себя поведет следующая команда:

[root@localhost ~/etc]# git reset 63468
Unstaged changes after reset:
M fstab
M resolv.conf

По сравнению с примером выше мы убрали ключ -p . При этом изменился журнал git (бесследно пропали записи о сделанных коммитах):

[root@localhost ~/etc]# git log
commit 63468c4c541603e23d69dfb5ac43525f3eaab040
Author: Anton Chernyshov
Date: Sun Dec 27 18:51:32 2009 +0300

Добавление в сети новых хостов

commit 4beda47f709af7d8695a73bd1f69d3e319d34ba7
Author: Anton Chernyshov
Date: Sun Dec 27 18:37:39 2009 +0300

Экспортирование директории /srv/data через NFS

commit 0f97eb7b8f46bcdadf000bff1b12e5e29ac02a27
Author: Anton Chernyshov
Date: Sun Dec 27 17:57:38 2009 +0300

Инициализация репозитория

А вот сами файлы при этом не изменились. Это логично, потому что мы не указали ключ -p, означающий patch (исправление). git status подтверждает это:

[root@localhost ~/etc]# git status
# On branch working
# Changed but not updated:
# (use "git add ..." to update what will be committed)
# (use "git checkout -- ..." to discard changes in working directory)
#
# modified: fstab
# modified: resolv.conf
#
no changes added to commit (use "git add" and/or "git commit -a")

Данная команда позволяет отменять неправильные коммиты, удаляя записи о них в журнале git, но не модифицируя сами файлы. После такой команды можно выполнить отмененные коммиты заново или сделать git checkout . , заодно отменив и изменения файлов:

[root@localhost ~/etc]# git checkout .
[root@localhost ~/etc]# git status
# On branch working
nothing to commit (working directory clean)

А что насчет использования разных веток? К ним мы сейчас как раз вернемся. Напомню, что сейчас мы работали с ветвью working. Переключаемся обратно в master:

[root@localhost ~/etc]# git checkout master
Switched to branch 'master'
[root@localhost ~/etc]# git log
commit 0f97eb7b8f46bcdadf000bff1b12e5e29ac02a27
Author: Anton Chernyshov <anton@localhost>
Date: Sun Dec 27 17:57:38 2009 +0300  

Инициализация репозитория

А куда делись все наши изменения? А все просто — их тут и не было. Мы работали с другой ветвью и вносили изменения туда. Можно проверить содержимое файлов, которые мы меняли ранее:

[root@localhost ~/etc]# cat exports
[root@localhost ~/etc]#

Файл exports, как мы видим, пустой.

Предположим мы решили, что изменения внесенные в ветвь working достаточно хорошо протестированы и их можно переносить в рабочую ветку:

[root@localhost ~/etc]# git merge working
Updating 0f97eb7..63468c4
Fast forward
exports | 2 ++
hosts | 2 ++
2 files changed, 4 insertions(+), 0 deletions(-)

Команда git merge производит слияние указанной ей в качестве параметров ветки с текущей, т.е команда git merge working переносит ее содержимое в текущую ветку. И, кстати говоря, еще показывает какие-именно файлы поменялись.

[root@localhost ~/etc]# git log
commit 63468c4c541603e23d69dfb5ac43525f3eaab040
Author: Anton Chernyshov <anton@localhost>
Date: Sun Dec 27 18:51:32 2009 +0300  

Добавление в сети новых хостов  

commit 4beda47f709af7d8695a73bd1f69d3e319d34ba7
Author: Anton Chernyshov <anton@localhost>
Date: Sun Dec 27 18:37:39 2009 +0300  

Экспортирование директории /srv/data через NFS  

commit 0f97eb7b8f46bcdadf000bff1b12e5e29ac02a27
Author: Anton Chernyshov <anton@localhost>
Date: Sun Dec 27 17:57:38 2009 +0300  

Инициализация репозитория

Как мы видим, что теперь вывод команды git log для данной ветки аналогичен выводу данной команды для ветки working.

Таким образом, используя приведенный набор команд можно достаточно легко вести историю изменений в директории /etc, самое главное не лениться и не забывать почаще вызывать для этого команду git. Да и экспериментировать с системой теперь можно гораздо смелее :). Ниже приведено резюме упомянутых команд git:

Резюме команд:

  • git init — создание репозитория в текущей директории
  • git addfilename — постановка файла filename в очередь на коммит. Если вместо filename указать точку, в очередь встанут все файлы, информации об изменении которых нет в репозиториях
  • git commit — подтверждение сделанных изменений.
  • git commit -a — то же, что и выше, плюс подтверждение удалений файлов.
  • git branchbranchname — создание ветви branchname в репозитории.
  • git branch — вывод информации об имеющихся ветвях
  • git checkoutbranchname — переключение на работу с ветвью branchname.
  • git checkout -p commit_id — отмена указанного коммита
  • git reset -p commit_id — откат репозитория с конца до указанной точки, при этом все файлы репозитория меняются на указанную точку, журнал git не меняется
  • git reset commit_id — удаление информации о коммитах с конца до указанной точки БЕЗ модификации файлов репозитория
  • git log — вывести список коммитов.
  • git show — показать информацию о последнем коммите
  • git status — вывод информации о статусе репозитория (измененные, новые, удаленные файлы
  • git mergebranchname — производит перенос изменений из ветки branchname в текущую.
  • git show commithash — покажет изменения подтвержденные последним коммитом.

Подробности работы с git можно посмотреть в его официальной документации:man -k git , в директории /usr/share/doc/git и на официальном сайте git.

Для защиты директории /etc в качестве средства «второго эшелона» можно использовать хорошо известную команду tar. Сохраненные архивы файлов позволят восстановить несколько файлов, не редактируя их. Ниже приведен скрипт для выполнения регулярного резервного копирования директории /etc :

#!/bin/bash
 
# This file in UTF-8 encoding
# Комментарием выше мы указываем используемую кодировку,
# потому что для русского языка их существует очень много.
 
# Задание директории для создания резервных копий
BACKUP_DIR=/home/root/backup
 
# Задаем количество резервных копий
BACKUP_COUNT=10
 
# Проверяем существование директории для резервных копий
# и если ее не существует - создаем ее
if [ ! -d $BACKUP_DIR ]; then
mkdir -p $BACKUP_DIR
fi
 
# Проверяем код выполнения предыдущей операции
EXIT_STATUS=$?
 
# Если код выполнения не равен нулю (операция НЕ завершилась успешно),
# то завершаем работу скрипта
if [ $EXIT_STATUS -ne 0 ]; then
echo "Невозможно создать директорию"
echo "Завершение работы"
exit $EXIT_STATUS
fi
 
# Архивация директории /etc
tar -cpjf $BACKUP_DIR/etc_$(date +%d_%b).tar.bz2 /etc
 
# Удаление резервных копий возрастом старше указанного
find $BACKUP_DIR/ -type f -ctime +$BACKUP_COUNT -delete
 
# Возвращаем код успешного завершения работы операционной системе
exit 0
 

Комментарии к скрипту:

Переменная $? обычно хранит статус выполнения предыдущей команды. Если команда завершилась успешно, то он обычно равен нулю. Правила хорошего тона обычно требуют выполнять проверку успешного выполнения критически важных операций.

В команде tar используется следующий набор опций tar -cpjf, означающих следующее:

  • c — создание архива;
  • p — сохранение при архивации прав доступа;
  • j — для сжатия архива использовать архиватор bzip, как вариант можно было бы указать z для использования gzip;
  • f — имя файла для создаваемого архива (данная опция должна быть последней).

Полный список возможных опций можно посмотреть в man tar. В качестве имени архива мы используем следующее etc_$(date +%d_%b).tar.bz2 .
Здесь мы, для создания гарантированно разных имен файлов, используем автоподстановку, применяя для этого команду date. Те опции, которые используются здесь создают следующие имена файлов:

[root@localhost /home/root]# ls -1 /home/root/backup
etc_13_Dec.tar.bz2
etc_14_Dec.tar.bz2
etc_15_Dec.tar.bz2
etc_18_Dec.tar.bz2
etc_20_Dec.tar.bz2
etc_21_Dec.tar.bz2
etc_22_Dec.tar.bz2
etc_27_Dec.tar.bz2

Полный список возможных вариантов вывода команды date можно посмотреть в man date. Завершает скрипт команда find, используемая со следующими опциями: find $BACKUP_DIR/ -type f -ctime +$BACKUP_COUNT -delete

Ее опции означают следующее:

  • она выполняет поиск в директории, заданной переменной $BACKUP_DIR
  • ищет обычные файлы — опция -type f
  • поиск файлов имеющих возраст более заданного количества дней задается опцией -ctime , например, опция -ctime +10 найдет файлы старше 10 дней
  • опция -delete говорит команде find, что найденные файлы следует удалять.

Данный скрипт нужно добавить как задание для cron, чтобы он выполнялся регулярно. Сделать это можно или через crontab -e (man crontab), или добавить его в каталог /etc/cron.daily.
Еще можно сделать так, чтобы данные резервные копии отправлялись через ssh на другой хост сети, но это уже совсем отдельная тема и об этом мы поговорим позже.

Статья размещена в личном блоге Антона Чернышева

Комментарии (6) к “Управление каталогом /etc с использованием tar и git”

  1. Хорошая статья. Но по моему строчку:

    [root@localhost ~/etc]# vim /etc/hosts

    Следует заменить на:

    [root@localhost ~/etc]# vim ~/etc/hosts

    Или это специально сделано для новичков? 😉

    Ещё хотел бы добавить. Использовать git diff для быстрой оценки ситуации не лучший способ. git status для этих целей удобнее. Выводится текшая ветка и какие файлы изменены и какие уже приготовлены к коммиту.

    Кроме того не приведена информация по отмене изменений, тоже специально? Следующая команда приведет к отмене всех изменений которые ещё не были внесены в репозитарий:

    [root@localhost ~/etc]# git checkout .

    Вместо точки можно указать конкретный файл.

  2. > [root@localhost ~/etc]# vim /etc/hosts
    > Следует заменить на:
    > [root@localhost ~/etc]# vim ~/etc/hosts

    Или это специально сделано для новичков? Нет, не специально, конечно же это опечатка. Спасибо Вам огромное за ее нахождение :). Методы opensource в действии :). Проще даже заменить ее просто на:
    [root@localhost ~/etc]# vim hosts

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

  3. Владимир Черный говорит:
    Декабрь 30, 2009, 4:45 пп

    По просьбе автора внесены изменения, в частности, добавлен рассказ про git checkout, упомянутый в комментарии Ивана

  4. -=Sphynkx=- говорит:
    Январь 9, 2010, 1:27 дп

    Полезная статейка!! Сам когда-то таким бпловался, но только с CVS…

  5. Snejok говорит:
    Февраль 13, 2010, 6:18 пп

    В первом упоминании команды «git commit» не хватает пар-ра -a, т.е. «git commit -a»

  6. Snejok говорит:
    Февраль 13, 2010, 6:19 пп

    Нет, вру, все правильно =)

Оставьте комментарий