Управление историей изменения конфигурационных файлов в Linux

Одной из самых часто возникающих при обслуживании сервера проблем является проблема идентификации изменений в настройках служб. К примеру, web-сервер работал хорошо, однако, после изменения настроек его производительность упала или он начал работать нестабильно. Традиционным способом решения данной проблемы является сохранение копии конфигурационного файла, однако, это далеко не самый удобный вариант решения проблемы. В этой статье мы рассмотрим стратегию и инструкцию по настройке сохранения изменившихся конфигурационных файлов в репозиторий Git, что позволит не только иметь резервную копию всех конфигурационных файлов, но и легко наблюдать за историей их изменений и самими изменениями.

Итак, давайте представим наше идеальное решение, которое позволило бы нам в автоматическом режиме отслеживать изменения в конфигурационных файлах и сохранять данные изменения в репозиторий Git. Какими свойствами оно должно обладать:

  1. автоматически отслеживать изменения и сохранять их для будущего анализа;
  2. отслеживать историю изменений каждого файла с возможностью доступа к историческим состояниям и сравнения текущего состояния с историческим;
  3. безопасность хранения изменений и самих конфигурационных файлов;
  4. отслеживание изменений конфигураций нескольких серверов.

Хочется, отметить, что, при применении средств непрерывной автоматизации конфигураций (continuous configuration automation), подход, который мы обсуждаем здесь не будет иметь большой пользы, но, как правило, такие инструменты как Ansible, Chef, Puppet применяются только при управлении большими парками серверов. Применяющие же данные инструменты люди вполне осознают как добиться эффективного отслеживания изменений.

Итак, для начала работы необходимо определиться с провайдером Git. Вы можете самостоятельно установить и настроить Git, но для использования Git с удобным интерфейсом, позволяющим делать анализ изменений online, я рекомендую воспользоваться Gitlab. Образ для Docker можно взять здесь, а его настройка описана в официальной документации. Однако, некоторым администраторам будет проще купить платный аккаунт на GitHub за 7 долларов, в котором можно организовать неограниченное количество приватных репозиториев как для целей задачи управления конфигурациями, так и для иных скриптов. Далее, в данной статье будем считать, что используется GitHub. Мы будем использовать публичный репозиторий и давать ссылки на него.

Для решения задачи нам понадобится репозиторий Git, будем считать, что в каждом репозитории будет каталог с именем сервера, в котором будут отслеживаться конфигурационные файлы для этого сервера, например, для файла /etc/passwd с сервера jupiter:

configurations/jupiter/etc/passwd

Возможно организовать работу таким образом, чтобы все изменения конфигурационных файлов переносились в Git. Для этого можно воспользоваться инструментарием Inotify, например, inotifywait, однако, это кажется избыточным в общем случае. В рамках данной инструкции будем считать, что синхронизации 1 раз в час достаточно для нормального отслеживания изменений.

В данной инструкции нас не беспокоит определение того, кто изменил конфигурационные файлы. Мы будем считать, что пользователь, который это делает авторизован для выполнения задачи. Иными, словами, мы храним изменения не для того, чтобы бить палкой виновного, а для того, чтобы уметь определить что именно происходило с конфигурационными файлами.

Итак, для выполнения нашей цели определим пути файлов конфигураций, которые будем отслеживать. Обычно, /etc, /usr/local/etc, но у Вас могут быть и другие. Будем считать, что в рамках задачи мы отслеживаем пути:

/etc
/usr/local/etc

Рассмотрим, как будет выглядеть общая схема работы нашей системы по шагам:

  1. периодически будем переносить изменения из отслеживаемых каталогов в репозиторий с помощью rsync;
  2. каждый день в репозитории создается отдельная ветка для состояния предыдущего дня, в которую помещаются изменения на этот день;
  3. в ветке master всегда будет находиться текущая конфигурация.

Особого внимания заслуживает пункт «2». Часто администратору требуется восстановить конфигурацию на какой-то конкретный день. Создание ветки для каждого дня позволит решить эту задачу.

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

Создание репозитория

Итак, для начала необходимо создать репозиторий Git, в котором мы планируем хранить конфигурации. В случае GitHub, данная задача решается следующим образом (у нашей компании на GitHub имя пользователя netpoint-dc:

echo "# configurations" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin git@github.com:netpoint-dc/configurations.git
git push -u origin master

Эти команды будет необходимо ввести на нашем первом сервере, конфигурацию которого мы планируем отслеживать. Скопируйте их в текстовый файл или просто в буфер обмена.

Установка клиента Git на сервер

Для Debian, Ubuntu

apt-get update && apt-get install git

Для CentOS

yum install git

Генерация файла ключей SSH для доступа к Git

Доступ к Git возможно осуществлять по протоколу SSH, для этого необходимо положить публичную часть ключа на сервер Git. В GitHub это делается по данному адресу. Сам же ключ необходимо сгенерировать командой:

ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:wBHTpwEdejAhByHJ9R/pajk3WNfofpJWEE+2I46dgoM root@git-config-article
The key's randomart image is:
+---[RSA 2048]----+
| ..o=oX*..       |
| o. = *+.o o     |
| = ++ = .        |
| =..oo+          |
| . .S+o+..       |
| E o=oo+ .       |
| *.o..o          |
| . o o+ .        |
| ..o             |
+----[SHA256]-----+
root@git-config-article:~#

Публичную часть ключа необходимо добавить на Ваш сервер Git (для GitHub адрес):

cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFrbYQFrRHTFrcvy4N3n/rKj9qWJfpjrka7MnzpH0lvU1TllOgKD4XqP4a3CIr7lMgnkBtoXQcKHgYtX9aeMVUSLKCkNj0yxbIzcGaQi/fe5YBpAk7sKhpVwHXYqnrL9WVf9ZQ/D3uI/M9lxUZwj6TuBXYIsBx/+oe5kKhS01GhXYsFyWKLMUHS/NLaozPH9lFoa4fiQqi4Z9oK2pPzpz+AyiGV+yOxry/RlXtkq2KnB4qRkdf907zC94zIVRxPjZbvlvNlRCf2PLSAMnGkNorVdIjEP+b0tY+H8STeoo9PT6Pyt7IusMEqy4l8ppLqzbDpfPId5/6YbOk4CnaEVOJ root@git-config-article

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

Обратите внимание, мы используем для доступа к Git протокол git://. Если Вы планируете использовать протокол HTTPS, то Вы будете использовать аутентификацию по паре login/password, что не очень удобно.

mkdir configurations
cd configurations
echo "# configurations" >> README.md 
git init 
git add README.md 
git commit -m "first commit" 
git remote add origin git@github.com:netpoint-dc/configurations.git
git push -u origin master

Если все прошло успешно, то Вы должны получить сообщение, похожее на следующее:

root@git-config-article:~/configurations# git push -u origin master
The authenticity of host 'github.com (192.30.253.113)' can't be established.
RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'github.com,192.30.253.113' (RSA) to the list of known hosts.
Counting objects: 3, done.
Writing objects: 100% (3/3), 241 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:netpoint-dc/configurations.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.

А в репозитории Git — репозиторий с таким содержимым.

Наполнение репозитория файлами

Для решения данной задачи воспользуемся командой rsync :

mkdir -p /root/configurations/$(hostname)
rsync -av /etc /usr/local/etc /root/configurations/$(hostname)/

Если Вы отслеживаете большее количество каталогов, то их просто необходимо добавить в перечень отслеживаемых. После переноса файлов необходимо их добавить новые в Git:

find /root/configurations/* -exec git add {} \;
git commit -a -m "Configuration updated at $(date) from $(hostname)"

Загрузим файлы в удаленный репозиторий:

git push

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

Создание ветки конфигурации на дату

Создадим ветку с состоянием текущей конфигурации на данный день и загрузим его в репозиторий Git.

git checkout -b $(date +"%Y-%m-%d")
git push origin $(date +"%Y-%m-%d")

Собираем все вместе

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

#!/bin/bash

if [ "$#" -lt 2 ]
then
  echo "At least 2 or more parameters should be passed"
  echo "Usage: syncer.sh /path/to/repo /path/to/configs [/path/to/other/configs ...]"
  echo "--"
  echo "Avoid final slashes when specifying config directories if the intention is to"
  echo "have dir rather than dir contents"
  exit
fi

REPO_DIR=$1 # где хранится репозиторий
shift
DIRS=$@ # какие каталоги синхронизуем

echo "Repository is located at "$REPO_DIR
echo "Configuration dirs are located at "$DIRS

HOSTNAME=$(hostname)
TODAY=$(date +"%Y-%m-%d")
HOUR=$(date +"%H")

echo "Hostname is "$HOSTNAME
echo "Today is "$TODAY

cd $REPO_DIR

# выкачаем обновления с сервера
git pull
# переключимся на master ветку, куда добавляем изменения
git checkout master

mkdir -p $REPO_DIR/$HOSTNAME

# добавим новые файлы в репозиторий
rsync -av $DIRS $REPO_DIR/$HOSTNAME/
find $REPO_DIR/* -exec git add {} \;

# сохраним изменения
git commit -a -m "Configuration updated at $(date)"

# отправим изменения на сервер
git push

# если последняя синхронизация в сутках, создадим ветку для сегодняшнего дня
if [ $HOUR = "23" ];
then
  git checkout -b $TODAY
  git push origin $TODAY
  git checkout master
fi

Данный скрипт можно скачать или посмотреть с подсветкой синтаксиса на GitHub.

cd ~
wget https://raw.githubusercontent.com/netpoint-dc/configurations/master/syncer.sh
chmod 755 syncer.sh

Запуск скрипта осуществляется следующим образом:

bash /root/configurations/syncer.sh /root/configurations /etc /usr/local/etc

Для тестирования добавьте пользователя и выполните синхронизацию:

useradd testuser
bash /root/configurations/syncer.sh /root/configurations /etc /usr/local/etc

В репозитории должны быть соответствующие изменения. Теперь остается создать сценарий sync-configs в /etc/cron.hourly, выдать ему права 755 и убедиться в том, что он отрабатывает корректно:

#!/bin/bash
bash /root/configurations/syncer.sh /root/configurations /etc /usr/local/etc

Проверяем работоспособность:

chmod 755 /etc/cron.hourly/sync-configs
useradd testuser2
/etc/cron.hourly/sync-configs

В списке изменений в Git должно отразиться новое изменение.

Добавление файлов и каталогов в список исключения

Иногда Вы может захотеть не добавлять какие-то файлы в синхронизацию. Для этого Вам пригодится файл .gitignore, который Вы можете поместить в корень репозитория. В этом файле Вы можете перечислить каталоги, упрощенные регулярные выражения и файлы, которые не требуется сохранять на сервер. Подробнее о .gitignore можно прочитать в документации Git.

Заключение

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

Таким же образом Вы можете осуществлять и работу со вторичными данными, например, списком установленных в системе пакетов. Например, для Debian, Ubuntu:

dpkg --get-selections > /opt/system-info/packages.lst

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

NB: в рамках этой обучающей статьи использовался публичный репозиторий Git на GitHub. Вы не должны сохранять свои конфигурации в публичный репозиторий. Используйте только репозитории, доступ к которым не предоставляется неавторизованным пользователям.