アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

LVMのスナップショット機能を使ってXenイメージのバックアップ

今回は、社内サーバに関するバックアップの一環で、LVMのスナップショットを使用してXenイメージのバックアップをとってみました。

LVM(Logical Volume Manager)とは?


名前の通り、論理ディスクを管理する機能です。
通常のパーティションだと、サイズを動的に変更したり、複数のディスクにまたがって一つのパーティションにするなど出来ませんが、そこにLVMを噛ませることにより、柔軟に管理出来るようになります。

LVM概要


LVMは既存のパーティション上に乗って仮想化する機構なので、下記の3種類のレイヤがあります。
・PV(Physical Volume)
 物理的に書き込む領域です。(HDDのパーティションなど)
 領域をLVMのPVとして初期化した際に、PE(Physical Extents)という単位で分割され、この単位で論理領域の確保が出来るようになります。

・VG(Volume Group)
 PVをまとめたもので、LVMとして使用出来る領域になります。
 物理領域(PV)をまとめて一つの デバイス にしたものと思ってください。

・LV(Logical Volume)
 実際に使用する領域。
 VGにパーティションを切ってしたものとでも思ってください。

細かい説明はググってください。

スナップショットとは?


ある時点に記録されている状態を取っておいたもの。
スナップショットを作成した瞬間の情報を保持することが出来ます。

Xenのイメージなど、常時使用中で何か書き込みがあるファイルの場合、cpなどで普通にコピーするとコピー中もデータが書き換わり不整合が起てしまうので、スナップショットを作成してコピーすることにより、破損を最小限(突然電源を落とした程度)にとどめてバックアップ出来ます。
ただし、Copy on write で実装されている関係で、スナップショットを作成した瞬間から書き込みコストが増大しますので、恒久的なバックアップには使えません。(使えないこともないですが遅くなります)
そのため、今回はスナップショットからデータを吸い出したあとに消去する手法にしました。

スナップショットの作成


スナップショットの作成は下記のようにします。


lvcreate -s -L 2G -n "snap_20120719" "/dev/xenimages/xen-image-disk0"

簡単ですね。

-s スナップショットとして作成
-L スナップショット領域のサイズ(元ボリューム側で変更されたサイズがこのサイズを超えるまで使用可能。
  超えるとスナップショットのデータが壊れる可能性あり)
-n スナップショットの名前
元ボリュームのパス

となります。

実際の作業


基本的な流れは、下記のようになります。
・スナップショット作成
・マウント
rsyncで内容をコピー
・アンマウント
・スナップショット削除

毎回手動で実行するのは馬鹿馬鹿しいので、cronで実行出来るようにスクリプト化しました。
ついでに古いものの自動削除も。



#!/bin/bash

export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

function backup {
    orgvol="$1";

    # スナップショットパス定義
    snapvol="$orgvol-snap_`date '+%Y%m%d%H%M%S'`"

    if [ -b "$snapvol" ];then
        echo "$snapvol が存在します"
        return 9;
    fi

    if [ ! -b "$orgvol" ];then
        echo "$orgvol が存在しません"
        return 9;
    fi
    
    ret=0

## LVM自体バックアップ版
## 無駄が多いのでボツ
#    # スナップショットのエリアをddする
#    echo "バックアップ中..."
#    if ! dd if="$snapvol" | gzip > "$backupdir/`basename $snapvol`.gz";then
#        echo "ddの実行に失敗"
#        ret=2
#    fi

    # バックアップ先ディレクトリ作成
    snapbackupdir="$backupdir/snap_`basename $snapvol`"
    test ! -d "$snapbackupdir"  & & mkdir "$snapbackupdir";
    if [ ! -d "$snapbackupdir" ];then
        echo "バックアップディレクトリの作成に失敗"
        return 3
    fi
    # マウント先作成
    echo "tmpmount作成"
    test ! -d "tmpmount"  & & mkdir "tmpmount"
    if [ ! -d "tmpmount" ];then
        echo "マウント先ディレクトリの作成に失敗"
        return 4
    fi

    # LVMスナップショットを取る
    echo "スナップショット作成 `basename $snapvol`" "$orgvol"
    if ! lvcreate -s -L 2G -n "`basename $snapvol`" "$orgvol";then
        echo "スナップショットの作成に失敗"
        return 1;
    fi

    # スナップショットをマウントしてrsync
    echo "mount"
    if ! mount -o ro,nouuid "$snapvol" "tmpmount";then
        echo "マウントに失敗"
        ret="5"
    fi

    if [ $ret -eq 0 ];then
        echo "rsync中..."
        if ! rsync -auv --progress "tmpmount/" "$snapbackupdir/";then
            echo "rsync実行に失敗"
            ret=6
        fi
    fi

    # umount
    echo "umount"
    if ! umount "tmpmount";then
        echo "umountに失敗"
        ret=7
    fi

    # rmdir
    echo "rmdir"
    if ! rmdir "tmpmount";then
        echo "tmpmount削除に失敗"
        ret=8
    fi

    # スナップショットを削除
    echo "スナップショット削除"
    if ! lvremove -f "$snapvol";then
        echo "スナップショット削除に失敗"
        ret=4
    fi
    
    return $ret;
}

function rotate {
    # 1日分以降を削除
    backupdir="$1"
    for dir in $backupdir/snap_*; do 
        # unixtimeに変換
        utime="`echo $dir | perl -MTime::Local -pe 's/.*(201\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/($6,$5,$4,$3,$2-1,$1)/; $_=(timelocal eval $_)."\n"'; `"
        time7="$((`date +%s` - 86400))"
        if [ "$utime" -gt 1240432473 ];then # 念の為
            if [ "$utime" -lt "$time7" ];then
                rm -rfv "$dir"
            fi
        fi
    done
}

# バックアップするデバイス スペース区切りで複数指定可
lvmnames="/dev/xenimages/xen-image-disk0"

# バックアップ先
backupdir="/mnt/backup"

rotate $backupdir;

# 全てのボリュームに対して実行
for orgvol in $lvmnames;do
    backup "$orgvol"
    res="$?"
    if [ "$res" -ne 0 ];then
        echo "code: $res" | /usr/bin/mail -s "[`hostname`] LVM snapshot faild" mailaddress@example.com
    fi
done


単純ですがこんな感じで書いて見ました。
これを毎日1回実行すればスナップショット使用してバックアップ出来ます。
なお、ディレクトのパスとか名前は環境によって違うのであくまでも 参考まで にしてください。
もし実際のサーバで試す場合には、壊れてもいいサーバでテストを入念にしてください。

バックアップしようとして、全部消えるとか洒落にならないですよね。
ちょっとしたバグで、多量のデータが消えると言う悪夢がたまにあるので・・・。

使用感


弊社の場合、対象が400GB程度ある+バックアップ先にGlusterFSを使用しているためバックアップ自体は遅いですが、数時間あれば終わります。
バックアップ中はパフォーマンスは落ちますが、深夜なのでそれほど苦情は出ていないようです。
※詳細なパフォーマンスについては測定もできますが、本番運用中+GlusterFSを使用しているため速度的に不安定で大したデータは取れないので、そのうちに環境できたらやるかもしれません。

また、この方法だとディスクのフルバックアップになるため無駄が多くなります。どうにかして差分バックアップ出来ないか模索中なので、出来たらブログの記事にします。

以上、LVMスナップショットを使ったバックアップの紹介でした。