2008年9月21日日曜日

Linuxサーバー上のファイルバックアップ

サーバー上でのファイルのバックアップでやりたいことは2つ

  • スクリプトを作成し、cronで定期的に別ディスクにファイルをコピーする

  • 定期的に(年次?)、ファイルをDVDにコピーする仕組みを作る



  • まずは、1つめの定期コピー。
    最初はいろいろと高度なことをやって別ディスクに同じ内容のものをコピーしようと思っていたが、既にRAID5を組んでいるので、あまりメリットはなさそうな感じがしてきた。そもそも、ハードウェア障害だけに対応するのなら、同じマシン上の別ディスクではなく、別のマシンのディスクにコピーしないと、あまり意味はないだろう(RAID5になっていることを考えると)。
    というわけで、単純にファイルを定期的にコピーし、世代管理をするようにしたほうが効果が高いと思った。その方が、cpだけで処理が進むので、スクリプトも単純になり、メンテナンスも楽になる。
    そして、このようにファイルのミラーを用意しておけば、年次バックアップの際は、そのディレクトリを直接DVDに焼けばよくなる。これで、2番目の課題も解決する。

    バックアップスクリプトは、例によってこちらを参考に多少手直ししたものを使用する。

    現在のハードディスクの状態は、
    # df --si

    で調べることができる。

    バックアップするファイルは、/homeの中にあるディレクトリ。試しに、各ディレクトリがどのくらい使用しているかを調べるには、
    du * -s --si

    を使えば良い。これをチェックしていき、どのファイルをバックアップに含めるか決定し、/root/backuplistに書き加えていく。

    また、subversionとtracに関しては、各アプリケーションのバックアップコマンドを使うようにスクリプトを変更する。そして、バックアップコマンドで作られたファイルをコピーするようにする。

    backup.shにアプリケーションのバックアップコマンドを追加し、/root/backuplistにバックアップ対象のファイルを書き加えたら、準備完了。
    # chmod 700 backup.sh
    # ./backup.sh
    # ls -lh /backup
    # tar tjvf /backup/backup.tar.gz2


    そして、cronの設定。毎週木曜日の3:00に実行するように設定。
    # crontab -e
    00 03 * * 4 /root/backup.sh


    これで実際に運用してみようとしたところ、コピー&tarにものすごく時間がかかり、実用的でなかった。寝て起きても終わっていなかったので、5-6時間では終わらないということ。こりゃ毎週やるのは無理だ。時間を短縮させるためにも、差分だけコピーするようにしないといけないとの結論に。というわけで、rsyncを利用してバックアップを行うことにする。

    元のスクリプトを変更し、cpではなくrsyncをするようにする。バックアップは世代管理をし、ロジックはスクリプト内で閉じるようにする。つまり、スクリプトが
    対象のディレクトリは、同じディレクトリ名で違う場所にある場合は混乱してしまうため、ディレクトリ毎に保存先を変更するため、「/」はありで指定する。これを「/」なしで指定すると、対象のディレクトリをコピーしてしまうため、バックアップディレクトリの階層が無駄に深くなってしまう。

    というわけで、作ったスクリプトは以下の通り。
    #!/bin/bash

    #
    # ローカル内でバックアップ
    #
    # 注意事項
    # backuplistのリストから削除した場合、バックアップ先は手動で削除。スクリプトはrsyncを呼ぶだけで、リストが消えたことは考慮しない
    # 例: /home/working working
    # というエントリがあった場合、/backup/backup0/workingにファイルがコピーされる。このエントリーを消した場合でも、
    # /backup/backup0/working は残り続けるので、必要ならば手動で消す。
    # 世代管理を変更した場合は、このファイル内のBACKUPGENを変更するだけでなく/backup/backup?も消す
    # スクリプト内では、/backupディレクトリの一番古いディレクトリとsyncするだけなので、世代を変更する場合はディレクトリを削除する。
    # また、このファイル内のBACKUPGENの数だけ最初にディレクトリを作成してしまうので、こちらも変更する必要がある。
    # BACKUPGENの数字と、/backupディレクトリ内のディレクトリの大きい方が採用される

    LANG=C

    #
    # 設定開始
    #

    # バックアップ対象リスト名
    # ※バックアップ対象をフルパスで記述したリスト
    BACKUPLIST=/root/backuplist
    [ ! -s $BACKUPLIST ] && echo "$BACKUPLIST is not found" && error_exit

    # バックアップ対象外リスト名
    # ※バックアップ対象外をフルパスで記述したリスト
    BACKUPNOLIST=/root/backupnolist

    # バックアップ保存世代数
    # ※当日分を含めた過去分バックアップを保存する世代数
    # ※過去分バックアップを保存しない場合は1を指定する
    BACKUPGEN=2

    # バックアップ先ディレクトリ名
    BACKUPDIR=/backup
    mkdir -p $BACKUPDIR

    # バックアップディレクトリ名
    BACKUPDIRNAME=backup
    # バックアップディレクトリの作成
    dirnum=1
    while [ $dirnum -le $BACKUPGEN ];
    do
    if [ ! -d $BACKUPDIR/$BACKUPDIRNAME$dirnum ]
    then
    mkdir -p $BACKUPDIR/$BACKUPDIRNAME$dirnum
    fi
    dirnum=`expr $dirnum + 1`
    done


    # バックアップログファイル名
    BACKUPLOG=/var/log/backup.log

    #
    # 設定終了
    #

    # 異常終了処理関数定義
    error_exit () {
    rm -f $TMPBACKUPNOLIST
    exit 1
    }

    # 最も古いバックアップディレクトリの取得し、そことsyncする
    OLDBACKUPDIR=`ls -rt $BACKUPDIR | head -1`
    TARGETDIR=$BACKUPDIR/$OLDBACKUPDIR
    echo $TARGETDIR
    echo "target dir $TARGETBACKUPDIR" >> $BACKUPLOG


    # バックアップログファイル作成
    rm -f $BACKUPLOG
    touch $BACKUPLOG
    chmod 400 $BACKUPLOG


    # バックアップ実行
    # $BACKUPFROM で指定されたディレクトリを $BACKUPDIR/backup/$BACKUPTO にコピーする
    # 例: /home/samba/working workingTest -> /backup/backup0/workingTest/working ディレクトリにコピー
    # /home/samba/working workingTest -> /backup/backup0/workingTest 以下にファイルをコピー
    BACKUPCODE=0
    echo "`date` backup start" >> $BACKUPLOG
    while read BACKUPFROM BACKUPTO
    do
    if [ -n $BACKUPFROM -a -n $BACKUPTO ]
    then
    if [ ! -d $TARGETDIR/$BACKUPTO ]
    then
    mkdir -p $TARGETDIR/$BACKUPTO
    fi
    rsync -av --delete $BACKUPFROM $TARGETDIR/$BACKUPTO > $BACKUPLOG 2>&1
    code=$?
    if [ $code -ne 0 ]; then
    BACKUPCODE=$code
    fi
    fi
    done < $BACKUPLIST
    if [ $BACKUPCODE -ne 0 ]; then
    cat $BACKUPLOG | mail -s "BACKUP NG CODE IS $code" root
    fi

    echo "`date` backup end" >> $BACKUPLOG

    2 コメント:

    pc-otasukeman さんのコメント...

    初めましてこちらの記事を参考にさせて頂き
    バックアップ設定をしているのですがバックアップスクリプトの以下の所でエラーが出て
    進みません。
    while read BACKUPFROM BACKUPTO

    自分なりにスクリプトを書き足した物を掲載します。

    backup.shの内容

    #!/bin/bash
    ## ローカル内でバックアップ
    ## 注意事項
    # backuplistのリストから削除した場合、バックアップ先は手動で削除。スクリプトはrsyncを呼ぶだけで、リストが消えたことは考慮しない
    # 例: /home/working working
    # というエントリがあった場合、/backup/backup0/workingにファイルがコピーされる。このエントリーを消した場合でも、
    # /backup/backup0/working は残り続けるので、必要ならば手動で消す。
    # 世代管理を変更した場合は、このファイル内のBACKUPGENを変更するだけでなく/backup/backup?も消す
    # スクリプト内では、/backupディレクトリの一番古いディレクトリとsyncするだけなので、世代を変更する場合はディレクトリを削除する。
    # また、このファイル内のBACKUPGENの数だけ最初にディレクトリを作成してしまうので、こちらも変更する必要がある。
    # BACKUPGENの数字と、/backupディレクトリ内のディレクトリの大きい方が採用される
    LANG=C
    #
    # 設定開始
    #
    # バックアップ対象リスト名
    # ※バックアップ対象をフルパスで記述したリスト
    BACKUPLIST=/root/backuplist
    [ ! -s $BACKUPLIST ] && echo "$BACKUPLIST is not found" &&
    error_exit
    # バックアップ対象外リスト名
    # ※バックアップ対象外をフルパスで記述したリスト
    BACKUPNOLIST=/root/backupnolist
    # バックアップ保存世代数
    # ※当日分を含めた過去分バックアップを保存する世代数
    # ※過去分バックアップを保存しない場合は1を指定する

    BACKUPGEN=10
    # バックアップ先ディレクトリ名
    BACKUPDIR=/backup
    mkdir -p $BACKUPDIR
    # バックアップディレクトリ名
    BACKUPDIRNAME=backup
    # バックアップディレクトリの作成
    dirnum=1
    while [ $dirnum -le $BACKUPGEN ];
    do
    if [ ! -d $BACKUPDIR/$BACKUPDIRNAME$dirnum ]
    then
    mkdir -p $BACKUPDIR/$BACKUPDIRNAME$dirnum
    fi
    dirnum=`expr $dirnum + 1`
    done
    # バックアップログファイル名
    BACKUPLOG=/var/log/backup.log
    #
    # 設定終了
    #
    # 異常終了処理関数定義
    error_exit () {
    rm -f $TMPBACKUPNOLIST
    exit 1
    }
    # 最も古いバックアップディレクトリの取得し、そことsyncする
    OLDBACKUPDIR=`ls -rt $BACKUPDIR | head -1`
    TARGETDIR=$BACKUPDIR/$OLDBACKUPDIR
    echo $TARGETDIR
    echo "target dir $TARGETBACKUPDIR" >> $BACKUPLOG
    # バックアップログファイル作成
    rm -f $BACKUPLOG
    touch $BACKUPLOG
    chmod 400 $BACKUPLOG
    # バックアップ実行
    # $BACKUPFROM で指定されたディレクトリを $BACKUPDIR/backup/$BACKUPTO にコピーする
    # 例: /home/samba/working workingTest -> /backup/backup0/workingTest/working ディレクトリにコピー
    # /home/samba/working workingTest -> /backup/backup0/workin
    BACKUPFROM=/winxp/samba/tokyo
    BACKUPTO=/backup



    BACKUPCODE=0
    echo "`date` backup start" >> $BACKUPLOG
    while read BACKUPFROM BACKUPTO
    do
    if [ -n $BACKUPFROM -a -n $BACKUPTO ]
    then
    if [ ! -d $TARGETDIR/$BACKUPTO ]
    then
    mkdir -p $TARGETDIR/$BACKUPTO
    fi
    rsync -av --delete $BACKUPFROM $TARGETDIR/$BACKUPTO > $BACKUPLOG 2>&1
    code=$?
    if [ $code -ne 0 ]; then
    BACKUPCODE=$code
    fi
    fi
    done < $BACKUPLIST
    if [ $BACKUPCODE -ne 0 ]; then
    cat $BACKUPLOG | mail -s "BACKUP NG CODE IS $code" root
    fi
    echo "`date` backup end" >> $BACKUPLOG


    わたくしが、シェルスクリプトをあまり理解できていないのが原因なのは重々承知しております。

    BACKUPFROM とBACKUPTOの記述が変ではないかと思うのですが、今ひとつ分かりません
    例えば
    /motodataを
    /backup/以下のフォルダに10世代のフォルダを作り圧縮無しでバックアップするスクリプトを教えて頂けないでしょうか?


    こちらの記事のような世代フォルダの中に圧縮無しのファイルをバックアップして行く方式でのバックアップを是非行いたいと思います、
    お手数かと存じますが、何卒ご教授の程お願い致します。

    nori さんのコメント...

    while read BACKUPFROM BACKUPTO
    done < $BACKUPLIST
    で$BACKUPLIST(/root/backuplist)のファイルを1行づつ読み込んでいます。このファイルの各行に書かれた、"バックアップ元 バックアップ先"が、それぞれが変数に格納されます。
    /motodataを10世代管理したい場合は、/root/backuplistに
    /motodata backup
    と記述し、スクリプト内のBACKUPGEN=2を
    BACKUPGEN=10
    と変更すれば大丈夫です。