Windows+Dockerの環境が重い問題をunisonで解決する

Windows + Dockerの開発環境でアプリケーションが重い問題をunisonというツールで解決したので共有します。
ネット上にいくつかunisonを使っている記事はあるのですが、より簡単に導入でき、アプリケーションへの影響が少ない方法にたどり着いたので記録を残します。

ちなみに自分はLaravelアプリケーションの開発でこの環境を使用しています。
3か月ほど運用していて、普通に開発している分には大きな問題はありませんでした。
composerでパッケージを全てバージョンアップした際に同期がおかしくなったぐらいです。
一応メリデメを上げると以下のとおりです。

* メリット
  * アプリケーションの実行が早くなる
* デメリット
  * 作業開始時にunisonクライアントを起動するひと手間が増える

なお、docker-composeを使用している前提の構成になっております。
それでは見ていきましょう。

# 大まかな構成について

[unison](https://www.cis.upenn.edu/~bcpierce/unison/)というファイル同期ツールを使います

以下の図のように、**unisonクライアント**をWindows側にインストールし、**unisonサーバー**をdocker-composeのサービスの１つとして起動します。
この２つのunison間で**ソースコードを同期**します。
（既存の`docker-compose.yml`にunisonサーバを追加する事になります。）

unisonで同期したソースコードを**Dockerボリューム経由で他のコンテナにも共有**します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71575/443aa00e-1814-a211-554c-cdcd71f41a7f.png)

# サンプルコードを用意しました

本記事の構成をすぐ試せるサンプルを用意しました。
詳しくはリポジトリのREADMEを参照ください。

リポジトリ： https://github.com/t-kuni/docker-unison-example

# Unisonサーバーを起動します

docker-compose.ymlにunisonサーバーを追加します。

```yml
version: '3'
services:
  # Unisonサーバー
  unison:
    image: onnimonni/unison
    environment:
      - UNISON_DIR=/tmp/unison # 同期したファイルを書き込むフォルダを指定する
    ports:
      - "5000:5000"
    volumes:
      - app-code:/tmp/unison # Dockerボリュームをマウントするフォルダを指定する
    restart: always
  # その他のコンテナ
  apache:
    image: httpd:2.4
    volumes:
      - app-code:/usr/local/apache2/htdocs/ # その他のコンテナにも、Dockerボリューム経由でソースコードを共有する
    ports:
      - "8080:80"
volumes:
  app-code:
```

準備が出来たら`docker-compose up -d`でコンテナを起動しましょう

# Unisonクライアントをダウンロードします

以下のページからunisonクライアントをダウンロードします。

unisonクライアント： https://github.com/bcpierce00/unison/releases

サーバーのバージョンと合わせる必要があるため今回は`2.51.2`のクライアントをダウンロードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71575/2019b040-afe8-c7c8-3cb4-ef4106e6dacd.png)

`docker-compose.yml`と同じ階層に**unisonフォルダを作成してunisonクライアントを配置**します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71575/89aaa722-4902-cc16-b88a-3f502e3ea585.png)


# Unisonクライアントを起動します

unisonクライアントを起動する**batファイル**を用意します。

docker-compose.ymlと同じフォルダにsync.batという名前で以下のコードを作成します。

```bat
:LOOP
    .\unison\unison . socket://192.168.99.100:5000/ ^
        -repeat watch ^
        -auto ^
        -batch ^
        -prefer newer ^
        -ignore "Path .git" ^
        -ignore "Path .idea" ^
        -ignore "Path node_modules"
goto :LOOP
```

上記のコードは、unisonを起動してカレントフォルダを同期しています。

IPアドレスの部分（`192.168.99.100`）にはコンテナのIPアドレスを入力してください。
また、まれにクライアントが落ちる事があるので無限ループで再起動するようにしています

## unisonのオプションについて

unisonオプションの意味は以下の通りです。

* -repeat watch
    * ファイルを監視し変更があれば同期を行います
* -auto
    * コンフリクト時の確認を自動で許可する
* -batch
    * 実行時の確認を行わない
* prefer newer
    * コンフリクト時の解決方法として「より新しい物」を優先します
* ignore
    * 同期しないファイルを指定します。

`ignore`オプションについては、必要に応じて追加してください。
例えばLaravel環境ですと`storeage`配下のいくつかのファイルは除外した方が良いです。

その他のオプションについては[公式ドキュメント](https://www.cis.upenn.edu/~bcpierce/unison/download/releases/stable/unison-manual.html)を参照してください。

準備が出来たら`sync.bat`を起動します。
あとは自動的に同期を行ってくれます。

# 動作確認

以下のコードを`index.html`としてdocker-compose.ymlと同じフォルダに配置し、unisonで同期させます。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>title</title>
</head>
<body>
    <h1>Hello unison!</h1>
    <p>このメッセージが表示されればunisonでの同期に成功しています</p>
    <p>index.htmlを書き換えて再同期されるか試してみてください</p>
</body>
</html>
```

`192.168.99.100:8080`（IPアドレスはコンテナのIPに置き換えてください）に接続して以下の画面が表示されればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71575/1b51b2a4-eea2-5308-906c-af2e1e30c0b2.png)


# 同期が壊れた時は？

同期に使用しているDockerボリューム（サンプルでは`app-code`）を`docker volume rm`で削除して、再同期してください。

以上です！