マルチOS対応Docker開発環境の作り方【Mac対応編】

この投稿は  [Magento Advent Calendar 2019](https://adventar.org/calendars/4213) の11日目です。

皆さんDocker使ってますか？

以前はVirtualbox+Vagrantを使って開発環境を整えていたのですが、動きが遅い重い！
当時個人的に気になっていたDockerに（趣味半分で）乗り換えてみたところ、早い軽いで素晴らしいものでした。

私はもっぱらLinux（Ubuntu）をメインで使っているのですが、社内にはMacやWindowsを使っている開発者もいます。

せっかく作ったし他のOSでも使えるようにしよう、まずはMacで試してみよう！と思っていたところ、ある問題に阻まれてしまったのでした。

## Docker for Macの欠点

Linuxで使っていたイメージや設定ファイルを移動させ、起動させるところまでは順調でした。
ですが…

**明らかに動きが遅い……！！！**

実はこの問題、Docker for Macでは有名で、以前私もこの問題を解決しようと悪戦苦闘していました。
[Docker Meetup Kansai](https://dockerkansai.connpass.com/) で発表したスライドがあるので、もし興味があればそちらもご覧ください。

> ![Docker開発環境のMac対応に苦戦した話.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237636/83a1a41a-1f54-c272-f5ee-56dbad4b631c.jpeg)
> [Docker開発環境のMac対応に苦戦した話](https://docs.google.com/presentation/d/1Gi2fQSw2ceyv9qYw1XXm1B_wy_sOGJIFjeMtSdEqBOA/)

このときは1/4程度しか改善されないという、早くはなったが実用的ではないという悲しい結果でした。


今回はその続きの話となります。

その後調べたところ、2つ効果があるかも知れない解決策を見つけることが出来ました。

## NFS Sharing Volume

実は、dockerにはVolumeにNFSをマウントする機能があります。
ですが、Docker for Macの遅さはmacOSとLinuxコンテナのファイルシステムの違いが原因なので、NFS越しにマウントしたところで早くなるはず無いだろう…と思っていました。

ところが、

> [Docker for Mac でも快適な Symfony 開発環境を作りたい](https://tamakiii.hatenablog.com/entry/docker-for-mac-nfs-for-symfony)

こちらの記事によるとNFS Volumeを使うことでかなり改善されることが判明しました。
なぜ早くなるのかは謎ですが、効果があるかも知れないなら試すしかありません！

Macでnfsdを動かすには、`/etc/nfs.conf` と `/etc/exports` を設定しておく必要があります。

```plaintext:/etc/nfs.conf
#
# nfs.conf: the NFS configuration file
#
nfs.server.mount.require_resv_port = 0
```
```plaintext:/etc/exports
/System/Volumes/Data/Users/{USERNAME}/{PASS_TO_PROJECTROOT} localhost -alldirs -mapall={uid}:{gid}
```

`-mapall` オプションで指定したUIDとGIDは、NFSでアクセスした際に指定したユーザーのものとして扱われます。

macOS Catalinaをお使いの方には注意点が二点あって、一つは `/Users/{USERNAME}` で始まるパスを指定してもエラーになってしまいます。
詳しくは以下の記事が参考になるかと思います。

> [macOS CatalinaからNFS exportsするには](https://qiita.com/imaiworks/items/b657046ea499ec8fd95c)

もう一点は、DocumentsやDownloads、Desktop下を使う場合は明示的に権限を付与（？）しなくてはいけないようです。

> [Docker: the Problem with MacOS Catalina](https://objekt.click/2019/11/docker-the-problem-with-macos-catalina/)

以上が設定できれば `sudo nfsd start` で起動するとホスト側の設定は終了です。

docker-compose.ymlの方にもNFSを使う設定をします。

```yaml:docker-compose.yml
version: "3"
services:
  nfs:
    image: ubuntu:18.04
    volumes:
      - src:/src:nocopy
volumes:
  src:
    driver: local
    driver_opts:
      type: nfs
      device: ":/System/Volumes/Data/Users/{USERNAME}/{PASS_TO_PROJECTROOT}"
      o: addr=host.docker.internal,rw,nolock,hard,nointr,nfsvers=3
```

services下のvolumesに `:nocopy` を忘れず、driver_optsのdeviceにexportsで指定したパスを書くことを忘れずに！
これでNFS Volumeを使ったコンテナが起動できます✌️

欠点を挙げるとするならば、プロジェクトごとにdocker-compose.ymlにパスを書く必要があって手間という点です。
Gitで管理していたりすると、誤Pushとかがありそうで怖いですよね…。

## Docker-sync

もう一つは高速にホストとコンテナ間を同期するためのツール、docker-syncです。

> http://docker-sync.io/

このツールは、ホストとコンテナ間でrsyncやunisonなどを使って高速な同期を実現しているようです。
rsyncの方が高速（？）のようですが同期が双方向ではなく一方通行（ホスト→コンテナ）なのがネックなので、今回はunisonを使ってみます。

```bash
brew install unison
brew install eugenmayer/dockersync/unox
gem install docker-sync
```

以上のコマンドでインストールが完了します。
詳しいインストール手順は[公式ドキュメント](https://docker-sync.readthedocs.io/en/latest/getting-started/installation.html)をご覧ください。

docker-syncには `docker-sync.yml` が必要になります。

```yaml:docker-sync.yml
version: "2"
syncs:
  src:
    src: '/{PASS_TO_PROJECTROOT}'
    sync_strategy: 'unison'
    sync_userid: '{UID}'
```

```yaml:docker-compose.yml
version: "3"
services:
  nfs:
    image: ubuntu:18.04
    volumes:
      - src:/src
volumes:
  src:
    external: true
```

docker-sync.ymlとdocker-compose.yml間でVolume名を統一しないと正しく起動しないので注意が必要です。
設定ファイルが準備できたら、

```bash
docker-sync start
```

このコマンドで起動してあげると、Volumeを作成して同期が開始されるという仕組みになっています。
Docker-syncを起動した状態でdockerコンテナにVolumeをマウントすると、ホスト-コンテナ間が同期されている状態になる、といった具合です。

Docker-syncの欠点は別途インストールが必要な点と、このコマンドを打つ手間があるところでしょうか。
起動しっぱなしでも良いのかも知れませんが、使い終わったら終わらせておきたい気もしますよね…🤔

## 測定

```
time dd if=/dev/zero of=/benchmark bs=1k count=100000
```
このコマンドで100MBのファイルを作成する時間を計測するベンチマークを取ってみました。

結果は…

- 通常のVolumeが約25秒
- NFS Volumeが約2.5秒
- Docker-syncが約0.35秒

と、Docker-syncの圧倒的勝利となりました！
NFS Volumeもかなりはやいのですが、Docker-syncはほぼネイティブと変わらない速度ですね。

## まとめ

いかがだったでしょうか？（殴）

NFSでもかなり早くで感動したのですが、Docker-syncはさらに早くてこんなにも違うのかと驚きました。

ここまでMagento Adventcalendarっぽく無い内容でしたが、Magento2はプロジェクトのファイル数がどうしても多くなってしまうので、読み書きのスピードはとても大事になってきます。
今回のこの結果を踏まえて次回、どうマルチOSに対応したdocker-compose.ymlを書くといいのかを書いていこうかと思います。

次 → [マルチOS対応Docker開発環境の作り方【docker-compose編】](https://qiita.com/bravebright9/items/d978b556ff410b8f0616)