NASのバックアップ対象ディレクトリをinotifyで監視して更新されたディレクトリのみをrsyncでミラーリング
前回のスクリプトでは大まかな流れとしてduコマンドの結果を元に更新対象ディレクトリを絞り込んでrsyncに渡すという動作を行なっていましたが、監視対象内のサブディレクトリ数が多くなるとduコマンドの実行時間もそれなりにかかるようになるので、この部分をinotifywaitコマンドでの監視に置き換えています。
inotify-toolsがインストールされていない場合inotifywaitコマンドも使えませんので、その場合はまずinotify-toolsをインストールします。
inotify-toolsをインストール(Debian系の場合)
inotifywaitで監視可能な対象数はデフォルトで8192になっているかと思いますが、NAS全体を監視対象にしようとするとサブディレクトリ数も多くなり8192では不足する場合もままありますので、環境に合わせてこの設定を増やしておきます。
設定値の確認
この設定は再起動すると初期値に戻ってしまいますので、起動時に自動で設定されるようにしておきます。
いくつか方法はありますが、私はrc.localに追記しました。
以下を追記
以降は前回と同じ方法でmirroring.phpの実行設定をすれば完了です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
<?php /** * rsync ミラーリング * inotifiwait監視版 */ // ミラーリング元ディレクトリ define('SOURCE_DIR', '/home/nas/data/'); // ミラーリング先ディレクトリ define('BACKUP_DIR', '/home/nas_backup/data/'); // その他のrsyncオプション 例: '--exclude=/temp/ --exclude=/*.bak'; define('OTHER_OPTIONS', ''); /** * */ set_time_limit(0); date_default_timezone_set('Asia/Tokyo'); // 一時ファイル保存用ディレクトリ define('TEMP_DIR', (file_exists('/dev/shm/') ? '/dev/shm/.' : '/var/tmp/.'). md5(__DIR__)); if(!file_exists(TEMP_DIR)) { mkdir(TEMP_DIR); chmod(TEMP_DIR, 0700); } // 各ディレクトリ名のデリミタ補正 $sourceDir = preg_replace('|/+$|', '/', SOURCE_DIR. '/'); $backupDir = preg_replace('|/+$|', '/', BACKUP_DIR. '/'); // バックアップ元・バックアップ先が無かったら終了 if(!file_exists($sourceDir) || strpos($backupDir, ':') === false && !file_exists($backupDir)) { print "The source '{$sourceDir}' or backup '{$backupDir}' destination directory does not exist.\n"; exit; } // inotifywaitログファイルパス $inotifyLog = TEMP_DIR. '/inotify.log'; // inotifywaitプロセス管理 $res = inotifywaitProcessManage($sourceDir, $inotifyLog); // inotifywaitログが空か最終更新からの経過時間が2秒未満なら終了 if(file_exists($inotifyLog) && (filesize($inotifyLog) == 0 || time() - filemtime($inotifyLog) < 2) && !$res) { exit; } // ロックファイル名 $lockFilename = TEMP_DIR. '/backup.lock'; // ロックファイルが存在していたら同名のプロセス実行中とみなし終了 if(file_exists($lockFilename)) { print "A process with the same name is running.\n"; exit; } else { // ロックファイル作成 if(!@file_put_contents($lockFilename, 'Process is running.')) { print "Could not create `$lockFilename`.\nSet the permissions of the directory `". TEMP_DIR. "` to 0700.\n"; exit; } chmod($lockFilename, 0600); } // 更新対象ディレクトリ取得 $updateDirList = getUpdataDirList($inotifyLog); if(!$updateDirList) { $updateDirList[] = $sourceDir; } // 更新対象ディレクトリに対してrsync実行 foreach($updateDirList as $dir) { if(!file_exists($dir)) continue; $path = str_replace($sourceDir, '', $dir); // rsyncコマンド $command = implode(" ", [ 'rsync -avH', '--delete', OTHER_OPTIONS, '"'. preg_replace('|/+$|', '/', ($sourceDir. $path. '/')). '"', '"'. preg_replace('|/+$|', '/', ($backupDir. $path. '/')). '"', ]); print "$command\n"; exec($command); } // ロックファイル削除 unlink($lockFilename); exit; /** * */ // inotifywaitログから更新対象ディレクトリ取得 function getUpdataDirList($inotifyLog) { $retArr = []; if(file_exists($inotifyLog)) { $fp = fopen($inotifyLog, 'r+'); $tmpArr = []; while(($l = fgets($fp)) !== false) { $l = trim($l); if(!$l) continue; $l = preg_replace('|/[^/]+$|', '/', $l); $tmpArr[$l] = true; } if($tmpArr) ftruncate($fp, 0); fclose($fp); $retArr = $tmpArr; foreach($tmpArr as $k => $v) { foreach($tmpArr as $k_ => $v_) { if($k == $k_) continue; if(isset($retArr[$k]) && strpos($k_, $k) === 0) unset($retArr[$k_]); } } } return array_keys($retArr); } // inotifywaitプロセス管理 function inotifywaitProcessManage($sourceDir, $inotifyLog) { $command = "inotifywait -mr -o {$inotifyLog} -e create,delete,modify,moved_to,moved_from --format %w {$sourceDir}"; // 既存inotifywaitプロセス存在チェック exec('ps x', $res); $pf = 0; foreach($res as $tmp) { if(strpos($tmp, $command) !== false) $pf++; } // ログが無いかログ最終更新から1時間以上経過していたら // 既存inotifywaitプロセスをkillした上で再度inotifywaitをバックグラウンド実行 if(!file_exists($inotifyLog) || time() - filemtime($inotifyLog) >= 3600 * 1 || !$pf) { // inotifywaitコマンドが見つからなかったら終了 if(exec('type inotifywait', $o, $r) && $r !== 0) { print "Cannot find `inotifywait` command.\n"; exit; } exec("pkill -f \"{$command}\""); $command .= " &"; print "$command\n"; if(file_exists($inotifyLog)) unlink($inotifyLog); exec($command); chmod($inotifyLog, 0600); return true; } return false; } |
ディレクトリの更新監視にinotifywaitを利用してはいますが、更新があった場合に自動的にmirroring.phpが実行されるわけではなくmirroring.phpはこれまで通りcronで定期的に実行し、スクリプト内でinotifywaitのログを参照することでその間に更新のあった監視対象内のサブディレクトリを取得してrsyncに渡すという動作になっています。
ここまで書いておいて何ですが、inotifyで監視してrsyncで同期という流れはLsyncdと似たようなことをしているわけなので、人によっては素直にLsyncdを導入したほうが楽かもしれません。