Docker for Mac のホストを volume マウントするときに cache モードで高速化する
Docker 公式ドキュメントにあるこの話。
Docker for Mac ではホストを volume マウントすると非常に遅くなる。これは Mac のファイルシステムが Linux と異なるので、ホストとコンテナのファイルを同期するのにオーバーヘッドが生じるために起きる。そのため大量のファイルをマウントすると、ファイルの読み書きに時間がかかるようになる。
そこで、Docker 17.04 から、docker run
の --volume
オプションに cached
と delegated
が加えられた。
今回は cached
オプションでどれくらい高速化されるかを実験してみる。
各オプションの説明
--volume
のオプションは 3 つあって、それぞれ以下のようになっている。
consistent
: 完全な一貫性 (ホストとコンテナのマウントが常に同一のファイルに見える)cached
: ホストからの見え方が信頼できる (ホストの更新の前にコンテナ側で遅延が許容される)delegated
: コンテナから見え方が信頼できる(コンテナの更新の前にホスト側で遅延が許容される)
consistent
がデフォルトで、cached
ではファイル同期にいつくかの保証がなくなり、delegated
ではさらに保証が少なくなる。保証が少なくなるほど高速になる。cached
オプションはファイル読み込みが多いときにパフォーマンスが改善され、delegated
は一時ファイルの大量書き込みなどのユースケースで高速化される。cached
と delegated
の両オプションとも、適切なユースケースに限定して使うべき。
cached
モードが使えるユースケース
User-guided caching in Docker for Mac によると、次のようなユースケースで cached
モードが活躍する。
- ホストのエディタで開発し、コンテナで開発ツールを動かす
- コンテナで定期的なジョブを実行し、ホストのディレクトリに出力を保存する
- コンテナ側の処理のために巨大なデータアセットをホスト側にキャッシュする
cached
モードでどれくらい高速になるか試す
簡単に試してみる。まず volume 用のディレクトリを作る。
$ npm init -y $ npm add eslint
これで作られた node_modules ディレクトリをマウントすることにする。
ホストとコンテナで同じコマンドを実行し、その時間を計測する。コマンドは Node.js のプロセスを起動して、"eslint" モジュールを読み込むだけ。node_modules 下の大量のファイルを読み込むことになるので、ディスク I/O が遅いと時間がかかる。
ホストから。
$ time node -e "require('eslint')" real 0m0.586s user 0m0.404s sys 0m0.098s
IO 待ち含めて 0.586 秒かかった。
次は Docker コンテナで同じことをする。
volume にオプションを付けない場合:
$ docker run --rm -it -v $PWD/node_modules:/node_modules node:12-slim bash -c "time node -e \"require('eslint')\"" real 0m3.925s user 0m0.790s sys 0m0.610s
volume に cached
オプションを付けた場合:
$ docker run --rm -it -v $PWD/node_modules:/node_modules:cached node:12-slim bash -c "time node -e \"require('eslint')\"" real 0m1.958s user 0m0.410s sys 0m0.230s
cached
オプションを付けると、3.925 秒から 1.958 秒になった。
結論
cached
オプションを付けると確かにコンテナ側のファイル読み込みが速くなる。が、ホストと同等ではなく、やはりまだ遅い。
開発用にホストのディレクトリをマウントするなら、node_modules などの大きなディレクトリはマウントから除外したほうがいいだろう。