UUUM攻殻機動隊(エンジニアブログ)

UUUMのエンジニアによる技術ブログです

Rails開発環境をDocker化したときのtips

こんにちは、Railsエンジニア見習い補佐のナカハシです。

新しくRailsアプリ開発環境を立ち上げるに辺り、Ruby層まで含めたDocker化をしてみたときのあれこれを残しておきます。

f:id:k_nakahashi:20180403141337p:plain

今回の構成

新しく作るRailsアプリは、他のUUUMのRailsアプリのよくある構成と同様、Rails + MySQL + Redis(主にセッション用)の構成にしました。

こういった複数のミドルウェアと組み合わせた動作環境を作る場合、それぞれのDockerコンテナをいい感じに動かすためにDocker Composeを使います。公式ページに Quickstart: Compose and Rails というサンプルがあるので、これを元に設定ファイルを書きました。( rails new までの手順もここの通りに実施していますので、主な手順もこちらを参照して下さい)

MySQLコンテナとの接続

(ほとんどコピペで)書いたdocker-compose.ymlはこのようになりました。(最初にRedisとも書きましたが、記事の趣旨的に省略しておきます...)

version: '3'
services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: "password"
    ports:
      - "19801:3306"
    volumes:
      - ./tmp/db:/var/lib/mysql
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/myapp
    ports:
      - "19802:3000"
    depends_on:
      - db

これで http://localhost:19802/ にブラウザでアクセスすることで動作確認が出来ます。他のRailsアプリの環境とポートが被るとあれなので、 web 用のポート設定を ports: - "19802:3000" としています。

DBはプロダクションでの動作環境と合わせてイメージを mysql5.7 とし、ポート番号は web と同様、他の開発環境にかぶらないように 19801 を設定しています。

この場合、Dockerイメージで動作しているMySQLと同じDockerネットワークにいるRailsアプリからの接続は、以下のようになります。

# database.yml
default: &default
  adapter: mysql2
  pool: 5
  timeout: 5000

development:
  <<: *default
  database: myapp_development
  username: root
  password: password
  host: db
  port: 3306

ホスト側MacのMySQLクライアントから接続したい場合は 127.0.0.1:19801 で接続します。

$ mysql -u root -h 127.0.0.1 -P 19801 -p

Webサーバー起動失敗

そうこうしてうちに、以下の現象でサーバーの起動に失敗するようになりました(>_<

A server is already running. Check /myapp/rails-app/tmp/pids/server.pid.

pidファイルが不要に残ってしまうことで、この現象になってしまいます。pumaの設定などでpidファイルに関するコントロールはできるとは思うのですが、今回は、愚直に起動前に毎回消すようにします。 UUUMではタスクランナーとしてmakeをよく使います ので、今回は Makefile に以下のように書きました。

.PHONY: run
run:
    rm -rf tmp/pids/server.pid
    docker-compose up

これで、以下のコマンドでサーバー起動できるようになります。

$ make run

vendor/bundle

よくあるRails環境では、依存Gemをホストのシステム領域ではなく、プロジェクト内のパス vendor/bundle にインストールしますよね。

$ docker-compose run web bundle install --path vendor/bundle

さて、これで試しに現在のルーティングを確認したりテストを実行してみると、だいぶ時間がかかります。これは、特にGem群がDockerイメージのマウント対象になっていることが原因なようです。

試しに雑に time コマンドで動作時間だけ計測してみると、、、

$ time docker-compose run web rails routes
Starting myapp_db_1 ... done
...(略)
You don't have any routes defined!

Please add some routes in config/routes.rb.

For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html.
docker-compose run web rails routes  0.47s user 0.16s system 1% cpu 51.529 total

rails routes コマンドに1分近くかかっています。。この傾向はrailsコマンド全体で同じですので、単体テストをバリバリ回したいシチュエーションだと相当つらいですね。

docker-compose.yml で以下の設定を追加すると、約20秒まで短縮できました。

version: '3'
services:
  (略)
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/myapp
      # ★☆ 追加 ★☆
      - ./vendor/bundle:/myapp/vendor/bundle:delegated

まとめ

開発環境をDockerに寄せておくと、互換性の問題も発生しにくいしセットアップが簡単になっていいですね。 コーダーやデザイナーとの作業分担時には、特に効果を発揮しそうです。