Rails
RSpec
docker
dockerformac

RailsのRSpecにとってDocker for Macはどれぐらい遅いのか?

Railsを開発しているとRSpecの実行時間が遅くて開発効率が悪くなることがあります。
これは開発環境にDocker For Macを使っているからでしょうか?

背景

Docker For Macは遅いと言われます。

これは現在のDocker For Macでも遅いのでしょうか?

rawフォーマットは試していません

また、最新のDocker For Macでは速くなっているようです。

Docker for Macのディスクスループットを約2倍にする - Qiita

Docker Community Edition 17.12.0-ce-mac46 2018-01-09 (Stable)の更新で、ディスクスループットが320MiB/secから600MiB/secになるらしいので適用する。

APFS, SSD, High Sierraで有効。

現在Sierraを使っているので、試していません。

実行環境

  • MacBook Pro Late 2013
  • macOS Sierra 10.12.6
  • Docker Community Edition 17.12.0-ce-mac49 (21995)

手段

AWS EC2のAmazon Linux2インスタンスを使って試します。

Dockerの起動時間を抜いて計測するために

docker-compose run --service-ports web bash

を実行し、Docker上で

time bundle exec rspec spec/models/hoge_spec.rb

を実行します。

bash-4.3# time bundle exec rspec spec/models/hoge_spec.rb
No DRb server is running. Running in local process instead ...
Rack::File headers parameter replaces cache_control after Rack 1.5.
....................................................................................................................................................................................

Finished in 5.15 seconds
180 examples, 0 failures

real    0m23.581s
user    0m0.010s
sys 0m0.000s

realの値がtimeコマンドの結果です。
rspecコマンドの起動から終了までの実時間です。
これは「Rails環境の読み込み」時間を含みます。

Finished in 5.15 secondsがRSpecのテストの実行時間です。
差の18.4秒は「Rails環境の読み込み時間」です。

結果

以下の環境で試しました。

  • Docker For Mac
  • t2.micro
  • c4.large

いずれもDockerとdocker-composeでRailsアプリケーションを構成しています。

環境 real rspec real比率 rspec比率 Rails環境読み込み比率
Docker For Mac 0m23.581s 5.15 seconds - - -
t2.micro 0m14.258s 2.9 seconds 60% 56% 62%
c4.large 0m11.456s 2.52 seconds 49% 49% 48%

EC2上では40〜50%の時間短縮が計測されました。
単純にDocker for Macを使うとLinux上でDockerを使う場合の、2倍の時間が掛かると考えて良いでしょう。

Docker For Mac

bash-4.3# time bundle exec rspec spec/models/hoge_spec.rb
No DRb server is running. Running in local process instead ...
Rack::File headers parameter replaces cache_control after Rack 1.5.
....................................................................................................................................................................................

Finished in 5.15 seconds
180 examples, 0 failures

real    0m23.581s
user    0m0.010s
sys 0m0.000s

t2.micro

bash-4.3# time bundle exec rspec spec/models/hoge_spec.rb
No DRb server is running. Running in local process instead ...
Rack::File headers parameter replaces cache_control after Rack 1.5.
....................................................................................................................................................................................

Finished in 2.9 seconds
180 examples, 0 failures

real    0m14.258s
user    0m0.000s
sys 0m0.000s

c4.large

bash-4.3# time bundle exec rspec spec/models/hoge_spec.rb
No DRb server is running. Running in local process instead ...
Rack::File headers parameter replaces cache_control after Rack 1.5.
....................................................................................................................................................................................

Finished in 2.52 seconds
180 examples, 0 failures

real    0m11.456s
user    0m0.000s
sys 0m0.000s

考察

EC2の方が2倍近く速い

Docker For MacのDisk I/Oの遅さが原因に思えます。

Docker for Macのディスクスループットを約2倍にする - Qiita の効果とも一致します。
ファイルシステムのraw format化で、解消されることが期待できます。

c4.largeがt2.microより15%速い

他のインスタンスタイプ、例えば

  • t2.small
  • m4.large
  • c3.large

を使っても、t2.microとは差がありません。
これはc4.largeが(標準で)EBS 最適化インスタンスだからです。

https://aws.amazon.com/jp/ec2/instance-types/

C5、C4、M5、M4、P3、P2、G3、および D2 の各インスタンスの場合、この機能はデフォルトで有効になっており、追加料金は発生しません。

やはり、RailsのRSpecでは、Disk I/Oがボトルネックのようです。

その他の高速化

Rails application preloader

例えば、Springを使うと「Rails環境のロード時間」をスキップできます。

今回のテストのように、Rspecの実実行時間の8割が「Rails環境のロード時間」に使われている場合は、Rails application preloaderを使うと、テストの繰り返しやすさに効果的です。

 参考

スローテストを解消する

RailsのRSpecは、Rails環境のロード時間を除いても、遅いことが多いです。
特にモデルのテストの場合は、it毎にDBにテスト用のレコードを用意するためのinsert文を発行するためです。
必要なレコードが20件ある場合は、体感でわかるぐらい遅くなりました。

テスト数が多いと線形に遅くなります。
簡単に20秒掛かるテストセットが作れます。
Mac上で20秒掛かるテストが、Linux上で10秒になっても、テスト待ちのストレスはあまり解消されません。
ローカル環境とリモート環境を切り替える手間を考えると、テストの繰り返しやすさは上がりません。
地道にスローテストを解消した方が、テストの繰り返しやすさには効果が高いです。

before :allで複数のテストでまとめてテストデータを用意したり、FactoryGirl.createの代わりにFactoryGirl.buildを使ったりして、DBのレコード追加削除を減らすのが効果的です。

参考