2015年2月15日日曜日

ZFS on Linux の snapshot 運用スクリプト例

legacy なファイルサーバ (ext3+sambaを利用) のバックアップを、rsync を使って、ZFS on Linux 上に採取しているのですが、ZFS の snapshot 機能を使うことで、例えて言うと「タイムマシン」のような効果を得ています。

具体的には、毎日1回 rsync で、ファイルサーバの内容を ZFS 上へバックアップして、その後、その ZFS 領域に対して1日1回の snapshot を採取しています。これで、間違ってファイルを消したりした場合に、snapshot を過去に辿って参照することで、間違って消してしまう以前の出来るだけ最新状態のファイルを復活させるということが可能になります。

ただ、1日1回の snapshot とはいえ、日々繰り返せば1年では 365 もの snapshot が残ることになり、そのファイルサーバの更新頻度から考えると過剰に思えました。そこで、次のようなアルゴリズムで、古い snapshot を削減するようにしました。

■わたしが考えた古い snapshot を削除するゴルゴリズム
(1) 先月の snapshot までは、1日1つを維持する。
(2) 先々月の snapshot は、奇数日だけ残して半減させる。
(3) 3ヶ月めに入ったら、1日・7日・13日・19日・25日を残して、その他を削除する。
(4) 4ヶ月めに入ったら、13日の snapshot だけを残す。
このような条件で過去の snapshot を削除することで、直近なら1日の粒度で、古くは1ヶ月に1つの粒度の snapshot を残すようにしました。

以下、そのスクリプトを掲載したいと思います。同じようなことをしたいと考える人が居るかなと思いますし、一から書くのも骨なので、ご参考まで。

まず、平凡ですが、スナップショットを採るスクリプトです。
#!/bin/bash
#
# Name: my-zfs-snapshot
#
# Authors: staka <blue3waters at gmail dot com>
#
# This file is subject to the GNU General Public License.
# It comes with NO WARRANTY.
#

TARGET_ZFS=tankX/fsvdata  ※対象を指定

TIME_STAMP=`date +%F-%H%M`

/sbin/zfs snapshot ${TARGET_ZFS}@${TIME_STAMP}

exit $?
次に、古いスナップショットを削除するスクリプトです。
#!/bin/bash
#
# Name: my-zfs-destroy-snapshot
#
# Authors: staka <blue3waters at gmail dot com>
#
# This file is subject to the GNU General Public License.
# It comes with NO WARRANTY.
#

PATH=/sbin:$PATH

TARGET_ZFS=tankX/fsvdata  ※対象を指定

B2_MONTH=`date -d "$(date +%Y-%m-15) -2 month" +%Y-%m`
B3_MONTH=`date -d "$(date +%Y-%m-15) -3 month" +%Y-%m`
B4_MONTH=`date -d "$(date +%Y-%m-15) -4 month" +%Y-%m`
B4_FLAG13=

DESTROY_LIST=

for SNAPSHOT in `zfs list -H -t snapshot -o name`
do
    DESTROY=no

    [[ $SNAPSHOT != "${TARGET_ZFS}@"* ]] && continue

    if [[ $SNAPSHOT = *"${B2_MONTH}-"* ]] ; then
        case $SNAPSHOT in
        *"${B2_MONTH}-"?[02468]-*)
            DESTROY=yes
            ;;
        esac
    fi

    if [[ $SNAPSHOT = *"${B3_MONTH}-"* ]] ; then
        case $SNAPSHOT in
        *"${B3_MONTH}-01-"*)
            ;;
        *"${B3_MONTH}-07-"*)
            ;;
        *"${B3_MONTH}-13-"*)
            ;;
        *"${B3_MONTH}-19-"*)
            ;;
        *"${B3_MONTH}-25-"*)
            ;;
        *)
            DESTROY=yes
            ;;
        esac
    fi

    if [[ $SNAPSHOT = *"${B4_MONTH}-"* ]] ; then
        case $SNAPSHOT in
        *"${B4_MONTH}-13-"*)
            B4_FLAG13=yes
            ;;
        *)
            DESTROY=yes
            ;;
        esac
    fi

    if [ $DESTROY = "yes" ] ; then
        DESTROY_LIST="$DESTROY_LIST $SNAPSHOT"
    fi
done

for SNAPSHOT in $DESTROY_LIST
do
    if [[ $SNAPSHOT = *"${B4_MONTH}-"* ]] && [ "$B4_FLAG13" != "yes" ] ; then
        continue
    fi
    zfs destroy $SNAPSHOT
done

exit 0
この2つのスクリプトを cron で1日1回実行するようにスケジュールしています。

これを書いて使い始めたのが 2013年11月 なので、1年3ヵ月ほど使っています。
この間、1度だけ、復活させたいファイル(たぶん誤操作で、いつの間にか消えていた)があり、恩恵を得られました。非常に助かりました。

このスクリプトの中で肝となる部分は、先々月(B2_MONTH)などを、どうやって求めるか?ですが、それについては、次の記事も参照ください。
■関連記事
date コマンドで先月を求める方法

人気ブログランキングへ にほんブログ村 IT技術ブログへ