クライアントのMacbookからサーバのストレージに定期的にバックアップを取るため、cronを動かそうと考えましたが、どうもmacOSではlaunchdが推奨されるようなので、launchdを使います。
何番手封じ感はありますが、自身の備忘録がてら書いてみます。
今回実現したい内容
現在のシステムは次のようになります。ファイル共有にはSambaを利用しています。
今回はMacbookのiTunesフォルダの完璧なクローンを、リモートコンピュータ(RaspberryPi4)の外付けディレクトリ上に定期的に作成します。
rsyncについて
rsyxcはmacOSに標準インストールされていますが、バージョンが古すぎて使い物にならないので、home brew 🍺 で最新版を入れます。
brew.sh
rsyncのインストール
homebrewを導入したらrsyncをインストールします。libiconvはファイル名の文字コードを合わせるために利用します。
$ brew update $ brew install libiconv $brew install rsync
rsyncの使い方
書式は以下になります。
$ rsync <オプション> <同期元> <同期先>
<同期元>と<同期先>にリモート上のディレクトリの指定が可能です。(user@192.168.0.2:/home/pi/など)
rsyncのオプション設定
今回はリモートディレクトリ上にホストの完全なクローンを作成するので、rsyncのオプションは以下のものを使用します。
オプション名 | 動作 |
---|---|
a | 全てのディレクトリに含まれるファイルをパーミッション・タイムスタンプ・その他諸々保持して処理する。 |
u | 同期先のファイルが同期元より新しい場合はスキップする。スキップするだけで同期元の更新はしない。 |
z | 圧縮して送信する。処理負荷を引き換えに通信速度を得る。 |
--delete | 同期元にないファイルは同期先から削除する。取り扱い注意 |
--iconv=<同期元>,<同期先> | 転送の際に文字コードを変換する。macOSはUTF-8-Macが採用されており、UTF-8ではない。今回は--iconv=UTF-8-MAC,UTF-8を指定。ライブラリ「libiconv」が必要。 |
--exclude <ワイルドカードマスク> | 除外ファイルを指定する。今回は.DS-Storeや._<削除したファイル名>といったMacが自動生成する |
--log-file=<ファイル名> | ログファイルを保存する。-vvvで見れる内容には日付が書いていないため、標準出力のリダイレクトではなくこちらを利用。 |
rsyncで実行するコマンドは次のようになります。 rsyncは/usr/local/bin/rsync
として呼び出します。
$ rsync -auz --delete --iconv=UTF-8-MAC,UTF-8 --exclude '.*' --log-file=$log "$src" "$dest"
スクリプトの作成
このコマンドを直接launchdで実行しても良いのですが、rsyncの開始時と終了時を簡単に識別したいので、出力ログに記述します。
#!/bin/bash src=$1 dest=$2 log=$3 echo "$(date +'%Y/%m/%d %T') Start rsync $src => $dest" /usr/local/bin/rsync -auz --delete --iconv=UTF-8-MAC,UTF-8 --exclude '.*' --log-file=$log "$src" "$dest" echo "$(date +'%Y/%m/%d %T') Finish rsync $src = $dest"
保存先はログインユーザーが参照可能なディレクトリならどこでも可能です。
保存した後はchmod 700 nasSync.sh
等で実行権限を付与します。LaunchdAgentはログインユーザーで実行するため、所有者以外のパーティションの設定は不要です。
Lauchdの実行環境の作成
cronより書くことが多いです。cronとは違って実行できない場合はキューに保存し、実行できる時に実行してくれる機能があるらしいです。
plistファイルの作成・配置
Launchdの設定ファイル(ここではmycron.iTunesSync.plist
)を~/Library/LaunchAgents/
に配置します。
今回は毎日0時0分に動作するように設定しています。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>mycron.iTunesSync</string> <key>ProgramArguments</key> <array> <string> ここに.shのフルパスを入力 </string> <string> ここに.sh第一引数(src)を入力 </string> <string> ここに.sh第二引数(dest)を入力 </string> <string> ここに.sh第三引数(log path)を入力 </string> </array> <key>StartCalendarInterval</key> <dict> <key>Hour</key> <integer>0</integer> <key>Minute</key> <integer>0</integer> </dict> <key>StandardOutPath</key> <string> 標準出力先を入力 </string> <key>StandardErrorPath</key> <string> 標準エラー出力先を入力 </string> </dict> </plist>
スクリプトへの引数は
SSH公開鍵を設定
Launchdでコマンドを実行するにあたり、SSH接続の際にリモートのパスワードを要求されてはうまくバックアップを作成できないので、SSH公開鍵をリモートのログインユーザーに保存します。
Macbookにて
$ mkdir ~/.ssh $ cd ~/ $ ssh-keygen -t rsa
すると、.sshディレクトリ内にid_rsa.pubが作成されるので、これをリモートに送信します。マウント済みであればcpコマンドでよし、マウントしていなければscpコマンド等で送ります。
id_rsa.pubを送信したら、ログインユーザの.ssh/authorized_keysファイルに追記します。
$ mkdir ~/.ssh $ cat <id_rsa.pubのパス> >> ~/.ssh/authorized_keys
最後にサーバーをknown_hostsに追加するため、一度接続を試みます。
$ ssh <リモートのauthorized_keysに登録したログインユーザー名>@<リモートのIPアドレス>
二度目の接続でパスワードを尋ねられなくなったら登録完了です。
Launchdの登録
前手順で作成したplistをlaunchdに読み込みます。(例ではmycron.iTunesSync.plist)
$ launchctl load ~/Library/LaunchAgents/mycron.iTunesSync.plist
これで全ての手順が完了です!
感想
cronがMacスリープ時に動かないからlaunchdを使う必要があったり、rsyncについて調べていたら2日くらいハマってしまい疲れました。
しばらくはログを見て今日もラズパイ生きてるなあと見守ってあげようと思います👏
トラブル対応について
ここで設定するまでにいろいろな問題が発生し、解決するために奮闘していました。
nkhnd.hatenablog.jp
以前はこの記事のおまけで記述していましたが、意外と多くの方が検索されているようだったため、問題解決を念頭に体裁を整え読みやすくしました。
この記事を読んでいて「どうしてこんな回りくどい設定方法なのだろう」「この記事の通りに設定したのにできなかった」時は、参考にしてください。