ccollect という rsync--link-dest オプションによるハードリンクをうまく使って差分バックアップをしてくれるツールでバックアップ設定をしました。

インストール

deb パッケージは存在しないので、まず git clone https://github.com/ungleich/ccollect ~/src/github.com/ungleich/ccollect などで最新 (現時点で 1.0) の ccollect を github のミラーから取得します。

本家 の download ページにある tarball は 0.8 まででちょっと古いです。

共通設定

設定は CCOLLECT_CONF (デフォルトは /etc/ccollect) の中に置いていきます。

共通設定は defaults の中に、バックアップごとの設定は sources の中に置いていきます。

バックアップ保存回数の設定

defaults/intervals の中に適当なファイル名でバックアップの保存回数を設定していきます。

daily などの名前をつけることが多いようですが、 ccollect 自体に毎日自動実行する機能があるわけではないので、自前で cron などを使って実行する必要が有ります。

  • sudo mkdir -p /etc/ccollect/defaults/intervals
  • echo 10 | sudo tee /etc/ccollect/defaults/intervals/daily
  • echo 24 | sudo tee /etc/ccollect/defaults/intervals/monthly
  • echo 10 | sudo tee /etc/ccollect/defaults/intervals/weekly

ここでは日時バックアップと週次バックアップは 10 回分、月次バックアップは 2 年分保存するようにしてみました。

不完全なバックアップの削除

ccollect では構造化された設定ファイルをパースするのではなく、簡単な内容のファイルの中身が設定値になっていたり、ファイルの存在がフラグとなっていたりするようになっています。

ここでは rsync の途中で ssh が切れたなどの理由で不完全なバックアップができてしまった時に削除するフラグを設定します。

  • sudo touch /etc/ccollect/defaults/delete_incomplete

ローカルのバックアップ設定の追加

まず動作確認も兼ねて、ローカルのバックアップを取る設定を追加してみます。

  • sudo mkdir -p /etc/ccollect/sources/$(hostname)-home
  • echo '/home' | sudo tee /etc/ccollect/sources/$(hostname)-home/source
  • echo "/srv/backup/$(hostname)-home" | sudo tee /etc/ccollect/sources/$(hostname)-home/destination

バックアップから除外するファイルも設定してみます。 除外指定ということを明示するために - をつけていますが、つけずにパターンだけでもこの場合は同じです。 exclude ファイルの書式の詳細は rsync "--exclude-from" で検索して調べてください。

  • echo '- *.swp' | sudo tee /etc/ccollect/sources/$(hostname)-home/exclude
  • echo '- *~' | sudo tee -a /etc/ccollect/sources/$(hostname)-home/exclude

今回は関係ないかもしれませんが、 / パーティションなどをバックアップする時にはつけた方が良い --one-file-system オプションも追加しておきます。

  • echo '--one-file-system' | sudo tee /etc/ccollect/sources/$(hostname)-home/rsync_options

サマリー表示を有効にしておきます。 初回実行なので詳細表示も有効にしてみます。

  • sudo touch /etc/ccollect/sources/$(hostname)-home/summary
  • sudo touch /etc/ccollect/sources/$(hostname)-home/verbose

初回バックアップ実行

destination ファイルで指定したバックアップ先ディレクトリは自動作成されないので、手動で作成してバックアップを実行します。 2 回実行してちゃんと差分バックアップになっているのを確認します。

  • sudo mkdir -pv $(cat /etc/ccollect/sources/*/destination)
  • sudo ~/src/github.com/ungleich/ccollect/ccollect daily $(hostname)-home
  • sudo ~/src/github.com/ungleich/ccollect/ccollect daily $(hostname)-home

容量も 2 倍になっていないのを確認します。

  • sudo du -s /srv/backup/$(hostname)-home /home

動作確認ができたので、詳細表示オプションを削除しておきます。

  • sudo rm /etc/ccollect/sources/$(hostname)-home/verbose

リモートからのバックアップ設定の追加

source にリモートホストを設定する以外はローカルの設定と同様に設定していきます。

  • sudo mkdir /etc/ccollect/sources/vps-etc
  • echo /srv/backup/vps-etc | sudo tee /etc/ccollect/sources/vps-etc/destination
  • echo root@vps.example.jp:/etc | sudo tee /etc/ccollect/sources/vps-etc/source
  • echo '- *.swp' | sudo tee -a /etc/ccollect/sources/vps-etc/exclude
  • echo '- *~' | sudo tee -a /etc/ccollect/sources/vps-etc/exclude
  • sudo touch /etc/ccollect/sources/vps-etc/summary

ネットワーク的につながらない時はバックアップが失敗するので、事前チェックするようにします。

  • sudoedit /etc/ccollect/sources/vps-etc/pre_exec
  • sudo chmod +x /etc/ccollect/sources/vps-etc/pre_exec

pre_exec の内容は以下の通りです。 ccollect のドキュメントの Testing for host reachabilty を参考にしています。

#!/bin/sh
set -e
cur_conf_dir="${CCOLLECT_CONF:-/etc/ccollect}/sources/$name"
SRC_HOST=`cat "$cur_conf_dir/source" | cut -d"@" -f2 | cut -d":" -f1`
ping -c1 -q "$SRC_HOST" || exit $?

ssh 設定

セキュリティ的にはあまり好ましくないのですが、バックアップ用に root から root に ssh で接続できるようにします。

まず、バックアップ先のローカルのマシンで root の ssh 用の鍵を作成します。

  • sudo ls -al /root/.ssh で root に ssh の鍵がないのを確認したら sudo ssh-keygen で生成します。存在する場合は別のファイル名で生成して sudoedit /root/.ssh/configIdentityFile を設定しておきます。自動実行で使用するので、パスフレーズは空にしておきます。
  • ssh のポート番号を変更しているなど、別途設定が必要な場合は sudoedit /root/.ssh/config で設定しておくのを忘れないように注意が必要です。
  • sudo cat /root/.ssh/id_rsa.pub で公開鍵を表示してコピーしておきます。

続いて、バックアップ対象の VPS (バックアップ元) の方で ssh を許可する設定をします。

  • sudo install -m700 -d /root/.ssh/root/.ssh がなければ作成します。
  • sudoedit /root/.ssh/authorized_keys で接続を許可する鍵として、先ほどコピーした公開鍵を貼り付けます。
  • 必要に応じて from="pattern-list"no-agent-forwarding,no-user-rc,no-X11-forwarding,no-port-forwarding などの制限も追加しておきます。
  • sudoedit /etc/ssh/sshd_configPermitRootLoginno 以外にします。例えば without-password にしておきます。
  • sudoedit /etc/ssh/sshd_configAllowUsers による制限をしている時は AllowUsers root@接続元IPアドレス を追加しておきます。接続元 IP アドレスが固定ではない場合は、セキュリティ的に弱くなりますが AllowUsers root で許可します。
  • /etc/ssh/sshd_config の設定を変更した場合は sudo service ssh restart で反映させておきます。

設定ができたら、接続元 (バックアップ先のローカルのマシン) から ssh の接続確認をします。

  • sudo ssh root@vps.example.jp hostname などで ssh 接続ができることの確認とホスト鍵の確認を済ませておきます。

初回バックアップ実行

ローカルでのバックアップと同様にバックアップ先ディレクトリを作成してからバックアップを実行します。

  • sudo mkdir -pv $(cat /etc/ccollect/sources/*/destination)
  • sudo ~/src/github.com/ungleich/ccollect/ccollect daily vps-etc
  • sudo ~/src/github.com/ungleich/ccollect/ccollect daily vps-etc

リモートからの一般ユーザー権限でのバックアップ設定の追加

dokku で persistent storage としてボリュームマウントを使っているとファイルの所有者とグループがアプリケーションのデプロイのたびに変わってしまって、差分バックアップに支障が出そうだったので、一般ユーザーでのバックアップも設定しました。

XDG Base Directory SpecificationXDG_CONFIG_HOME を参考にして ~/.config 以下に /etc 以下と同じ構造で設定を作成することにしました。

  • mkdir -p ~/.config/ccollect/defaults/intervals
  • echo 10 > ~/.config/ccollect/defaults/intervals/daily
  • echo 24 > ~/.config/ccollect/defaults/intervals/monthly
  • echo 10 > ~/.config/ccollect/defaults/intervals/weekly
  • mkdir -p ~/.config/ccollect/sources/vps-srv
  • echo /srv/backup/vps-srv > ~/.config/ccollect/sources/vps-srv/destination
  • echo vpsuser@vps.example.jp:/srv > ~/.config/ccollect/sources/vps-srv/source
  • echo '- *.swp' > ~/.config/ccollect/sources/vps-srv/exclude
  • echo '- *~' >> ~/.config/ccollect/sources/vps-srv/exclude
  • touch ~/.config/ccollect/sources/vps-srv/summary

ネットワーク的につながらない時はバックアップが失敗するので、事前チェックするようにします。

  • editor ~/.config/ccollect/sources/vps-srv/pre_exec
  • chmod +x ~/.config/ccollect/sources/vps-srv/pre_exec

pre_exec の内容は以下の通りです。 「リモートからのバックアップ設定の追加」で作成したものと全く同じ内容です。

#!/bin/sh
set -e
cur_conf_dir="${CCOLLECT_CONF:-/etc/ccollect}/sources/$name"
SRC_HOST=`cat "$cur_conf_dir/source" | cut -d"@" -f2 | cut -d":" -f1`
ping -c1 -q "$SRC_HOST" || exit $?

初回バックアップ実行

一般ユーザー権限でバックアップするので、バックアップ先ディレクトリを chown しておきます。

ssh vpsuser@vps.example.jp で一度接続してホスト鍵の確認なども終わらせておきます。

設定ファイルの場所が違うので、環境変数 CCOLLECT_CONF を設定しつつ実行します。

  • sudo mkdir -pv $(cat ~/.config/ccollect/sources/*/destination)
  • sudo chown $(id -u) /srv/backup/vps-srv
  • env CCOLLECT_CONF=$HOME/.config/ccollect ~/src/github.com/ungleich/ccollect/ccollect daily vps-srv
  • env CCOLLECT_CONF=$HOME/.config/ccollect ~/src/github.com/ungleich/ccollect/ccollect daily vps-srv

バックアップ自動実行設定

cron で毎日自動バックアップが動くように設定します。 時間がかかるので、 cron.daily のファイルの中でも最後に実行されるように zz- で始まる名前にしています。 そして、パッケージで入れたファイルと区別できるように local という文字列を名前に入れています。

その際、保存回数が一番多くて保存期間が長い monthly を優先するようにしてみました。

ログ保存用のディレクトリは一般的な debian の流儀に合わせて adm グループのみ読めるようにしています。 install コマンドについては installコマンドでコマンド数を減らす を参考にしてください。

ログは rotate などはせずに全部残して、 tools/ccollect_analyse_logs でエラーや警告があれば cron からのメールとして飛ぶようにしました。 その際、 tools/ccollect_analyse_logs の exit status が grep の exit status そのままなので、エラーの有無と逆の意味に感じられてしまうので、反転するようにしました。

  • sudoedit /etc/cron.daily/zz-local-ccollect
  • sudo chmod +x /etc/cron.daily/zz-local-ccollect

/etc/cron.daily/zz-local-ccollect:

#!/bin/sh
INTERVAL=daily
if [ 7 = "$(date +%u)" ]; then
  INTERVAL=weekly
fi
if [ 01 = "$(date +%d)" ]; then
  INTERVAL=monthly
fi
mkdir -p /var/log/ccollect
LOGDIR="/var/log/ccollect"
LOGFILE="$LOGDIR/$(date +%Y%m%d-%H%M).log"
LOCALUSER="localuser"
CCOLLECT_DIR="/home/$LOCALUSER/src/github.com/ungleich/ccollect"
install -m750 -oroot -gadm -d "$LOGDIR"
{
  su - "$LOCALUSER" -c 'env CCOLLECT_CONF=$HOME/.config/ccollect '"$CCOLLECT_DIR"'/ccollect -a '"$INTERVAL"
  "$CCOLLECT_DIR/ccollect" -a "$INTERVAL"
} >"$LOGFILE" 2>&1
if /bin/sh "$CCOLLECT_DIR/tools/ccollect_analyse_logs" "we" < "$LOGFILE"; then
  # found
  exit 1
else
  # not found
  exit 0
fi

リモートの dokku の home のバックアップ設定

他の設定例として、リモートの dokku の home のバックアップ設定もしてみました。 設定が似ている vps-etc を雛形としてコピーして destinationsource などを書き換える形で設定しました。

  • cd /etc/ccollect/sources
  • sudo cp -a vps-etc vps-home
  • sudoedit vps-home/destination/srv/backup/vps-home に変更
  • sudoedit vps-home/sourceroot@vps.example.jp:/home に変更
  • sudoedit vps-home/exclude- cache を追加 (/home/dokku/$APP/cache/ は buildpack での build 時などのキャッシュに使われるのと、ファイルの所有者とグループがどんどん変わるので、バックアップからは除外)

初回バックアップ実行

vps-etc のバックアップと同様にバックアップ先ディレクトリを作成してからバックアップを実行します。

  • sudo mkdir -pv $(cat /etc/ccollect/sources/*/destination)
  • sudo ~/src/github.com/ungleich/ccollect/ccollect daily vps-home
  • sudo ~/src/github.com/ungleich/ccollect/ccollect daily vps-home

uid, gid 問題

LDAP などでアカウントを共通化していれば問題ないのですが、 rsync では uid や gid を数値のまま保存してコピーするので、バックアップ元とバックアップ先で同じ uid に対して別のユーザーが存在すると、意図しないユーザーが読めるバックアップができてしまうので、注意が必要です。

この記事の例だと /srv/backup/vps-home のパーミッションを変更する (sudo chmod 700 /srv/backup/vps-home) などの対処をしておくと良いと思います。

バックアップの差分の確認

ccollect.text の Comparing backups によると rsync -n -a --delete --stats --progress daily.20080324-0313.17841/ daily.20080325-0313.31148/ のように -n オプション付きで rsync を実行することによってバックアップの差分を確認できるようです。

まとめ

ccollect で差分バックアップを作成するようにしました。

rsync によるバックアップなので、圧縮などもするバックアップツールと違い、バックアップの内容も元のディレクトリ構造そのままでわかりやすいので、一部だけ復元するなどの操作も素直に実行しやすくなっています。

ハードリンクなので i-node は消費しますが、変化がないファイルについては容量を消費しないので、バックアップサイズも抑えられます。

Disqus Comments

Kazuhiro NISHIYAMA

Ruby のコミッターとかやってます。 フルスタックエンジニア(って何?)かもしれません。 About znzに主なアカウントをまとめました。

znz znz


Published