目次

バックアップ

データのバックアップが如何に重要であるかは、今更説明するまでもないでしょう。 しかし、実際にデータのバックアップを行なっているヒトは、意外と少ないかもしれません。 重要性を理解しながらも、「面倒くさい」「自分のところは大丈夫」などという理由で、これを行なわないというケースが多々あるのではないかと思います。

データは失われれば、戻りませんが、なくなってみるまで、その実際に意味するところというのは中々わからないのかもしれません。

データが実際に丸ごと失われてしまう……そういう事態は、なるほど、確かに、それほど頻繁には起こらないものかもしれません。しかし、うっかりミスで、ファイルの内容を修復できないくらいに書き換えてしまったりすることなんていうのは、そう珍しくなくあるものではないでしょうか? バックアップはこういう場面でもファイルの内容を取り戻すのに利用することが出来ます。

まずは、バックアップを取ることから始めましょう。ここでは、手軽にバックアップを取り、また、簡単に内容の一部、或いは全部を戻せることを念頭に、具体的な方法を説明します。

バックアップを取る

バックアップを取るのに利用されるコマンドといえば、dump/restoreあるいは tar、cpioなどが一般的でしょうか? また、rsyncもミラーリングのみならず、バックアップ目的に利用されています。複数世代のバックアップを効率よく取ることを考えると、バックアップは差分バックアップが行なえることが好ましいです。dumpコマンドはこうした欲求に対応しているので、最初に候補に挙がりますが、バックアップされたデータの一部を直接参照したりするのが面倒ですので、ファイルの内容の一部を取り戻したいなんていう要求にこたえるのは中々に面倒です。tar, cpioも同様です。

rsyncはディレクトリ構造を丸ごと、ミラーするために利用されるので、バックアップ先にあるファイルは、元にあるファイルと同じようにアクセス出来ますので、バックアップをより弾力的に、有効に活用できます。但し、差分バックアップを作るには一工夫が必要です。単純に行なえば、ただ、複数のミラーが出来てしまうだけになるからです。

ここでは、一工夫することで、差分バックアップを行なえ、かつ、バックアップされているデータの利用により弾力性のある rsyncを使ってバックアップを取ることを考えます。

rsyncで差分バックアップを取る

先に書いたように、単純にrsyncを複数世代で行なうと、世代数分の丸ごとコピーが出来上がってしまいます。バックアップに要する時間の点からも、そして何より、ディスクスペースの問題からも、好ましいこととはいえません。そこで差分バックアップを取ることになります。

rsyncで差分バックアップを実現する仕掛けは、ファイルのリンクにあります。rsyncをするときに、一世代前のバックアップデータへのハードリンクを作成しておき、変更のあったものだけを、上書きしていくことで、自動的に差分バックアップが行なえます。しかも、差分であるはずの最新世代のバックアップディレクトリにも、ファイルのリンクによって完全なバックアップイメージが存在しているのです。

2.5.6以前のrsyncには、自力でこの処理を行なう機能がありませんでしたので、rsyncに先立ち、cp -alを使って、バックアップ世代間のファイルのリンクを行なっておく必要がありましたが、2.5.6以降のrsyncでは、–link-dest というオプションによって、指定されたディレクトリにあるファイルにリンクを張りながら、必要に応じてファイルを上書きする処理をしてくれます1)

以下に、この –link-destをサポートしたrsyncを使ってファイルをバックアップするスクリプトの例を示します。

#!/bin/sh

# daily backup script for paprika

DIR="/backup/backup."
SRCS="/etc /var /home"
DST="${DIR}0"

# rotate backup
n=4
rm -rf "${DIR}${n}"
while [ ${n} -gt 0 ]; do
        m=`expr ${n} - 1`
        if [ -d "${DIR}${m}" ]; then
                mv "${DIR}${m}" "${DIR}${n}"
        fi
        n=${m}
done
mkdir ${DST}
for src in ${SRCS}; do
    /usr/bin/rsync -abz --delete --link-dest=../backup.1 ${src} ${DST}
done

この例では、/etc, /var, /homeの内容を順に、/backup/backup.0 の下に作成します。バックアップに先だち、世代交代の作業をしています。mv “${DIR}${m}” “${DIR}${n}“で行なわれている処理がそれです。 最古のバックアップは rm -rf “${DIR}${n}“でがっさりと削除されます。それでも、各世代間にまたがっているファイルはリンクという形で共有されているので、この作業で失われることはありません。

各ディレクトリは、完全な形でバックアップを含んでいますので、ファイルでもディレクトリツリーでも自在にアクセスすることが出来ます。うっかり手元のファイルの更新を失敗して、内容を修復不能なほどに壊してしまっても、適切なバックアップからそれを取り出すことで簡単に回復できます。

毎日、一世代ずつバックアップを作成するならば、昨日、ファイルを壊してそのままにして、最新のバックアップには壊れたファイルがコピーされてしまっても、前日分のバックアップから壊れる前のファイルが入手できますので、安心です。

また、各ディレクトリに固有のファイルは、更新されたファイルだけで、その他の、更新されていないファイルに関しては、リンクという形で共有されているので、ディスクスペースはフルバックアップ一世代分に加えて、差分の分しか消費されません。

あとは、例のスクリプトを /etc/cron.daily/backup.shとでもして置いておけば、毎朝4時ごろになると、差分バックアップが自動的に行なわれるようになります。たったこれだけの手間で、毎日、きちんとバックアップがされるようになるのです。このヒト手間、惜しむのは馬鹿馬鹿しいと思いませんか?

バックアップの復元

丸ごとリストアするなら、rsyncの先と元をひっくり返して呼んでやればいいだけです。このとき当然ですが、–link-destは外します。

# rsync -abz --delete /backup/backup.0/home /
(/homeをリストアする例)

一部のファイルやディレクトリを戻すのであれば、cp -pでもtarでも好きなものを使えばいいですし、ファイルの内容の一部を知りたいだけなら、lessでもviでも適宜利用すればいいでしょう。ファイルは、完全な形でそこにコピーされているのですから。

リモートにSSH経由でバックアップを作成する

バックアップをリモートにとる方法はいくつかある。一番簡単なのはNFSでリモートのディレクトリをマウントしてしまう方法だろう。これだと、特にリモートであることを意識する必要は無い。

しかし、NFSで共有されたディスクは、安全面で脆弱であり、また、ネットワークをまたいだ遠方との間では実用的ではない。

rsyncdを使う方法も考えられるが、通信路の安全性を確保するために、ここではSSH経由で同期することを考えたい。

上記の、バックアップスクリプトの流れに沿って、次の手順で処理を実行することとする。

  1. バックアップディレクトリのローテート
  2. rsyncの実行

以下に、実現の手順を説明する。

SSHDの設定変更

一般的に、SSHはrootのアクセスを認めない。安全面から考えれば当然であるが、それではバックアップ出来ないので、特定の場合にのみこれを許可する設定を、バックアップされる側(リモート側)に設定する。

/etc/ssh/sshd_config の次のパラメータを変更する。

PermitRootLogin forced-commands-only

'forced-commands-only' とすると、authorized_keys ファイルに登録された公開鍵に紐づけられているコマンドのみ実行可能となる。つまりは、SSHで接続してきても何でも実行出来るわけではないということである。

キーファイルの作成と登録

特定のコマンドのみ受け付ける条件でSSHを通すので、今回は二本立てで処理を行うことになる。つまり、バックアップディレクトリのローテーションと、rsyncの本体だ。

このため、キーファイルも二つ必要となる。多世代バックアップではなく単純に最新のバックアップのみをとり続ける場合には、rsyncの設定のみで問題ない。

鍵の作成はバックアップ元のホストで行う。

# ssh-keygen -t ecdsa -N "" -f ~/.ssh/rotatedir
# ssh-keygen -t ecdsa -N "" -f ~/.ssh/rsync

パスフレーズを空にすることで、バッチ処理で利用可能とする。呉々も鍵の管理は厳重にする必要がある。

~/.ssh には rotatedir, rotatedir.pub, rsync, rsync.pub の四つのファイルが出来ているはずである。このうち、rotatedir.pub と rsync.pubを、リモート側の ~/.ssh/authorized_keys に転記する。

command="/bin/ls" ecdsa-sha2-nistp256 AAAA...== root@host
command="/bin/ls -l" ecdsa-sha2-nistp256 AAAAB...== root@host

command=”…“はそれぞれの公開鍵の前に挿入しておく。このコマンドが、ssh 接続したときに実行されるコマンドになる。今は、暫定的に ls にしておく。もちろん本番ではこれを変更する。

設定が出来たら、ローカルのホスト側からリモートホストへ ssh をしてみる。

# ssh -i ~/.ssh/rsync root@remote
# ssh -i ~/.ssh/rotatedir root@remote

それぞれ、ls と ls -l の結果が見えればOKである。初回のみ、リモートホストの妥当性について確認されるかも知れないが、問題なければ yes とこたえておけばよい。

コマンドラインの確認

バックアップは、ディレクトリのローテーションとrsyncの二本立てである。まずは、ディレクトリのローテーションをするスクリプトをリモート側に作成する。

ディレクトリのローテーション

/root/bin/rotatedir.sh であると仮定する。

#!/bin/sh

TARGET=$1
if [ -z "${TARGET}" ]; then
    TARGET="localhost"
fi
DIR="/var/backup/${TARGET}/backup."

DST="${DIR}0"

# rotate backup dir
n=4
rm -rf "${DIR}${n}"
while [ ${n} -gt 0 ]; do
    m=`expr ${n} - 1`
    if [ -d "${DIR}${m}" -a ! -d "${DIR}${n}" ]; then
        mv "${DIR}${m}" "${DIR}${n}"
    fi
    n=${m}
done
mkdir ${DST}

こんな感じのスクリプトを作成し保存、実行可能としたら、rotatedir.pub の方の commandを command = ”/root/bin/rotatedir.sh” に変更する。

rsyncのコマンドライン

こちらは、command = ”/bin/ls”などのままで、まず、一回、rsync を実行してみる。その際、コマンドラインに -vv オプションをつけておく。

[root@local ~]# /usr/bin/rsync -vv -abz -e "ssh -i /root/.ssh/rsync" --delete --link-dest=../backup.1 /home root@remote:/var/backup/local/backup.0
opening connection using: ssh -i /root/.ssh/rsync -l root remote rsync --server -vvblogDtprze.iLsf --delete --link-dest ../backup.1 . /var/backup/local/backup.0
protocol version mismatch -- is your shell clean?
(see the rsync man page for an explanation)
rsync error: protocol incompatibility (code 2) at compat.c(174) [sender=3.0.8]
[root@local ~]# 

こんな感じで失敗するはずなので、失敗したら、ssh -i の行に着目して、rsyncから後ろの部分から vv を取っ払ったものを、リモート側の authorized_keys の command の部分に転記する。即ちこんな感じになる。

command="/root/bin/rotatedir.sh" ecdsa-sha2-nistp256 AAAA...== root@host
command="rsync --server -blogDtprze.iLsf --delete --link-dest=../backup.1 . /var/backup/local/backup.0" ecdsa-sha2-nistp256 AAAAB...== root@host

バックアップ

あとは、ローカル側からディレクトリのローテーションとrsyncを呼び出すだけである。

#!/bin/sh
SRCS="/etc /home /var /root"
DST=/var/backup/local/backup.0
ssh -i /root/.ssh/rotatedir remote
for src in ${SRCS}; do
    /usr/bin/rsync -abz --delete --link-dest=../backup.1 ${src} root@remote:${DST}
done

などとしておけばよい。

VineLinuxに関してへ戻る。

1)
VineLinux 3.1では、この記事を書いている時点で2.6.3がインストールされています。もちろん、–link-destはサポートされています。