トップ 差分 一覧 ソース 置換 検索 ヘルプ PDF RSS ログイン

ディレクトリをバックアップする

rsyncによるバックアップ


http://x68000.q-e-d.net/~68user/unix/pickup?rsync
http://www.itmedia.co.jp/enterprise/articles/0804/25/news034.html

tar を使う方法とかあるけど、rsync もなかなか良い。

オプションの -a はアーカイブモードで -rlptgoD と同じである。各オプションの意味は以下のとおり。

ディレクトリを再帰的にコピーする (-r)
シンボリックリンクを、そのままシンボリックリンクとしてコピーする (-l)
パーミッションをそのままコピーする (-p)
タイムスタンプをそのままコピーする (-t)
グループをそのままコピーする (-g)
ファイルオーナー (所有者) をそのままコピーする (-o)
デバイスファイルやを特殊ファイルを、そのままコピーする (-D) 

rsync -av /foo/from_dir /bar/to_dir

sshサーバに転送するには

rsync -av -e ssh user@example.com:from_dir/ /foo/to_dir/ 

 ファイル名の文字コードを変換しながら転送

http://rottarte.net/blog/archives/2847

--iconv=SRC-CHARSET,DEST-CHARSET

というオプションを使うとファイル名の文字コードを変換できる。

rsync -auv --iconv=EUC-JP,UTF8 ./ 123.123.123.123 :/dest/dir/

 差分バックアップ

差分バックアップするには --link-dest オプションを使う

rsync -av --delete --link-dest=../dir1_bak0 ~/dir1/ ~/dir1_bak1

相対パスを利用する場合は、--link-destで指定するディレクトリは、コピー先ディレクトリからの相対パスになるの注意。
絶対パスを使うと安心。



--link-destは、このオプションで指定したディレクトリ以下とコピー元を比較し、ファイルの所有者、タイムスタンプ、パーミッションなどすべてが一致するファイルであれば、指定されたディレクトリ内のファイルのハードリンクを作成します(ファイルが一致しない場合は、ハードリンクではなくコピーが行われます)

ls -li

でハードリンクになっていることが確認できる。

サンプルシェル

 #!/bin/sh
 export LANG=ja_JP.UTF-8
 
 ###############
 #  環境変数
 ###############
 nowTime=`date +%Y%m%d_%H%M%S`
 limit=5
 sourcePath="/home/centos/temp/backup/src"
 backupBasePath="/home/centos/temp/backup/dest"
 destPath=${backupBasePath}/${nowTime}
 workPath=${backupBasePath}/work
 
 execListBackupBasePath="ls \"\${backupBasePath}\" | grep \"[0-9]\{8\}_[0-9]\{6\}\""
 pidFile=/var/run/backup.pid
 mail="hoge@hoge.com"
 subject="[Backup Log] ${nowTime}"
 
 # LINE_SEP='
 # '
 LINE_SEP=$'\n'
 IFS_DEFAULT=$IFS
 
 ##############
 #   関数群
 ##############
 # sendMail(){
   # subjectBase64=""
   # lines=`echo -e ${subject} | fold -c10`
   # sep=""
   # IFS=${LINE_SEP}
   # for line in ${lines}; do
     # subjectBase64=${subjectBase64}${sep}"=?UTF-8?B?`echo -e ${line} | base64`?="
     # sep="\n\t"
   # done
   # IFS=${IFS_DEFAULT} 
   # #subject="${subjectBase64%\\n\\t}"
   # # echo -e ${subjectBase64}
   # body=`echo -e $1 | base64`
   # echo -e "To:${mail}\nSubject:${subjectBase64}\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: base64\n\n$body" | /usr/sbin/sendmail -t
 # }
 
 checkError(){
   if [ $1 -ne 0 ]; then
     echo $2
     # sendMail "バックアップ異常終了\n$2"
     exit $1
   fi
 }
 
 checkProcess(){
   if [ -f "${pidFile}" ]; then
     PID=`cat "${pidFile}" `
     if (ps -e | awk '{print $1}' | grep ${PID} > /dev/null); then
       return 1
     fi
   fi
   echo $$ > "${pidFile}"
   return 0
 }
 
 judgeFullBackup() {
   local result=0
   fullBackup=1
   local countBackup=`eval ${execListBackupBasePath} | wc -l`
   if [ ${countBackup} -ne 0 ]; then
     fullBackup=0
     beforeBackup=`eval ${execListBackupBasePath} | sort -r | head -n 1`
     result=$?
     beforeBackup="${backupBasePath}/${beforeBackup}"
   fi
   return ${result}
 }
 
 runBackup() {
   if [ -d "${workPath}" ]; then
     echo 作業用ディレクトリを削除します。
     echo rm "${workPath}"
     rm -rf "${workPath}"
     result=$?
     checkError ${result} "作業用ディレクトリ削除に失敗しました。"
   fi
   
   if [ $fullBackup -eq 1 ]; then
     echo バックアップ種類 ベースバックアップ
     echo バックアップソース "${sourcePath}"
     echo バックアップ先 "${destPath}"
   else
     echo バックアップ種類 差分バックアップ
     echo バックアップソース "${sourcePath}"
     echo バックアップ比較元 "${beforeBackup}"
     echo バックアップ先 "${destPath}"
   fi
   echo ${LINE_SEP}
   
   if [ $fullBackup -eq 1 ]; then
     #(time rsync -av --chmod=ugo-w --delete "${sourcePath}/" "${workPath}/")
     (time rsync -av --delete "${sourcePath}/" "${workPath}/")
   else
     #(time rsync -av --chmod=ugo-w --delete --link-dest="${beforeBackup}" "${sourcePath}/" "${workPath}/")
     (time rsync -av --delete --link-dest="${beforeBackup}" "${sourcePath}/" "${workPath}/")
   fi
   result=$?
   checkError ${result} "バックアップに失敗しました。"
   
   mv "${workPath}" "${destPath}"
   result=$?
   checkError ${result} "作業ディレクトリの名前変更に失敗しました。"
   
   return 0
 }
 
 deleteBackup() {
   IFS=$LINE_SEP
   local countBackup=`eval ${execListBackupBasePath} | wc -l`
   local countDelete=`expr ${countBackup} - ${limit}`
   if [ $countDelete -gt 0 ]; then
     local deleteDirs=`eval ${execListBackupBasePath} | sort | head -n ${countDelete}`
     for deleteDir in ${deleteDirs}; do
       deleteDir="${backupBasePath}/${deleteDir}"
       echo delete "${deleteDir}"
       (time rm -rf "${deleteDir}")
     done
   fi
   IFS=${IFS_DEFAULT} 
 }
 
 ####################
 #   メイン処理
 ####################
 echo "#########################"
 echo backup start
 date "+%Y-%m-%d %H:%M:%S"
 echo "#########################"
 
 checkProcess
 result=$?
 checkError ${result} "多重起動エラー"
 
 # パスの存在チェック
 if [ ! -d "${sourcePath}" ]; then
   checkError 1 "${sourcePath}がありません。"
 fi
 
 if [ ! -d "${backupBasePath}" ]; then
   checkError 1 "${backupBasePath}がありません。"
 fi
 
 if [ -d "${destPath}" ]; then
   checkError 1 "${destPath}が既に存在しています。"
 fi
 
 # バックアップモード決定
 judgeFullBackup
 result=$?
 checkError ${result} "バックアップモード決定に失敗しました。"
 
 # バックアップ実行
 runBackup
 result=$?
 checkError ${result} "バックアップに失敗しました。"
 
 # 古いバックアップファイルを削除
 deleteBackup
 
 echo "#########################"
 echo backup end
 date "+%Y-%m-%d %H:%M:%S"
 echo "#########################"
 rm "${pidFile}"
 
 # sendMail "バックアップ正常終了"
 echo "バックアップ正常終了"

 cron を使う場合の注意

cron で実行すると LANG環境変数が設定されずにログファイル等が化けることがある。
cron から実行されるシェル等の冒頭で

export LANG=ja_JP.UTF-8

で文字コードを設定しておくと良い。

 ファイルの変更検出について

rsyncは通常のオプションでは、日付とサイズによってファイルの変更検出を行う。
ファイルの中身で変更検出を行う場合は
-c または --checksum

転送要否を決定する際、タイムスタンプとファイルサイズではなく、128bit の MD4 チェックサムを用いて同一ファイルか否かをチェックする。

を使う。

 バックアップ領域の書込権限

可能であれば、バックアップ領域は読取専用にしておく。
通常はroot権限でも書込出来ないように読取専用でマウントしておく。
バックアップ時に書込み可能にする。

方法1

通常は

mount -o ro /dev/sdb1 /mnt/backup

にしておき、バックアップ時に

mount -o remount,rw /mnt/backup

にする。ただし、バックアップ中は書込み可能になる

方法2

名前空間を使ってバックアッププロセス以外は書込不可にする。
通常は

mount -o ro /dev/sdb1 /mnt/backup

にしておく。バックアッププロセスを

unshare -m

で実行して分離している状態で

mount -o remount,rw /mnt/backup

にする。


[カテゴリ: OS > Linux]

[通知用URL]



  • Hatenaブックマークに追加
  • livedoorクリップに追加
  • del.icio.usに追加
  • FC2ブックマークに追加

最終更新時間:2017年12月29日 22時22分28秒