Edited at
WHITEPLUSDay 24

docker-syncを使わないホスト・コンテナ間での爆速ファイル同期

More than 1 year has passed since last update.


追記 20170407 Docker for Macでconsistencyを指定することでパフォーマンスを改善できるようになりました。

unisonを使わなくても本体の機能である程度解決できるようになりました。

下記の計測してるので参照ください。

http://qiita.com/kai-zoa/items/5f38909d37e0e4e36a28


元記事

この記事はWHITEPLUS Advent Calendar 2016 24日目になります。

開発環境整備担当兼クリーニング師の kai-zoa です。

20日目に書いたDocker for Macのファイル共有が遅いので計測してみるでdocker-for-mac公式は問題を認識しているが優先度低いという旨のコメントを頂いたので、なにかしら対策を講じたい気持ちになり調査しました。

すでにdocker-syncを使う方法は他の方が紹介されているので、今回は使わない方法について書きたいと思います。

実際に開発環境に組み込んでデザイナー・エンジニアの両チームに共有するのにあらかじめrubyを入れてもらったり、gemを使えるようにしてもらうのが面倒くさい方にオススメです。(というのが私の都合です)

環境はmacOSです。


unisonを使います

いきなりですが、これが答えです。

docker-syncでも使われていますが、同じようにunisonを使ってホストとコンテナのファイルシステムを相互同期します。

DockerHubにalpineベースのシンプルなunisonサーバーのDockerイメージがあったので参考にしました。

onnimonni/unison

このようにunisonサーバーをデータコンテナーにして、同期するディレクトリはアプリケーション側のボリュームにマウントします。

docker-composeの定義はこんな感じです。


docker-compose.yml

version: '2'

services:
data:
image: whiteplus/unison
container_name: piyo_data
volumes:
- /var/www/piyo
ports:
- "5000:5000"
environment:
- UNISON_DIR=/var/www/piyo
web:
container_name: piyo_web
build: .
depends_on:
- data
volumes_from:
- data
ports:
- "80:80"
- "443:443"
environment:
- NGINX_PATH_PREFIX=/var/www/piyo


※ データコンテナー側にinotify-toolsを入れたかったのでonnimonni/unisonに手を入れてwhiteplus/unisonにしてます。

全ソースはこちら

github.com/WHITEPLUS/advent-calendar2016-docker-unison-example


動作確認

起動します。

$ docker-compose up -d

起動直後はプロジェクトのファイルを同期してないのでwebにアクセスすると404になります。

$ open http://localhost


ホストのファイルをデータコンテナに同期する

それではunisonクライアントをインストールして、./webappのソースをデータコンテナに同期してみます。

$ brew install unison

$ unison ./webapp socket://127.0.0.1:5000/ -auto -batch
Contacting server...
Connected

Waiting for changes from server
Reconciling changes
dir ----> html
Propagating updates

Synchronization complete at 21:12:35 (2 items transferred, 0 skipped, 0 failed)

$ open http://localhost #開いてみる

WAO! ちゃんと表示されました


データコンテナからファイルを編集して同期する

それでは逆にデータコンテナからファイルを編集して同期してみましょう

$ docker exec -it piyo_data sh # データコンテナに入る

$ cd /var/www/piyo/html/
$ echo PIYOPIYO > index.html #データコンテナからファイルの内容を変更
$ exit

$ unison ./webapp socket://127.0.0.1:5000/ -auto -batch #同期
Contacting server...
Connected

Looking for changes
Waiting for changes from server
Reconciling changes
<---- changed html/index.html
Propagating updates

Synchronization complete at 21:21:53 (1 item transferred, 0 skipped, 0 failed)

$ cat ./webapp/html/index.html #変更がホストに反映されてることを確認する
PIYOPIYO

WAO! うまくいきました


ファイル変更を監視して同期する

ここまでできたら、ホストとコンテナでお互いのファイル変更イベントを検知して、unisonを動かすようにすれば完璧ですね。

そこでお互いのinotifyイベントを監視して、ホスト側にログ出力し、tailからxargsでunisonに繋ぐスクリプトを書いてみました。(githubの方にもコミットしています)

unison-fsmonitorをmacOSに入れればこんなもの必要ないかもしれないですがまだ試してません


sync.sh

#! /usr/bin/env bash

LOCAL_DIR=./webapp
REMOTE_DIR=/var/www/piyo
REMOTE_CID=piyo_data

recv_local() {
while read -r event; do
test -n "${event}" && \
echo "LOCAL: ${event}" >> events.log
done
}

recv_remote() {
while read -r event; do
event=$(echo -n $event \
| grep -E '(CREATE|MOVE|MOVED_TO|MOVED_FROM|MODIFY|ATTRIB|DELETE|DELETE_SELF)[,:]' \
| sed -e 's/^[A-Z,_]*://g' \
| tr -d '\n')
test -n "${event}" && \
echo "REMOTE: ${event}" >> events.log
done
}

UNISON="unison ${LOCAL_DIR} socket://127.0.0.1:5000/ -auto -batch"

${UNISON}

rm -f events.log
touch events.log

fswatch ${LOCAL_DIR} 2>&1 | recv_local &
CHILDREN=$!

docker exec -it ${REMOTE_CID} inotifywait -m -r --format '%e:%w%f' ${REMOTE_DIR} 2>&1 | recv_remote &
CHILDREN="${CHILDREN} $!"

trap "kill -KILL ${CHILDREN} > /dev/null 2>&1" INT TERM

tail -f ./events.log | xargs -I{} ${UNISON}


実行するとホストとコンテナ双方のファイル監視と同期をし続けてくれます。

$ ./sync.sh



積み残し

上記スクリプトだとunisonが同期した結果のファイル変更イベントを検知してunisonが起動するという無駄な動きもしてしまうので改良の余地があるなと思いました。

これ以上、並行処理でなんやかんややるならGoで書きたいですね‥

その前にfsmonitorを使った場合の動作確認をしよう!


明日

exmeatのmeatの話ですっ


ホワイトプラスではエンジニアを募集しています

ホワイトプラスでは、新しい技術にどんどん挑戦したい!という技術で事業に貢献したいエンジニアを募集しております。!