スタンドアロン* NIXサーバーのバックアップ。 TimeMachineをエミュレートする

出席者の誰もバックアップの重要性を説明する必要はないと思います。

問題は、何十もの既製のソリューションのうち、どれもコロケーションのスタンドアロン* NIXサーバーの要件を実際に満たしていないことです。

バックアップには何が必要でしたか?

1)すべてのデータの毎日の完全な bakap。 増分バックはありません。

2)単一ファイルの最速の回復。 アーカイバ(tar / gzip / bzip2 / rar)が消えます

3)迅速な監視「昨日156GBサーバーを正確にアップロードしたのは誰ですか?!!!」

4)ディスクに十分な空き領域がある限り、バックアップコピーを保持したい。

5)ディスク容量がまだ不足している場合、古いコピーを手動で削除することを心配したくない

簡単に言えば、LinuxサーバーにMAC OS TimeMachine機能を実装したかったのです。

そして、スクリプトを書き始めました。

おそらく既にTimeMachineの基礎はcp -al + rsyncの組み合わせであると推測しました

指では、次のようになります。

cp -al _backup _backup





今日はハードリンク付きで昨日のファイルをコピーします。 このようなコピーは、実際のディスク容量をほとんど占有しません。 それから始まります

rsync -a --del _backup





昨日のバックアップのコピーの違いをスキャンし、違いが見つかった場合、ハードリンクを削除してファイルの新しいバージョンを書き込みます。 新しいファイルとディレクトリの作成/コピー。 存在しないファイルを削除します。 この場合、昨日のバックアップ内のすべてのファイルはそれぞれの場所に残ります。

通常の作業バックアップスクリプトを作成するには、これら2つのコマンドでは不十分であることが明らかです。 スクリプトを2つのファイルに書くことにしました-1つは一連の機能を持つすべてのサーバーの標準、もう1つはcronを直接実行するサーバーごとの短い個別のファイルです。



したがって、これらのスクリプト:( ここからダウンロードできます

backup_functions.sh

 #!/bin/sh export LC_ALL=en_US.utf8 DIR_PATTERN='20..-..-..' CURR_DATE=`date +%F` #CURR_DATE=`date +%F_%R` RESERVE_G=5 BACKUP_MAIN_DIR='/backup' BACKUP_TMP_DIR='/tmp' BACKUP_DELTA=$BACKUP_TMP_DIR/backup.delta BACKUP_ERRORS=$BACKUP_TMP_DIR/backup.err BACKUP_REPORT=$BACKUP_TMP_DIR/backup.report BACKUP_LOG_FACILITY='user.notice' BACKUP_EXPIRES_DAYS=0 VERIFY_BACKUP_MOUNTED='no' PID_FILE='/var/run/backup.pid' BACKUP_MYSQL_DIR=$BACKUP_TMP_DIR/mysql_dump MYSQL_DATA_DIR='/var/lib/mysql' [ -z "`which rsync`" ] && { echo "RSYNC is not installed! backup will not work!"; exit; } [ -n "`which ionice`" ] && IONICE_CMD='ionice -c2 -n6' touch /etc/default/backup_exclude rm $BACKUP_DELTA $BACKUP_ERRORS $BACKUP_REPORT 1>/dev/null 2>/dev/null verify_backup_mounted() { mount -a [ -d "$BACKUP_MAIN_DIR" ] || { echo "BACKUP main directory does not exist!"; exit; } str=`df "$BACKUP_MAIN_DIR" | tail -1 | grep ' /$'` [ "$str" ] && { echo 'BACKUP partition is not mounted!!!!!!!!'; exit; } return 0 } prepare_for_backup() { if [ -s "$PID_FILE" ] && [ `cat "$PID_FILE"` -ne $PPID ] then if [ "`ps ax | awk '{print $1;}' | grep -f \"$PID_FILE\"`" ] then echo -n "Previous BACKUP script is still running. PID = "; cat "$PID_FILE"; exit else logger -t BACKUP -p $BACKUP_LOG_FACILITY "Previous BACKUP ended unexpectly" fi fi rm "$PID_FILE" 1>/dev/null 2>/dev/null echo $PPID > "$PID_FILE" old_dir=`pwd` cd "$BACKUP_MAIN_DIR" || { echo "BACKUP main directory does not exist!"; exit; } VERIFY_BACKUP_MOUNTED=`echo "$VERIFY_BACKUP_MOUNTED" | tr 'AZ' 'a-z'` [ "$VERIFY_BACKUP_MOUNTED" = "yes" ] && verify_backup_mounted reserve_k=$(($RESERVE_G * 1048576)) mkdir -p $BACKUP_TMP_DIR 1>/dev/null 2>/dev/null dirs_list=`ls | grep $DIR_PATTERN | sort` if [ -n "$dirs_list" ] then while { free_k=`df -k .|grep -v Filesystem| sed -e "s/.\+ \([0-9]\+\) .\+/\1/"` dirs_list=`ls | grep $DIR_PATTERN | sort` free_pre=$free_k [ $free_pre -lt $reserve_k ] ; } do dir_oldest=`echo $dirs_list | tr " " "\n" | head -1` [ -d $dir_oldest ] && { logger -t BACKUP -p $BACKUP_LOG_FACILITY "Deleting old backup in $BACKUP_MAIN_DIR/$dir_oldest" ; rm -rf $dir_oldest; } done fi [ "$VERIFY_BACKUP_MOUNTED" = "yes" ] && verify_backup_mounted last_date=`ls | grep $DIR_PATTERN | sort | tail -1` if [ -n "$last_date" -a \( "$CURR_DATE" != "$last_date" \) ] then logger -t BACKUP -p $BACKUP_LOG_FACILITY "Preparing. Copying $BACKUP_MAIN_DIR/$last_date -> $BACKUP_MAIN_DIR/$CURR_DATE" mkdir $CURR_DATE 1>/dev/null 2>/dev/null $IONICE_CMD cp -al "$last_date"/* $CURR_DATE 1>/dev/null 2>/dev/null rm -rf $CURR_DATE/_delta 1>/dev/null 2>/dev/null fi mkdir $CURR_DATE/_delta 1>/dev/null 2>/dev/null if [ $BACKUP_EXPIRES_DAYS -gt 0 ] then for expired_dir in `find "$BACKUP_MAIN_DIR" -maxdepth 1 -mtime +$BACKUP_EXPIRES_DAYS -type d | grep "$DIR_PATTERN"` do logger -t BACKUP -p $BACKUP_LOG_FACILITY "Deleting expired backup $expired_dir" ; rm -rf $expired_dir; done fi cd $old_dir return 0 } make_backup() { while [ -n "$1" ] do [ "$VERIFY_BACKUP_MOUNTED" = "yes" ] && verify_backup_mounted src=$1 full_src=`echo $PWD/$1 | sed -e 's://:/:g'` dst=`echo $BACKUP_MAIN_DIR/$CURR_DATE/$src | sed -e "s/\/\w\+$//"` mkdir -p $dst 1>/dev/null 2>/dev/null logger -t BACKUP -p $BACKUP_LOG_FACILITY "$full_src started" $IONICE_CMD rsync -axW8 --del --exclude-from=/etc/default/backup_exclude $src $dst 2>>$BACKUP_ERRORS sync shift done return 0 } make_backup_with_delta() { while [ -n "$1" ] do [ "$VERIFY_BACKUP_MOUNTED" = "yes" ] && verify_backup_mounted src=$1 full_src=`echo $PWD/$1 | sed -e 's://:/:g'` dst=`echo $BACKUP_MAIN_DIR/$CURR_DATE/$src | sed -e "s/\/\w\+$//"` mkdir -p $dst 1>/dev/null 2>/dev/null rm $BACKUP_DELTA 1>/dev/null 2>/dev/null logger -t BACKUP -p $BACKUP_LOG_FACILITY "$full_src (with delta) started" $IONICE_CMD rsync -axW8i --del $src $dst --exclude-from=/etc/default/backup_exclude 2>>$BACKUP_ERRORS | grep "^>f" | cut -d ' ' -f 2- 1>$BACKUP_DELTA old_dir=`pwd` cd $BACKUP_MAIN_DIR/$CURR_DATE dst=`echo $src | sed -e "s/\w\+$//"` xargs -a $BACKUP_DELTA -r -n5 -d '\n' -I '{}' echo $dst{} | xargs -r -n10 -d '\n' cp -ul --parents -t _delta rm $BACKUP_DELTA 1>/dev/null 2>/dev/null cd $old_dir sync shift done return 0 } send_email_report() { if [ -s $BACKUP_ERRORS ] then logger -t BACKUP -p $BACKUP_LOG_FACILITY "Sending email report" echo 'Content-type: text/plain; charset=utf-8' >> $BACKUP_REPORT echo 'Content-Transfer-Encoding: 8bit' >> $BACKUP_REPORT echo 'From: root@'`hostname --fqdn` >> $BACKUP_REPORT echo 'To: root' >> $BACKUP_REPORT echo 'Date:' `date` >> $BACKUP_REPORT echo -e 'Subject: Cron <root@'`hostname --fqdn`'> BACKUP\n\n' >> $BACKUP_REPORT cat $BACKUP_ERRORS >> $BACKUP_REPORT cat $BACKUP_REPORT | sendmail root fi rm $BACKUP_DELTA $BACKUP_ERRORS $BACKUP_REPORT $PID_FILE 1>/dev/null 2>/dev/null logger -t BACKUP -p $BACKUP_LOG_FACILITY "Finished" return 0 } make_mysql_backup() { MYSQL_DATA_DIR='/var/lib/mysql' mkdir $BACKUP_MAIN_DIR/$CURR_DATE/MySQL 1>/dev/null 2>/dev/null rm -rf $BACKUP_MAIN_DIR/$CURR_DATE/MySQL/* 1>/dev/null 2>/dev/null rm -rf $BACKUP_MYSQL_DIR 1>/dev/null 2>/dev/null mkdir -p $BACKUP_MYSQL_DIR 1>/dev/null 2>/dev/null cd $MYSQL_DATA_DIR logger -t BACKUP -p $BACKUP_LOG_FACILITY "MySQL started" for db_dir in `ls -p | grep '/' | tr -d '/'` do cd $MYSQL_DATA_DIR/$db_dir db_name=`echo $db_dir | sed -e 's/@003d/=/g' -e 's/@002d/-/g'` logger -t BACKUP -p $BACKUP_LOG_FACILITY "MySQL database '$db_name' started" for table in `ls | grep '.frm' | sed -e 's/\.frm//' -e 's/ /:::/g'` do table=`echo $table | sed -e 's/:::/ /g' -e 's/@003d/=/g' -e 's/@002d/-/g'` mysqldump $db_name "$table" --skip-lock-tables -Q -u $mysql_user -p$mysql_pass > "$BACKUP_MYSQL_DIR/$table.sql" done cd $BACKUP_MYSQL_DIR tar czf $BACKUP_MAIN_DIR/$CURR_DATE/MySQL/$db_name.tar.gz * 1>/dev/null 2>/dev/null rm $BACKUP_MYSQL_DIR/* 1>/dev/null 2>/dev/null done return 0 }
      
      







およびbackup.sh

 #!/bin/sh . /usr/local/sbin/backup_functions.sh BACKUP_EXPIRES_DAYS=365 #  30 RESERVE_G=30 BACKUP_MAIN_DIR='/backup' VERIFY_BACKUP_MOUNTED='yes' prepare_for_backup cd / make_backup etc boot home root opt srv usr/local make_backup_with_delta var/spool var/lib var/www BACKUP_MAIN_DIR='/backup' mysql_user='root' mysql_pass='jndey7hdFdfii7HN6ygdrarUh' make_mysql_backup send_email_report
      
      





Backup_functions.sh/ usr / local / sbinにあると想定されています 。 別のディレクトリにある場合-backup.shの対応する行を修正します

ご覧のとおり、スクリプトはグローバル変数を豊富に使用しています。 例えば

RESERVE_G-今日のバックアップを開始する前に最も古いコピーを削除することによってスクリプトが提供する予約(ギガバイト単位)

BACKUP_EXPIRES_DAYS-まだディスク領域がある場合でも、古いバックアップを削除する日数。 BACKUP_EXPIRES_DAYS == 0の場合、これは行われません。

BACKUP_MAIN_DIR-バックアップが書き​​込まれるディレクトリ。 内部には、「2011-11-15」のようなフォルダが作成され、そこにバックアップが書き​​込まれます。

VERIFY_BACKUP_MOUNTED-バックアップの下に別のディスクパーティションがマウントされているかどうかを確認します。 選択したパーティションが何らかの理由でマウント解除された場合、このチェックを行わないと、バックアップがrootfsまたはこれに適さないディスクの別のパーティションに大きく落ちることに注意してください。 ただし、バックアップが明示的かつ明確にrootfsまたは別の非専門パーティションに具体的に書き込まれる場合があります。 次に、指定する必要があります

VERIFY_BACKUP_MOUNTED = "no"

cronによってすでに直接実行できる短いbackup.shスクリプトは、いくつかの自作関数を呼び出します。

prepare_for_backup-この関数は、直接コピーする前にすべてを準備します。 大まかに言って、彼女はcp -al +あらゆる種類のチェックを行い、ディスクの空き容量がなくなったかどうかを確認します。

make_backup (パラメーター付き)-関数は実際にコピーを作成します

make_backup_with_delta (同じパラメーター)-関数はコピーを作成し、オプションですべての新しいファイルをハードリンクディレクトリ2011-11-15 / _deltaにマージします。 このディレクトリを調べると、昨日サーバーに156GBをアップロードしたユーザーをすばやく簡単に見つけることができます。

send_email_report-この関数は、バックアップ中にエラー(ある場合)を含む電子メールを管理者に送信します。

ファイルのバックアップに加えて、付録のMySQLのバックアップ関数も作成しました。

make_mysql_backup-この関数はパラメーターなしで呼び出され、テーブルmysqldump(つまり、1つのテーブル-1つの.sqlファイル)を作成し、各データベースのすべての.sqlファイルをdb_name.tar.gzにパックします



シートごめんなさい。 誰かが役に立つといいな。



更新:1日に1回以上バックアップを作成する場合-backup_functions.shの最初の行で次の行を置き換えます

CURR_DATE =「日付+%F」



CURR_DATE =「日付+%F_%R」



rsyncがインストールされていない場合、スクリプトは対応するメッセージで中断されます。

ioniceがインストールされている場合、rsyncは低いio優先度で起動されます(パラメーターは次の行で変更できます)

[-n "` which ionice` "] && IONICE_CMD = 'ionice -c2 -n6'



All Articles