2011/09/18

ゲストマシンをディスクイメージとしてバックアップする

そもそもゲストマシンはディスクイメージとして存在しているので、ホストマシンがそのファイルをコピーするだけで良いのですが、スパースファイルを再構成することで一度大きくなってしまったディスクイメージを小さくしましょう、という意図があります。
(SSDが潤沢にあればこんなことしなくて良いのに!)

また、ホストマシン側は 4GBのSSDしか使用できない環境なので、今回はゲストマシン側だけで何とかします。
(SSDが潤沢にあればこんなことしなくて良いのに!!)

なお、このblogは例によってメモ for 未来の私 です。

ゲストマシン環境
ディスクマウント状態はこんな感じ
/dev/xvda3 /dataは SDHCです。
バックアップ対象は /(/dev/xvda1) だけです。
バックアップ保存先は /data(/dev/xvda3) です。
/dev/xvda1 none swap sw 0 0
/dev/xvda2 / ext3 rw,noatime,nodiratime,errors=remount-ro 0 0
/dev/xvda3 /data ext3 rw,noatime,nodiratime 0 0

ホストマシン側から見ると
/dev/xvda1 と /dev/xvda2 は EeePC 4G-X 上のSSDに保存されている ディスクイメージファイル(スパースファイル)です。
/dev/xvda3 は外付けSDHC(/dev/sdc1)です。
ホストマシン側でマウントしているデバイスはゲストマシンでマウントできないため、ホストマシンは /dev/sdc1はマウントしていません。
仮想マシン起動用のcfgはこんな感じ(抜粋)
disk = [
  'file:/data_local/xen/domains/alpha/disk.img,xvda2,w',
  'phy:/dev/sdc1,xvda3,w',
  'file:/data_local/xen/domains/alpha/swap.img,xvda1,w',
]

バックアップ手順
1. ディスクイメージ作成
2GBのスパースファイルを作成します。
作成先は /data/tmp/disk.img です。
# dd if=/dev/zero of=/data/tmp/disk.img bs=1024M seek=2 count=0

2. ファイルシステム作成
作成したディスクイメージに ext3ファイルシステムを構築します。
対話的に y/n を聞かれることがあるので、自動的に y を答えるようにします。
# echo "y" | /sbin/mkfs -t ext3 /data/tmp/disk.img

3. 作成した空ディスクをマウント
ループバックデバイスとして作成したディスクを /mnt/tmp にマウントします。
# mount -o loop /data/tmp/disk.img /mnt/tmp

4. コピー
rsyncを使用して、/(ファイルすべて) を /mnt/tmp にコピーします。
ただし、SDHCである /data/ と マウントしている /mnt/tmp/ は除外します。
90秒スリープしているのはバッファの内容を書きこむのを待っているため。sleepしなくても良いのですが、なんとなく。
# /usr/bin/rsync -Sax / /mnt/tmp --exclude "/data/" --exclude "/mnt/tmp/"
# sleep 900

5. アンマウント
マウントを解除します。
# umount /mnt/tmp

6. GNU tarでアーカイブ
gzip や bzip2 では sparseとしての扱いが出来ずに、今回であれば 2GBのファイルとして扱ってしまいますが、sparseファイルが扱える GNU tar (Sオプション)を使い、/data/tmp/disk.img から /data/Dropbox/backup/disk.img.tgz を作成します。
# cd /data/tmp
# tar -Szcf /data/Dropbox/backup/disk.img.tgz ./disk.img

/data/Dropbox は Dropbox同期ディレクトリになっているので、ここに保存した disk.img.tgz はDropboxクラウドやLAN同期を経由して手元のMacにも転送されます。
これでバックアップは終了です。


これらバックアップはスクリプトにして、cronで週3回ほど自動実行させています。
スクリプトはこんな感じ。バックアップ開始/終了をtwitterにpostしています。

USERNAME@SERVER:# cat backup_image.sh
#! /bin/sh
set -x

TMP_DIR=/data/tmp
TMP_IMAGE=disk.img
DST_DIR=/data/Dropbox/backup
DST_FILE=disk.img.tgz
MNT_POINT=/mnt/tmp
TMP_FILE=/tmp/tmp.$$

TW_POST=/home/root/bin/twitter_post.pl

# mount check
# if mounted /mnt/tmp , this script will finish.
df | grep '/mnt/tmp$' > /dev/null
test $? -eq 0  &&  exit

#
if [ -f $TW_POST ]; then
    HM=`date +%H:%M`
    echo "disk image backup start at $HM" > $TMP_FILE
    $TW_POST $TMP_FILE
    rm $TMP_FILE
fi


test -f $TMP_DIR/$TMP_IMAGE  &&  rm -f $TMP_DIR/$TMP_IMAGE

# make new 2GB sparse file
dd if=/dev/zero of=$TMP_DIR/$TMP_IMAGE bs=1024M seek=2 count=0
echo "y" | /sbin/mkfs -t ext3 $TMP_DIR/$TMP_IMAGE

# mount image
test ! -d $MNT_POINT  &&  mkdir -p $MNT_POINT
mount -o loop $TMP_DIR/$TMP_IMAGE $MNT_POINT

# mount check
# if NOT mounted /mnt/tmp , this script will finish.
df | grep '/mnt/tmp$' > /dev/null
if [ $? -ne 0 ]; then
    if [ -f $TW_POST ]; then
        HM=`date +%H:%M`
        echo "disk cannot mounted. abort. $HM" > $TMP_FILE
        $TW_POST $TMP_FILE
        rm $TMP_FILE
    fi
    exit
fi
   

# copy
/usr/bin/rsync -Sax / $MNT_POINT --exclude "/data/" --exclude "$MNT_POINT/"
sleep 900

umount $MNT_POINT

test -f $DST_DIR/$DST_FILE  &&  rm -f $DST_DIR/$DST_FILE

(cd $TMP_DIR ; tar -Szcf $DST_DIR/$DST_FILE ./$TMP_IMAGE )

rm -f $TMP_DIR/$TMP_IMAGE

if [ -f $TW_POST ]; then
    HM=`date +%H:%M`
    echo "disk image backup finished at $HM" > $TMP_FILE
    $TW_POST $TMP_FILE
    rm $TMP_FILE
fi

0 件のコメント:

コメントを投稿