[Mac] DockerでCPU使用率が異常なときの対処法

 MacでDockerを使用していると、恒常的にCPU使用率が大幅に上がり、ファンが唸り声を上げることがあります。

原因

 様々な原因がありますが、当環境では、com.docker.hyperkitが数百パーセントのCPU使用率を記録していることがわかりました。

 com.docker.hyperkitは、dockerのコンテナにホストのdiskをマウントする機能の一部を担っていますが、どうもこれがあまり性能上良くないらしく、そのまま使っていると、CPU使用率が上がってしまうケースがあります。特に巨大なディスクをマウントすると起こるようです。

 この問題は非常に長くの間議論されています。環境により、解消法は様々なようですので、それらを紹介していきます。

対処法

 環境により、様々な対処法があり、有効なものも異なるようです。実行が簡単な順に紹介していきます。

OSごと再起動してみる

 もしかすると、他のプロセスと競合して重くなっていたりするかもしれません。原因の切り分けのために、再起動を行い、できるだけ最小の構成で起動してみます。

Kubernetesを無効にする

 デフォルトでは無効のはずですが、何かのはずみで有効になっている可能性があるので、一度確認してみるのも良いかもしれません。DockerのKubernetesを利用している方は飛ばしてください。

 preferences -> Kubernetes のEnable Kubernetesが無効になっているか確認します。

CPU, メモリの設定を調整する

 もしかすると、Dockerに割り当てるCPU、メモリなどの設定が高すぎるかもしれません。これを制限することによってシステム全体のパフォーマンスが改善する可能性はあります。

 preferences -> Resources -> ADVANCED

 ただ、多くのマシンパワーを必要とするようなシステムの場合、逆にDocker Containerの速度が下がってしまうことがある点に注意が必要です。

DockerのResources設定を適切なものにする

 Usersあたりがデフォルトとして設定されているのですが、こちらを最小限に抑えることにより、CPU使用率を下げることができるかもしれません。

 preferences -> Resources -> FILE SHARING の設定で、project directoryや、workspaceなど、可能な限り最小限のディレクトリを指定するようにします。

:cached、:delegatedをvolumeに付与する

 Performance tuning for volume mounts (shared filesystems)によると、:cachedあるいは、:delegatedといった、双方向のファイル反映を遅延させることによって、劇的にパフォーマンスを改善するオプションが存在していることがわかります。

 :cachedでは、ホストからコンテナへの書き込みが遅延するようになり、:delegatedでは、逆にコンテナからホストへの書き込みが遅延するようになります。しかし、ホストとコンテナ間の遅延が影響するようなケースは開発ではあまり無いような気もしますし、何も付与しなかったときに比べても、そこまで大きく遅延してしまうような動きではなかったので、ごくシビアなシステムでない限りは問題ないでしょう。

 docker-composeを利用している場合、以下のように、マウントボリュームの最後に:cachedまたは:delegatedを付与します。多くの場合、:cachedが有効なようですが、コンテナ内で頻繁にファイルの変更が行われるようなシステムの場合は、delegatedが有効かもしれません。

version: '3'

services:
  yourproject:
    volumes:
      - .:/yourproject:cached

 docker runなどを利用している場合は、-vオプションの末尾に付与します。

$ docker run -v .:/yourproject:cached -it bash

 当環境では、この対応でCPU利用率が劇的に改善(300%+ -> 10%)しました。

Dockerのrawファイルを作り直す

 再度環境構築が必要になってしまいますが、Dockerのrawファイルを作り直す手段もあります。このファイルが何らかの要因によって肥大化していると、あまりよくない影響を与えてしまっている可能性があります。

 最初は大丈夫だったけど、だんだん重くなっていってしまった…、などの場合に有効かもしれません。

 Docker -> Preferences -> Resources -> ADVANCED の Disk image locationのパスを開き、中のrawファイルを削除し、再度最初からDockerコンテナを作り直します。

docker-syncを使う

 docker-syncは、Docker for Macのボリュームマウント機構が非常に遅いことを受け、unisonまたはrsyncなどのファイル転送機構を活用してホスト・コンテナ間の転送を高速化した画期的なツールです。

 少し古いので有効性は微妙かもしれませんが、Qiitaにわかりやすい記事がいくつかありますので、紹介しておきます。

 残念ながら当環境では有効性を感じることはできませんでした。

最後に

 これらの対処法は限定的なものですが、Docker for Macのパフォーマンス問題は、多くの場合、ボリュームマウントに原因があることがわかります。

 たとえば、sassなどのprecompileを行うためのwatchコマンドや変更検知、オートリロードなどの仕組みは、開発の助けとなる有用なものですが、思いも寄らないところで負荷を掛けている可能性もあります。

 この記事が問題解決の助けになれば幸いです。また、他の事例がありましたらぜひコメント欄にお知らせください!

 また、Docker for MacのIssueでも活発に議論がなされているようです。よければそちらも御覧ください。