rsyncでHDDを定期バックアップ
このWordpressを運用してる僕の自宅サーバーですが、NASとしての役割もあるので、システム用のSSDとは別に2TBのファイルサーバー用HDDが積んであります。
で、今回バックアップ用に4TBのHDDを増設したので、2TBのHDDのデータをrsyncを使って4TBのHDDに定期的にバックアップされるようにしました。
仕様
こんな具合で動作すればいいかなって感じで決めます
- 毎日午前4時にバックアップが実行される
- 前回からの差分のみバックアップを行う
- 古いバックアップは自動で削除する
rsyncとハードリンク
rsyncでバックアップをする際に、--link-destオプションで前回のバックアップフォルダを指定すると、前回から変更のなかったファイルは前回のバックアップからのハードリンクとなり、ディスク容量を節約してくれます。
つまり、これだけで簡易的な差分バックアップになるのです。
しかも、ハードリンクではリンク元のファイルが削除されてもリンク先のファイルはそのまま使えるので、フルバックアップとか考えずに、古いバックアップはそのまま消しても大丈夫です。
なので、これを使うとバックアップがとっても楽にできます。
古いバックアップの削除
というわけで、古くなったバックアップは容赦なくrmしていきましょう。
ちなみに、削除する条件ですが、残すバックアップを以下に定め、これらに当てはまらないものを削除するようにしています。
- 当日のバックアップ
- 前日のバックアップ
- 前々日のバックアップ
- 当月の1日のバックアップ
- 当月の9日のバックアップ
- 当月の16日のバックアップ
- 当月の24日のバックアップ
- 先月の1日のバックアップ
- 先月の9日のバックアップ
- 先月の16日のバックアップ
- 先月の24日のバックアップ
- 2ヶ月前の1日のバックアップ
- 3ヶ月前の1日のバックアップ
- 4ヶ月前の1日のバックアップ
- 5ヶ月前の1日のバックアップ
- 6ヶ月前の1日のバックアップ
スクリプト
では、まずはバックアップと削除を行うスクリプトを書きましょう。
#!/bin/bash
source="/mnt/wd2t/"
destination="/mnt/wd4t/wd2t_backup"
today_destination="$destination/$(date +%Y-%m-%d)"
previous_backups_wildcard="$destination/*"
previous_backups=()
for last_backup in $previous_backups_wildcard; do
previous_backups+=( $last_backup )
done
if [[ $last_backup =~ \*$ ]]; then
rsync -a $source $today_destination
else
rsync -a --link-dest=$last_backup $source $today_destination
keep_dates=()
keep_dates+=( "$destination/$(date +%Y-%m-%d)" )
keep_dates+=( "$destination/$(date --date "1 day ago" +%Y-%m-%d)" )
keep_dates+=( "$destination/$(date --date "2 days ago" +%Y-%m-%d)" )
keep_dates+=( "$destination/$(date +%Y-%m-01)" )
keep_dates+=( "$destination/$(date +%Y-%m-09)" )
keep_dates+=( "$destination/$(date +%Y-%m-16)" )
keep_dates+=( "$destination/$(date +%Y-%m-24)" )
keep_dates+=( "$destination/$(date --date "1 month ago" +%Y-%m-01)" )
keep_dates+=( "$destination/$(date --date "1 month ago" +%Y-%m-09)" )
keep_dates+=( "$destination/$(date --date "1 month ago" +%Y-%m-16)" )
keep_dates+=( "$destination/$(date --date "1 month ago" +%Y-%m-24)" )
keep_dates+=( "$destination/$(date --date "2 months ago" +%Y-%m-01)" )
keep_dates+=( "$destination/$(date --date "3 months ago" +%Y-%m-01)" )
keep_dates+=( "$destination/$(date --date "4 months ago" +%Y-%m-01)" )
keep_dates+=( "$destination/$(date --date "5 months ago" +%Y-%m-01)" )
keep_dates+=( "$destination/$(date --date "6 months ago" +%Y-%m-01)" )
for p in ${previous_backups[@]}; do
for k in ${keep_dates[@]}; do
if [ $k = $p ]; then
continue 2
fi
done
rm -rf $p
done
fi
sourseがバックアップ元、destinationがバックアップ先です。
(ちなみに使っているHDDがWestern Digitalの物なので、wd2tやwd4tという名前にしてあります)
7〜11行目で過去のバックアップ一覧を取得して
12行目のif文は過去のバックアップがあるかどうかをチェックしています。
過去にバックアップがされていない場合は13行目で普通にバックアップが行われ、過去のバックアップがある場合は15行目で--link-destを指定した差分バックアップが行われます。
あとは17〜33行目で残すバックアップの一覧を生成して、35〜42で残すバックアップに存在しない日付のバックアップデータを削除しています。
ちなみに、2重のforループになっているのは、シェルスクリプトだと集合演算が面倒そうだったのでこうなりました。
cronでスクリプトを定期実行
今回バックアップ先に選んでいるwd2t_backupというディレクトリはrootからしかアクセスできないようにしてあるので、rootのcronで実行する必要があります。
なので、先ほどのスクリプトをrootのホームディレクトリ以下に配置(今回は/root/binというディレクトリを作ってその中に格納)し、sudo suでrootになっている状態でcrontab -eを実行してcronを設定します。
0 4 * * * /root/bin/wd2t_backup.sh
こうすれば毎日午前4時にwd2t_backup.shが実行されるようになります。
現状バックアップを始めてからまだそんなに時間が経っていないためバックアップは少ないですが、うまくバックアップできてそうですね。
ちゃんとファイルの中身が存在していることも確認してます。
システムのバックアップを取るにはrsyncだけでは不足かもしれませんが、個人のファイルサーバーのバックアップ程度ならこれで十分いけるのではないでしょうか。
バックアップは大事ですよ。
–deleteオプションはバックアップ先として既に存在するディレクトリを指定した場合に意味を持つものですので、1日1回の実行で常にバックアップ先ディレクトリが日付で変わるこの使い方の場合、–deleteオプションを付けても意味はありません。