lsyncdからfswatchへの移行で学んだコネタたち
パートタイムでお手伝いしているプロダクトで、開発用サーバーへの同期ツールが動かない、という問題がありました。解決の過程で、いくつかのちょっとした学びがあったのでまとめておきます。特に目新しいものはありませんが、狭い特定の用途では役立つかもしれません。
おしながき
- 複数のシェルスクリプトで共通の設定値を扱う
- Luaスクリプトに設定値を埋め込む
- 正規表現のデリミタ(区切り文字)を変更する
fswatch
で更新を監視して同期するrsync
の引数SRC
には末尾スラッシュをつける- Markdown のコードブロックに
`
(バッククオート)を書く
前提
- 手元での修正を
EC2
上の開発環境に同期する、という開発ワークロード - 初回はどかっと
rsync
で同期する - 開発中は変更があったファイルを監視し同期する
lsyncd
を使用
- いくつか課題があった
- 設定値が複数のシェルスクリプトにハードコードされていた
- 環境による差分があるので別スクリプトに分割されていた
- 手元の
rsync
のパスが環境によって違うとか
- 手元の
lsyncd
が動かない環境が出てきた
複数のシェルスクリプトで共通の設定値を扱う
設定値が複数のスクリプトにハードコードされていたので共通化した。
config.txt
のような設定ファイルを用意し、それぞれのスクリプトでsource
する- 個別の環境によって設定値が変わる場合
config.txt.sample
のようなファイルに設定例を記述しgitリポジトリに含める.gitignore
でconfig.txt
をバージョン管理から外しておく- 各自の手元で
config.txt
という名前でコピーして使う
- バッククオートによるコマンド実行の結果も取れる
- 例 :
RSYNC_COMMAND=`which rsync`
- 例 :
- ホームディレクトリは
~
では取れないので環境変数HOME
を使う- 例 :
SRC_DIR="${HOME}/project"
- 例 :
- 個別の環境によって設定値が変わる場合
- 例 :
config.txt.sample
RSYNC_COMMAND=`which rsync`
SRC_DIR="${HOME}/project"
DST_DIR="example.com:/path/to/target"
SSH_KEY="${HOME}/.ssh/id_rsa"
Luaスクリプトに設定値を埋め込む
lsyncd の設定ファイルは Lua で記述されている。こちらも設定値がハードコードされていたので共通化した。
- Luaでは
os.getenv('KEY')
で環境変数が取得できる - しかし
lsyncd
の設定ファイルとして動くときは取得できないようだ(要出典) - しかたないので envsubst を使った
lsyncd_config.lua.template
を用意しておく.gitignore
でconfig.lua
をバージョン管理から外しておく- 環境変数で置き換えたいところをシェルスクリプトと同様の記法で記述しておく
- 例 :
source = "${SRC_DIR}"
- 例 :
- 例 :
lsyncd_config.lua.template
sync {
default.rsync,
source = "${SRC_DIR}",
target = "${DST_DIR}",
delay = 15,
rsync = {
binary = "${RSYNC_COMMAND}",
archive = true,
compress = true
}
}
- 実行時に
config.lua
を生成する- 例 :
envsubst < lsyncd_config.lua.template > lsyncd_config.lua
- できあがった
config.lua
を使ってlsyncd
を起動する- 例 :
lsyncd lsyncd_config.lua
- 例 :
正規表現のデリミタ(区切り文字)を変更する
パスの文字列には /
が多用されるが、正規表現のデリミタもデフォルトが /
であるため、エスケープする必要がある。面倒だし読みにくくなるので、デリミタを変更すると便利。
referred to : sedの正規表現のデリミタに、/ 以外を使用する #Ruby - Qiita 🍣
- 例 : ローカルで変更があったファイルパスをリモートの同期先パスに置換する
target
には変更があったファイルのフルパスが格納されている- 以下はデリミタを
!
に変更している
target_file=`echo ${event} | sed "-e s!${SRC_DIR}!${DST_DIR}/!"`
fswatch
で更新を監視して同期する
lsyncd
が手元のmacOSでうまく動かなかったため、ファイルの更新監視を fswatch で行うようにした。
referred to : [macOS] Terminalでファイルを監視し更新されたら指定の処理をする - fswatch - ねこの足跡R 🍣
- 例 :
rsync_with_fswatch.sh
#!/bin/bash
source ./config.txt
ssh-add "${SSH_KEY}"
# 監視するディレクトリの存在チェック
if [[ ! -d ${SRC_DIR} ]]; then
echo "[error] Not Found target directory ${TARGET_DIR}" >&2
exit
fi
fswatch -0 ${SRC_DIR} | while read -d "" event; do
# ignoreパターンにマッチしたら処理をスキップ
if [[ \
${event} =~ \.git\/ || \
${event} =~ \/logs\/ || \
${event} =~ \.DS_Store \
]];then
continue
fi
# 変更があったローカルのファイルパスを同期先のパスに変更する
target_file=`echo ${event} | sed "-e s!${SRC_DIR}!${DST_DIR}/!"`
echo "sync ${event}"
# ローカルの削除を反映するため `r` と `delete` オプションをつける
# `rsync` する範囲が狭いほど速いので、変更があったファイルが置かれたディレクトリを同期する
${RSYNC_BINARY} \
-rv \
--delete \
`dirname ${event}`/ `dirname ${target_file}`
done
- 真面目にdaemonizeするほどヘビーなユースケースでもなさそうなので、同期専用のターミナルを開いておく、で割り切った
[TODO]
1ファイルずつsyncするのであれば、ローカルの存在をチェックしてscp
とrm
を使い分ければより速くなりそう
rsync
の引数 SRC
には末尾スラッシュをつける
rsync
関連のスクリプトを書くと毎回忘れてうわっとなるので自戒を込めて。
ディレクトリをsyncするとき、末尾スラッシュが…
- ある場合 : そのディレクトリ以下をsyncする
- ない場合 : そのディレクトリ そのもの をsyncする
今回のようにあるディレクトリ以下をsyncする場合は以下のように書くことで、 /src/foo/bar
以下の変更があったファイルが /dst/foo/bar
にsyncされる。
rsync /src/foo/bar/ /dst/foo/bar
末尾スラッシュを忘れると、 /src/foo/bar
そのもの が /dst/foo/bar
にsyncされるので、結果として /dst/foo/bar/bar
ができあがってしまう。
Markdown のコードブロックに `
(バッククオート)を書く
この記事にはバッククオートが多用されているため調べた。
referred to : Markdownでコードブロックにバッククオートを含める方法 🍣
つまり、こう書くと
こうなる。
まとめ
ひとつひとつはコネタだし知ってる人には常識みたいなことですが、それらの蓄積が問題解決になるんだなあ、と改めて認識したタスクでした。需要があるかは気にせず記事にするのもひさしぶりで、アウトプットする楽しさを思い出しました。
ということでまとめとしては…
- 日々の仕事の問題解決をアウトプットするのは大事だし楽しい
- アウトプットは謙虚に、でも萎縮せず
rsync
では末尾スラッシュを忘れるな
です!
see also : 「日々のアウトプットが変える!あなたのエンジニア・ライフ」というイベントに登壇してきたよ #forkwell // Kwappa研究開発室