【Mac・ファイル同期】rsync・sshfs・fswatchを使ってローカルとサーバーを同期したら色々便利だった

サーバーにファイルをアップロードする基本として

  1. ローカルのファイルを編集
  2. FTPでそのファイルをサーバーへアップロード

っていう作業がありますよね?
私もつい最近までの数年間この作業をしておりました。
絶対自動化できることは分かっていたのですが、それを組むのが面倒でやってなかったんですよねー。
ということで今回重い腰を上げて自動化に踏み切ったので、その軌跡を残したいと思います。

環境

  • ローカル
    • OS: macOS X Catalina (10.15.1)
    • Shell: Bash
  • サーバー
    • centos 6系

sshでローカルとサーバーが接続できること。
MacにHomebrewが入っていることを前提としてお話していきますよー。

まずは必要なものをインストール

使用するのは以下のもの。

  • rsync
  • sshfs(osxfuse)
  • fswatch

まずはこれら全てをbrewでインストールします。

rsync

rsyncはAディレクトリをBディレクトリに同期するコマンドです。

$ brew install rsync

実はmacには標準で入ってたりします。でも新しいバージョンの方が気持ちいいからHomebrewで改めて入れます。

sshfs ( osxfuse )

sshで接続した先のディレクトリをローカルでマウントすることができるコマンドです。
これめちゃくちゃ便利なので是非入れてみてください。
sshfsはそれをインストールする前にosxfuseをインストールする必要があります。

$ brew cask install osxfuse
$ brew install sshfs

fswatch

ディレクトリを監視して変更されたファイルを都度、標準出力に吐き出すコマンドです。

$ brew install fswatch

これで必要なものは出揃いました!
それでは新しくインストールしたこのコマンドたちを少し紹介していきますね。

rsyncでディレクトリ間の同期!

$ rsync <options> source dest

使い方はいたって簡単でsource(同期元)dest(同期先)に同期します。
例えば~/dev/sourcedir(同期元)~/dev/destdir(同期先)に同期する場合は以下のようにします。

$ rsync ~/dev/sourcedir/ ~/dev/destdir/

~/dev/sourcedir/のように最後のスラッシュを入れないと~/dev/destdir/sourcedirに同期されるので注意!
あと配下のディレクトリを再帰的に同期する場合には-rのオプションが必要です!

そしてrsyncはリモートのディレクトリも同期元あるいは同期先に指定できるんです。

$ rsync ~/dev/sourcedir/ USER@HOST:/home/user/www/destdir/

そしてrsyncはオプションで結構細かく設定できます。

  • 同期元に存在しないファイルは削除(–delete)
  • 同期の対象から除外するファイルの設定(–exclude=ファイル)
  • タイムスタンプもコピー(-t)
  • チェックサムで差分同期(-c)
  • 配下のディレクトリを再帰的に同期(-r)

などなど。

sshfsでリモートのディレクトリをマウントしてローカルのように扱う

$ sshfs USER@HOST:/path/to/remote /path/to/mountpoint

こちらも使い方がめちゃくちゃ簡単です。
リモートの/hoge/fooディレクトリをローカルの~/dev/mountpointにマウントしたい時は

$ sshfs USER@HOST:/hoge/foo ~/dev/mountpoint

でマウントできます!
Finderで確認すると

これが

こうなりました。
リモートのファイルをFinderで見られるのは非常にありがたいですよね!
もちろんターミナルでもアクセスできます。

ちなみにアンマウントは

$ umount ~/dev/mountpoint
# または
$ diskutil unmount ~/dev/mountpoint
# 最終手段は
$ killall sshfs

で行います。もしかしたらumountに失敗するかもしれません。

fswatchでディレクトリを監視してファイルの変更を検知!

$ fswatch <options> target

これだけでディレクトリの変更を検知できます。
ちょっと見てみましょう

$ fswatch ~/dev/targetdir
# test.htmlを作成
/Users/username/dev/targetdir/test.html
# test.htmlを更新
/Users/username/dev/targetdir/test.html
# style.cssを作成
/Users/username/dev/targetdir/style.css
# test.htmlを削除
/Users/username/dev/targetdir/test.html
# subdirディレクトリを作成
/Users/username/dev/targetdir/subdir
# subdirディレクトリにindex.htmlを作成
/Users/username/dev/targetdir/subdir/index.html

このように変更のあったファイルが標準入力で吐き出されます。
オプション次第で出力されるものが変わります。
このままだと全く使いようがありませんが、この出力をxargsに渡すことで真価を発揮します。

$ fswatch ~/dev/targetdir | xargs -n1 -I{} program

これで変更があるたびにprogramが起動されるようになります!
fswatchのオプションで監視除外のファイルを指定できたりするので.gitとか除外した方が良いかも?

長くなりましたが自動同期を開始します!

以下の流れで行います。

  1. sshfsでリモートのディレクトリをマウント
  2. fswatchでローカルの作業ディレクトリを監視
  3. fswatchの出力をxargsでrsyncに渡す
  4. rsyncはローカルの作業ディレクトリとマウントポイントを同期

リモートのディレクトリは「USER@HOST:/home/user/remote」
マウントポジションは「~/dev/mount/remote」
ローカルの作業ディレクトリは「~/dev/work」
と想定して説明します。
皆さんは適宜自分の環境に変換してくださいね!

最小規模での構築ならこれだけで自動同期です。

$ sshfs USER@HOST:/home/user/remote ~/dev/mount/remote
$ fswatch ~/dev/work | args -n1 -I{} rsync -rc --delete ~/dev/work/ ~/dev/mount/remote/

これで変更があるたびに
作業ディレクトリとマウントポジションがリアルタイム同期
つまりローカルとリモートがリアルタイム同期します。

実際には

  • fswatchの除外オプションで.git系を指定する
  • rsyncの除外オプションでも.git系を指定する
  • ディレクトリ内の.gitignoreを読み込んでrsyncの除外オプションにそれらを追加する

とか色々組み込んだシェルスクリプトで同期のスタートストップを行なっています。

自動同期はやっぱ楽よね

今までは別に苦じゃないしーって手動FTPアップロードしてたけど、自動同期の素晴らしさを知ってしまったらもう戻れないです!
どうか皆さんも素敵な自動化ライフを!

余談ですが、ただの自動同期ならsshfsは全く必要ありません。
rsyncでリモート同期できますからね。
でもマウントできた方が便利だし、確認をFinderでできるのはやっぱり楽ですよ!
CUIに凝ってる時は黒い画面いじってる俺カッケー。GUIで作業とか素人のやることですわって思うけど、やっぱGUIは見やすいですわ。