docker for macを使っているユーザが抱えている大問題。それが shared directory 遅い問題。このエントリでは、何故遅いのか、どうしたら良いのかをまとめた。
フォーラムの元ネタ
File access in mounted volumes extremely slow, CPU bound - Docker for Mac - Docker Forums
Githubのissue
File access in mounted volumes extremely slow · Issue #77 · docker/for-mac · GitHub
※注 超長い
どんな問題が起きているのか?
- Symfonyプロジェクトでブラウザ経由で開発環境にアクセスするとページ表示に30秒かかる
- コンテナ内で npm install すると10分以上経っても終わらない
- コンテナ内で gulp watch するとCPUファンが回りっぱなしになる
コンテナ内部からのファイルIOが遅い。
何故遅いのか?
Docker stuffの David Sheetsさんによる説明
File access in mounted volumes extremely slow, CPU bound - Docker for Mac - Docker Forums
上記解説は osxfs のドキュメントにもそのまま転機されている
https://docs.docker.com/docker-for-mac/osxfs/#performance-issues-solutions-and-roadmap
簡単にまとめると
- osxfsはOSXのFSEvents APIとLinux’s inotify APIをマッピングさせている
- osxfsの throughput は250 MB/s
-> これはほとんどのAPPで問題にならないはず
- コンテナ内からosxfsに対するファイルIOのシステムコールはlatencyが遅い。書き込み時で200μs
-> 通常のファイルシステムであれば under 10μs
つまりAPI間のマッピング行為に時間がかかっている => ここでCPU負荷が上がる
コンテナ内でのosxfsに対するreadが遅い。
コンテナ内でのosxfsに対するwriteはもっと遅い。
そんなにも遅いのに何故、dockerのチームがosxfsを採用しているのか。理由は2つ上げられていて
- graceful handling of file ownership
-> fileの権限周りのハンドリングが楽
- supporting filesystem events
-> こっちはよく分からない…
実測
ddを使って計測
1 | docker run --rm -it -v `pwd`:`pwd` -w `pwd` alpine /usr/bin/time /bin/dd if=/dev/zero of=test bs=1k count=100000 |
↑約20secかかる 100MBのファイル生成
vオプションを取り除いて計測すると 0.27sec (aufs)
つまり100倍近く遅い。
ただし、上記オプションでbs(ブロックサイズ)を1Mにして、countを100とかに変更すると0.2secほどで終了する。throughputよりlatencyが問題。
解決方法
完全に解決するわけではない。
CE 17.03 (stable) チャンネルを使う場合
stableチャンネルのdockerだと基本的には迂回策しか取れない。
シンプルな解決策
- Windows, またはLinux(Ubuntu等)を使う
=> 両OSでは同様のパフォーマンス問題は発生していない。 - docker-machineでVirtualBox上に立ち上げたdocker環境で開発環境を構築する
=> VM上のLinuxにおいてosxfsを使わないので速い
多量のファイル読込、書込をhost => containerの向きで利用する
- gulp watchをhost側で実行
- npm install, composer installをhost側で実行
=> npmや composerのバージョンをコンテナ内部と合わせる必要がある
=> コンテナとは・・・という気持ちに若干なる
リテラシーを必要とする解決策
- docker volumeでnfs共有したマウントポイントを共有
- docker-sync(rubygem)を使う rsyncコンテナを立ち上げて同期
- d4m-nfs を使う host側のnfsマウントを利用して同期
CE 17.05 (edge) チャンネルを使う場合
Dockerのチームがshared directory遅い問題に公式に対応中です。edgeチャンネルだと既に利用できます。
cachedフラグを利用する方法については、下記のポストを参照。
Docker for macの user-guided cachingを試す
ちなみに2種類のフラグが利用可能になる予定らしい
- cachedフラグ
-> readパフォーマンスの改善 17.04 edge以降 で利用可能
- delegateフラグ
-> writeパフォーマンスの改善 未実装
やめた方がいいやつ
コンテナ内でrsyncデーモン立ち上げてrsyncでファイル同期したり、sshデーモン立ち上げてscpでファイル同期したりするのは、そもそもコンテナ開発の意義が根底から覆っちゃってるので却下。
ちなみに
stable channelの17.06でcachedが入る予定なので、6月には状況はかなり良くなると思う。