Docker for Macのmount遅い問題まとめ

2017-05-23
Docker
Mac

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
※注 超長い

どんな問題が起きているのか?

  1. Symfonyプロジェクトでブラウザ経由で開発環境にアクセスするとページ表示に30秒かかる
  2. コンテナ内で npm install すると10分以上経っても終わらない
  3. コンテナ内で 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月には状況はかなり良くなると思う。