Docker for Macが遅い問題をdocker-syncで解決する

こんにちは。エンジニアの志村です。 Docker for Mac便利ですね!

docs.docker.com

日本語でも様々な記事が出てきています。

私もVagrant + Dockerをメインに使用しておりましたが、ついにDocker for Macに乗り換えました。 弊社では、dev環境をDockerにしているのですが、Vagrant + Dockerの時は快適に開発が出来ました。 ただ、VMを使わなくなった途端にめちゃくちゃ動作が重くなりました。 docker-compose --service-port ●●という感じで、rails serverとwebpack-dev-serverを立ち上げるのですが、ブラウザからの読込が劇的に遅い…。seedデータ突っ込むのもめちゃくちゃ遅い… フォーラムでも話題になっていますね。

forums.docker.com

私の環境でいうと、Vagrant + Dockerの時よりも、体感10倍近く速度が落ちました…。

なぜ遅いのか

forums.docker.com

このDockerの中の人の回答を見ると、osxfsを用いてファイルシステムイベントを検知・監視していることが主な原因っぽいですね。 勿論、その他環境により差異はありますが、ファイルシステムを監視する方法を替えれば解決できそうです。

docker-sync

ファイル同期で速度早いと良く言われているのがrsyncですね。 docker-dev-osxという選択肢もありますが、これはDocker for Mac未対応… どうしようかなーとフォーラムを眺めているとdocker-syncなるものがありました。 フォーラムの中でも評価が良く、結構な人が使ってそうなのでdocker-syncを使用したいと思います。

docker-sync by EugenMayer

docker-syncはRubyベースで書かれています。

docker-syncの使い方

  1. docker-sync, fswatchをインストールします。
$ gem install docker-sync
$ brew install fswatch

ファイルの同期に関してはrsync, unisonが選択出来ます。 rsyncがホスト→ゲストの一方向の通信なのに対し、unisonはNFSのように双方向の通信が可能なようです。 今回はrsyncを使用します。 unisonに関してはまた記事を書ければと思います。

$ brew tap homebrew/dupes
$ brew install rsync

※ schema.rb, Gemfile.lock等、ゲスト(Docker)側で生成されるファイルに関してはdocker cpコマンドを使用するとホスト側に持ってくることが出来ます。

# Docker内のschema.rbをPC側に持ってくる
$ docker cp コンテナ:/var/www/db/schema.rb ./db/schema.rb
  1. docker-syncの設定 docker-syncはymlファイルに設定を記述します。 Railsを触っている身としては普段から使用しているのでありがたいです。

docker-syncはDocker Composeを使用します。 production環境やstaging環境でもDockerを使用している方は、docker-compose.ymlの他に、開発環境用のymlファイルを用意する必要があります。 私は開発環境のみなので、docker-compose.ymlを直接編集してしまいます。

公式のwikiに載っているので参考下さい。

github.com

docker-sync.yml

syncs:
  # docker-syncで使用するvolume名を記載
  web-sync:
    notify_terminal: true
    # ホスト側(PC)のパスを記載 今回はRailsのルートディレクトリを指定
    src: './'
    # ゲスト側(Docker)にマウントするパスを記載 Docker内の/var/wwwにマウントされる
    dest: '/var/www'

    # IPの設定
    sync_host_ip: '127.0.0.1'

    # rsyncのポート設定
    sync_host_port: 10871

    # rsyncさせないファイル類を記載
    sync_excludes: ['Gemfile.lock', 'Gemfile', 'config.rb', '.sass-cache/', 'sass/', 'sass-cache/', 'composer.json' , 'bower.json', 'package.json', 'Gruntfile*', 'bower_components/', 'node_modules/', '.gitignore', '.git/', '*.coffee', '*.scss', '*.sass']

    sync_excludes_type: 'Path'

    sync_args: '-v'

    # ファイル同期方法の選択 rsync or unison
    sync_strategy: 'rsync'

    # ゲスト側でのユーザー名の指定
    sync_user: 'test'

    sync_userid: '5000'

    # ゲスト側でのgroupの指定
    sync_group: 'testgroup'

    sync_groupid: '6000'

    # fswatchで変更を検知させないファイルやディレクトリ sync_excludesとかぶせとけばよいかと
    watch_excludes: ['.*/.git', '.*/node_modules', '.*/bower_components', '.*/sass-cache', '.*/.sass-cache', '.*/.sass-cache', '.coffee', '.scss', '.sass', '.gitignore']

    watch_args: '-v'

    watch_strategy: 'fswatch'   

docker-compose.yml

docker-compose.ymlにはversion1, version2と2種類の記述方法があります。 ここではversion2を使用します。

version: '2'
services:
  database:
    image: postgres:latest
    volumes_from:
      - datastore
    expose:
      - '5432'

  elasticsearch:
    image: elasticsearch:latest
    volumes_from:
      - datastore
    ports:
      - '9200:9200'

  redis:
    image: redis:latest
    ports:
      - '6379:6379'
    volumes_from:
      - datastore

  memcached:
    image: memcached:latest
    ports:
      - '11211:11211'
    volumes_from:
      - datastore

  datastore:
    build: docker/datastore

  web:
    build: .
    command: bash -c 'rm -f tmp/pids/server.pid && bundle exec foreman start && bundle exec sidekiq -C config/sidekiq.yml -L tmp/sidekiq.log -d'
    volumes:
      # 下記で宣言したvolume名を指定
      - web-sync:/var/www:rw
      - /var/www/client/node_modules
    ports:
      - '3000:3000'
      - '8080:8080'
    links:
      - database
      - elasticsearch
      - redis
      - memcached

# docker-syncで使用するvolume ここで宣言した名前がdocker-sync.ymlで使用される
volumes:
  web-sync:
    external: true

このような感じです。 環境によって差異があると思いますので、適宜調整をお願いします。 一番重要なのは、volumesのsync volumeの設定と、アプリケーション(Rails)側のコンテナでvolumesを指定する部分です。

docker-syncの起動

$ docker-sync start

もしくは

$ docker-sync-stack start

で起動します!

docker-sync-stack startの場合は、docker-sync startと同時にdocker-compose upも行われます。 私はpry-byebugを使用したいので、普段docker-compose run --service-port webで起動しているため、ターミナルを一枚docker-sync startに使用し、さらにdocker-compose run --service-port webを起動させるようにしています。

ベンチマークはとっていないのですが、Vagrant + DockerをNFSで同期させたのと同じ、もしくはそれ以上に早くなりました! GUIで様々な設定が出来、VMとかも必要ないのでDocker for Macは便利ですね。 是非docker-syncを使って快適な開発環境を構築してみては如何でしょうか?

We’re hiring!

Cluexではビジネスサイド、エンジニアサイド共にメンバーを募集しています! お気軽にご連絡下さいませ!

www.wantedly.com

www.wantedly.com

www.wantedly.com

www.wantedly.com