home / vps / bkup バックアップ

サービス選定〜OSのインストール
サーバー類のインストール
nginx
さくら de Ubuntu
サーバーの引越
>>> バックアップ
Let's Encrypt

 元々OpenSUSEが入っていたAblenetのVPSを、さくらのVPSのバックアップ機として使うためUbuntuに入れ替え、rsyncで自動バックアップをとるようにしてみました。

home / vps / bkup OpenSUSEからUbuntuへ入れ替え

 SSHの鍵を変更したくない場合はバックアップしておく。 まぁ、/etc以下と/usr/local/etc以下を丸ごとバックアップしておくのがよいのではないだろうか。

 とりあえずインストールメディアでブートする必要がある。 Ubuntuのサイトから Downloads → Alternative Downloads → Download the network installer for 18.04 LTS → amd64 と進んで、ubuntu-installer/amd64からlinuxinitrd.gzをダウンロードする。 あとはGRUBを設定してブートする

 さくらのUbuntuメディアは必要な設定がほぼすべて、あらかじめ設定された状態でインストールされるようになっているが、今回は一般のインストールメディアなので、若干手順が違う。

 初回ブート後にも少し作業が必要。

home / vps / bkup /usr/local/etcとgit

 あとは正サーバーと同じように設定していくのだが、正サーバーでは/usr/local/etcをgitで管理しているので、まずはこれをcloneしてしまう。 /usr/local/etc/.gitのパーミッションに注意。 root:wheel 750 g+sとかにしておくのが無難。 cloneしたらブランチを作って、バックアップサーバー(副サーバー)にのみ適用する変更を加える。

 実際の運用では修正→コミット→fetch→マージ、という手順になるが、マージには方向性があるので気をつける。 つまり、今の例では正サーバーのリポジトリを副サーバーにcloneして、副サーバー用の変更を加えているから、正サーバー側で副サーバーの変更をマージすると、副サーバー用の変更が全部適用されてしまう。 この例では、正サーバーから副サーバー向きはマージ、副サーバーから正サーバーにはcherry-pickになる。

 副サーバーで設定の変更をテストして、それを正サーバーにも適用する場合、色々方法はあるが、面倒でも副サーバー側で作業ブランチを作り、そこに変更をコミットして、正サーバーでfetchしたあと、副サーバーのメインブランチから作業ブランチへの差分をcherry-pick、副サーバーに戻ってメインブランチに切り替えて、正サーバーの変更をfetchしてマージするのがいいと思う。 作業が終わったら作業ブランチは消してしまう。

 正サーバー専用の変更がある場合は、副サーバーにマージしてしまって、コミットをrevertするのがいいと思う。 こうしておけば正サーバー側はブランチを切らずに作業できる。 こういうルールを決めておかないと、あとでマージがにっちもさっちもいかなくなる。 まぁ全部cherry-pickで運用するのが一番安全かもしれない。

 マージ・cherry-pickを行う場合、普通は一度ローカルにブランチをチェックアウトすると思うが、実はその必要はない。 git merge origin/masterのようにすれば、リモートのブランチをいきなりマージすることができる。 正サーバー用のローカルブランチは消してしまってかまわない。 混乱の元なのでむしろ消してしまったほうがよい。 というのは、ローカルブランチにチェックアウトしてマージする場合、マージが必要ない限り副サーバー上で正サーバー用のブランチをチェックアウトすることはないから、マージ作業がないと副サーバー上の正サーバー用ローカルブランチはちょっと古いものになる。 これを他の機械でcloneすると中途半端な正サーバーのブランチをcloneしてしまう。 ローカルブランチを消してしまっても、pushするときにpruneしてしまわなければ問題ない。 というか、fetchで運用するので基本的にpushしないし、正サーバーでは正サーバー用のブランチがチェックアウトされた状態になっているから、pruneできないはず。

 お互いfetchしあうことでバックアップの代わりにもなる。 なお、ローカルブランチを作らない(clone後に消す)運用をしている場合、clone時にはローカルブランチしかcloneされないため、副サーバーからさらにcloneしたリポジトリには正サーバーのブランチはリモートブランチとしてすら存在しないことに注意。 正サーバーのブランチが欲しければ、正サーバーからもcloneする必要がある。

 設定を変更しないといけないのは主にMTAだと思われる。 転送専用の設定(virtualdomainsを削除、localsを空ににし、必要に応じてsmtproutesを設定)にする。 あと、基本的にユーザーディレクトリがないので、Webサーバーの設定も少し変える必要がある。 DNSはそのうち書こうと思っているLet's EncryptのACMEチャレンジゾーン以外は全部同じ設定で大丈夫。 というか、少なくともゾーンファイルは同じにしなければならない。

home / vps / bkup バックアップ

 先に結論。 rsync最強。 要点は、--link-destオプションでハードリンクを使って差分バックアップすることと、fake superを使うこと。

 ハードリンクを使うと、ディレクトリツリーを丸々構成したまま、変更のないファイルにはハードリンクを張るので、データの実体はひとつだけになる。 100個ファイルがあり、90個は更新がなく、10個は更新された場合、ディレクトリツリーとしては100個のファイル×2バージョン分が見えるが、実体としては元の100個+更新された分の10個、合計110個分だけしかディスク容量を消費しない。 また、途中のバックアップが不要になったら、そのディレクトリだけrm -rfしてしまえばよい。 たとえば、1月1日から毎日バックアップをとり続け、2月になったら1月分を全部消したとする。 この場合、1月3日に作成したファイルを1月29日に消したような場合はさすがに残らないが、2月までそのファイルが残っていれば、1月分を消してしまっても問題ない(リンクカウントが1になってちゃんと残る)。

 ちなみに新しくバックアップされたファイルはfind . -links 1で分かる。

 fake superを使うと、バックアップ先にrootでログインしなくてよくなる。 バックアップ元ではすべてのファイルを読む必要があるから、バックアップはroot権限で実行する必要がある。 それを書き込む先でディレクトリツリーをユーザーIDも含めて再構成しようとすると、バックアップ先でもroot権限が必要になる。 しかし、sshでrootログインを禁止しているから、ログインしてからスーパーユーザーになる必要があるが、パスワードで困ってしまう。

 fake superではバックアップ先のログインユーザーでファイルを作成し、ファイルの拡張属性にアクセス権の情報を格納する。 拡張属性はattr -g rsync.%stat filenameとすると得られる。

 具体的な作業はこんな感じ。

 あとはcronで定期的に実行すればよいが、mainhost側でこんなスクリプトを実行している。

#!/bin/bash
: ${user:=bkupuser}
: ${host:=bkuphost}
: ${module:=mainhost}
: ${sshkey:=~/.ssh/keyprefix-${host}}
: ${prevfiledir:=/var/rsync-backup}
: ${prevfilename:=prior-dir}
: ${prevfile:=${prevfiledir}/${prevfilename}}
: ${ssh:=ssh}
: ${sshcmd:=$ssh -i $sshkey -l $user}
: ${rsync:=rsync}
: ${rsyncopt:=--bwlimit=1M}

destdir=$(date +%Y%m%d)
rsynclinkdest=""
if [ -f ${prevfile} ]; then
    if [ "$(cat ${prevfile})" != "/${destdir}" ]; then
        rsynclinkdest="--link-dest=$(cat ${prevfile})"
    fi
fi

# create parent directory in the target host
$rsync --rsh="$sshcmd" $rsyncopt . $host::$module/$destdir/

while read from to; do
    if [ ! -z "$rsynclinkdest" ]; then
        linkdest=$rsynclinkdest/$to
    fi
    $rsync --rsh="$sshcmd" -azH $rsyncopt $linkdest $from $host::$module/$destdir/$to
done
echo "/$destdir" > $prevfile

 このスクリプトは

/home/bkupuser/mainhost/20190523/etc
のようなディレクトリにバックアップすることを想定している。

 rsynclinkdest変数は差分バックアップが可能ならば--link-destオプションが設定される。 初回バックアップ時は空になる。 バックアップに成功すると/var/rsync-backup/prior-dirにバックアップ先のディレクトリ名が書き込まれ、次回バックアップ時にこのファイルの内容が--link-destとしてrsynclinkdest変数に設定される。 最終的に転送先のサブディレクトリが追加されてrsyncのオプションに指定される。 rsyncはこのディレクトリを基準に転送対象のファイルを比較し、変更がなければハードリンクで済ませる。 つまり、ハードリンクによる差分バックアップを行う。

 次に一度rsyncコマンドを実行しているが、これは中間ディレクトリ(20190523の部分)を作成している。 rsyncは中間部分のディレクトリは作ってくれないが、末端のディレクトリは作ってくれるらしく、こうするとディレクトリだけ作ってくれるらしい。 mkdir -pに相当するオプションはないのかな? 見落としてるだけな気もするけど。

 while read ...からがバックアップ本体。 標準入力から1行にバックアップ元ディレクトリ、バックアップ先ディレクトリを書いたファイルを突っ込む。 たとえば、

/etc/ etc
/usr/local/etc/ usr.local.etc
のように書く。 これで/usr/local/etcはモジュールで指定されたディレクトリの20190523/usr.local.etc以下にバックアップされる。 バックアップ元の最後にスラッシュをつける必要がある。 付けないと20190523/usr.local.etc/etcにバックアップしようとする。

 -aはアーカイブ、-zは圧縮転送、-Hはハードリンクをハードリンクとして転送。 rsyncopt変数に設定してある--bwlimit=1Mは転送レートをバイト/秒単位で指定している。 module変数に設定してある値は、先のrsyncの設定ファイルで[mainhost]と書いてある部分に対応する。 実際の転送先や、アクセスを許すホストなどが設定してある。

 最後に今回のバックアップ先ディレクトリを/var/rsync-backup/prior-dirに記録しておく。 これで次回から差分バックアップが走る。


Copyright (C) 2019-2020 akamoz.jp

$Id: backup-server.htm,v 1.6 2020/02/06 16:06:27 you Exp $