システム設定ファイルをGitで管理する

サーバーの設定ファイルを履歴管理できないだろうか

多くの人が漠然と考えていたであろうアイデアを[ポエム](http://qiita.com/a-suenami/items/094761708b73c53adc54)であらわしたのが、
[Linuxサーバ全体をソース管理するアイディアについて](http://qiita.com/greencoffeemaker/items/fe88994baf9861baa8ce)でした。詞藻（詩の才能）があるって素敵なことですね。

私は以前からサーバーの設定ファイルを履歴管理することを試みていました。
しかし、その意義や目標はこちらのポエムに教えてもらった気がします。

そんなわけで、サーバーの設定ファイルをGitで管理する手法を考えたいと思います。

## 設定ファイルをコピーしてから編集する習慣
サーバーの設定ファイルを編集する場合、どのような方法をとっているでしょうか。
たとえば、Apacheの設定ファイル「/etc/apache2/httpd.conf」を編集する場合を考えます。
直接「/etc/apache2/httpd.conf」を編集する場合、さまざまな問題が発生します。

* 編集にroot権限が必要なので、ふだんの環境で編集できない
* 記述を間違えた場合、別途元のファイルをバックアップしておくなどの手段をとっていないと、戻せなくなる可能性がある

そのため、設定ファイルをどこか別の場所にコピーしてから一般ユーザーの権限で編集し、編集完了後に元の場所に戻しているという方も多いでしょう。このとき、/tmpなど一時ファイルディレクトリーにコピーするのも一つの考えです。一般ユーザーの権限でファイルを編集し、設定ファイルを元の場所にコピーし直すときにだけroot権限が必要になります。でもその場合、記述を間違えた場合にファイルを元に戻せない可能性は残ります。

また、いつ誰が編集したのかという痕跡を残す意味でもコピーした設定ファイルは残しておく価値があります。

そういうわけで、設定ファイルは一時ファイルのディレクトリーではなく、決まった場所に恒久的に残しておいた方がよいでしょう。私は、ホームディレクトリに「<サーバー名>\_conf」というディレクトリを作成し、その中に保存するようにしています。たとえばサーバー名が「hoge」の場合、設定ファイルのディレクトリは「~/hoge_conf」です。

また、1つのディレクトリにサーバーの設定ファイルを全部入れると整理が難しいので、次のようなサブディレクトリを作成します。

* env - 環境変数などの設定ファイル
* system - OS全体に影響するファイル。/etc/fstab、/etc/passwdなど
* apache - Apacheの設定ファイル

設定ファイルのコピーを恒久的に保存しておくとき、もう一つ悩ましいことに気がつきました。「httpd.conf」のような設定ファイルの名前は記録されるものの、元のファイルの場所がどこだったかわからなくなるのです。
そこで、ファイルをコピーするときにはファイルのフルパスの「/」を「!」に置換したファイル名にします。
「/」を「!」に置換したファイル名にするのは、Emacsエディタがバックアップファイルを作成するときの挙動を真似しています。

結果として、「/etc/apache2/httpd.conf」は、「~/hoge_conf/apache」ディレクトリに「!etc!apache2!httpd.conf」ファイルとしてコピーして保存することになります。

```
cp -p /etc/apache2/httpd.conf '~/hoge_conf/apache/!etc!apache2!httpd.conf'
```

なおファイル名に「!」をつけるときは、ファイル名全体を「'」や「"」で囲むか、「!」の部分を「\\!」と指定する必要があります。

## Gitでの設定ファイルの管理
ここまで述べてきたような設定ファイルをコピーしてから編集する習慣をつければ、
Gitでの設定ファイルの管理の管理ができるようになります。
具体的な手順は次のとおりです。

1. 設定ファイルを保存するディレクトリを作成し、Gitレポジトリとして設定
2. サブディレクトリの作成
3. 設定ファイルのコピーとコミット
4. 設定ファイルの編集
5. 元の場所へのコピーと動作確認
6. 編集した設定ファイルのコミット

### 設定ファイルを保存するディレクトリを作成し、Gitレポジトリとして設定
設定ファイルを保存するディレクトリを作成します。できれば、サーバーにOSをインストールした直後に行います。

```
mkdir hoge_conf
```

Gitをインストールし、設定ファイルを保存するディレクトリをGitレポジトリとして設定します。

```
cd ~/hoge_conf
git init
```

### サブディレクトリの作成
設定ファイルを保存するディレクトリのなかに、サブディレクトリを作成します。
たとえばApacheの設定をするときは、Apacheの設定ファイルを保存するディレクトリを作成します。

```
mkdir ~/hoge_conf/apache
```

### 設定ファイルのコピーとコミット
設定ファイルをサブディレクトリにコピーします。
たとえば、Apacheの設定ファイル「/etc/apache2/httpd.conf」を編集するときは、次のようにコピーします。

```
cp -p /etc/apache2/httpd.conf '~/hoge_conf/apache/!etc!apache2!httpd.conf'
```

コピーしたら、Gitでそのファイルをコミットします。

```
cd ~/hoge_conf/apache
git add !etc!apache2!httpd.conf
git commit !etc!apache2!httpd.conf -m "現状の/etc/apache2/httpd.confを追加"
```

### 設定ファイルの編集
コピーとコミットが済んだら、設定ファイルを編集します。
元の場所で直接編集する場合と違い、root権限の必要がないので、好きなエディタや編集方法が使えます。

### 元の場所へのコピーと動作確認
設定ファイルの編集が完了したら、元の場所にコピーします。

```
sudo cp '!etc!apache2!httpd.conf' /etc/apache2/httpd.conf
```

コピーしたら、動作を確認します。Apacheの場合、Apacheを再起動します。

```
sudo /etc/init.d/apache2 restart
```

### 編集した設定ファイルのコミット
動作を確認したら、編集した設定ファイルをコミットします。

```
git add !etc!apache2!httpd.conf
git commit !etc!apache2!httpd.conf
```

#### 問題があった場合
一方、Apacheの動作に問題が起きた場合などは、設定ファイルを最初にコピーしたときの状態に戻し、
元の場所にコピーしなおします。

```
git checkout !etc!apache2!httpd.conf
sudo cp '!etc!apache2!httpd.conf' /etc/apache2/httpd.conf
```

## 作業の自動化
上記のような作業で、設定ファイルの履歴は取得できるようになりました。
しかし、設定ファイルを編集するたびにcpやgitのコマンドを実行するのはやや面倒な気もします。

次のようなシェルスクリプトを作成しておけば、こうした作業を省力化できます。

```
CP=cp
CPFLAGS=-p
ECHO=echo
HOSTNAME=hostname
SED=sed
TR=tr
CD=cd
PWD=pwd
GIT=git

STARTDIR=`${PWD}`
CONFDIR=~/`${HOSTNAME} -s`_conf
DSTFILE=`${ECHO} $1 | ${SED} -e "s#/^$HOME#!HOME#" -e 's#^~#!HOME#' | ${TR} '/' '!'`

if test ! -d ${CONFDIR}; then
    ${ECHO} "${CONFDIR} is not exist." >&2;
    exit 1;
fi
if test -z $2; then
    ${ECHO} "Set conf class as arg 2." >&2;
    exit 1;
fi
if test ! -d ${CONFDIR}/$2; then
    ${ECHO} "$2 in ${CONFDIR} is not exist." >&2;
    exit 1;
fi
${CD} ${CONFDIR}/$2
${ECHO} "pwd: ${PWD}"
${CP} ${CPFLAGS} $1 "${DSTFILE}"
${GIT} add "${DSTFILE}"
${GIT} commit "${DSTFILE}" -m "現状の$1を追加"
${CD} ${STARTDIR}
```

このファイルを```~/bin/cp2conf```として実行権限をつけて保存します。```~/bin```にPATHが通っていれば、次のコマンドで設定ファイルのコピーとGitのコミットを実行できます。

```
cp2conf /etc/apache2/httpd.conf apache
```

## 他の設定ファイル管理方法との関係

前述のポエム「Linuxサーバ全体をソース管理するアイディアについて」ではコメント欄で、LVMやbtrfsなどファイルシステムの機能でスナップショットを取ることや、Dockerを使うことが提案されていました。
スナップショットなどを使う場合は、設定ファイルを漏れなく管理できるメリットがあります。サーバーのバックアップを取得したり、仮想化してスナップショットを取得することにも、同様のメリットがあるでしょう。
一方で、設定の意図や詳細な履歴はGitを使った方が管理しやすいように思います。
なので、サーバーの設定ファイルを管理するときは、スナップショットなどと、Gitによる設定ファイルの履歴管理を併用するのがいいんじゃないでしょうか。

[etckeeper](http://heartbeats.jp/hbblog/2013/09/etckeeper.html)は、実際に試したことはありませんが、「/etc」以外の場所にあるファイルも管理できないと実用は難しいんじゃないかと思っています。

## まとめ
工夫すれば、サーバーの設定ファイルはGitを使って履歴管理できます。