RubyMineでDocker-Compose経由の開発環境を構築する

業務でRubyMineを使っていて、docker-composeで構築されたアプリをRubyMineでデバッグ実行したくなった。

いきなり実プロジェクトを触るのもアレなので、まずは自前で動かせる環境を構築するところまでを行ったのでここに記しておく。

作業環境

MacOS 10.13.4
RubyMine Build #RM-183.5153.41, built on January 10, 2019
Docker for Mac version 18.09.1, build 4c52b90
docker-compose version 1.23.2, build 1110ad01

構築したい環境

  • Dockerfileでアプリのコンテナを作る
  • Rubyはコンテナ側のものを使用し、ホスト側のRubyは使わない
  • PostgresとRedisも別コンテナで稼働させる
  • Bundlerとかのインストール系はローカルにキャッシュさせて高速化したい
  • Docker-composeで必要なコンテナをよしなに起動
  • RubyMineで動かしたい

構築

まずはじめに

ホスト側のRubyを使用しないと書いたので、Gemfileは適当に書くか、コピペしてくる。

source 'https://rubygems.org'
gem 'rails', '~> 5.2.1'

とはいえローカルにRubyとBundlerぐらいは入ってるはずなので、普通に$ bundle init とかでもいいと思う。

Dockerfileの準備

次にRailsアプリのDockerfileを作る。内容は以下の通り。

FROM ruby:2.6
 
ENV APPDIR /usr/local/myapp
 
RUN apt-get update && \
    apt-get install -y vim less && \
    apt-get install -y build-essential libpq-dev && \
    apt-get install -y postgresql && \
    apt-get install -y nodejs && \
    gem install bundler && \
    apt-get clean && \
    rm -r /var/lib/apt/lists/*
 
WORKDIR $APPDIR
 
ENV BUNDLE_GEMFILE=$APPDIR/Gemfile

docker-compose.ymlの準備

そしてdocker-compose.yml

version: '2'
services:
  db:
    image: postgres:9.4
    volumes:
      - postgresql:/var/lib/postgresql/data
  redis:
    image: redis:3.2
  app:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - bundle:/usr/local/bundle
      - .:/usr/local/myapp:cached
    ports:
      - "3000:3000"
    environment:
      POSTGRES_HOST: db
      REDIS_HOST: redis
    depends_on:
      - db
      - redis
volumes:
  bundle:
  postgresql:

named-volumesを設定しておくとコンテナを破棄してもデータが永続化される。今回の例ではbundle,postgresqlが指定されていて、これらのデータは$ docker-compose downをした後も失われない。named-volumesは特定のディレクトリ名を指定する必要がなく、このVolumeはDockerがよしなに管理してくれる。

ちなみに、どんなVolumeが作られているのかを見たい場合は$ docker volume lsで一覧表示できる。

Image

ここまで来ると$docker-compose upで各コンテナが起動できるようになる。
ただし、まだRailsアプリそのものは存在しておらず、Gemfileがただそこにあるだけなので、まずはbundleを実行してやる必要がある。
$ docker-compose run --rm app bundle installを実行すると、新しいコンテナプロセスを生成して、その中でbundle installが実行される。
初めてやったときは「違うコンテナでbundle installしてもちゃんと反映されるの?」と思ったが、bundleボリュームをマウントしていることで、違うコンテナで実行しても同一のボリュームに反映されるので問題ないことに気が付いた。

Railsアプリの準備

$ docker-compose run --rm app bundle installを実行すると、Rails、そしてそれに必要なGemのインストールが始まる。完了したら次はRailsプロジェクトを生成してやる。
$ docker-compose run --rm app bundle exec rails new . -d postgresRailsプロジェクトを生成する。Gemfileを上書きしようとするので警告が出るが、Yで続行。

そしてdatabase.ymlは以下のように設定する。とりあえずdevelopmentの設定だけ以下に書いておく。

development:
  <<: *default
  database: myapp_development 
  username: postgres
  host: db

ここで注意点が二つあって、一つ目は、DockerHubで提供されているPostgresのイメージはusername: postgresしか存在しておらず、ここを省略した場合コンテナのユーザ名(今回の例ではrootになる)でアクセスしようとしてしまうため、NoDatabaseError: FATAL: role “root” does not existと表示されてPostgresから弾かれてしまう点。
もう一つは、RailsアプリとPostgresのコンテナは分離されているので、hostを明示的に指定してあげないとアプリはlocalhost:5432を探しに行ってしまい、PG::ConnectionBadが出てしまう点。
このdbというのはdocker-compose.ymlで指定しているdepends_on: dbのこと。(多分)

ちなみにPostgresのイメージで作られるユーザがpostgresしか無いのはここを見れば分かる。オープンソース便利。

github.com

これで一通りRailsアプリの準備が整った。あとは$ docker-compose run --rm app bundle exec rails db:createをして適当にhttp://localhost:3000あたりにアクセスすることができるようになる。
(もしかしたら$ bundle exec rails db:migrateも必要かも、今回の例では試す前にModelとか作っちゃってたのでmigrateを要求された。)

Image

無事Railsアプリが起動した。

で、ここからが本題

docker-composeで管理したアプリをRubyMineでデバッグ実行したい!

と言ってもここまで来たら大したことはしなくても大丈夫。やり方はここに書いてある通り。

https://pleiades.io/help/ruby/using-docker-compose-as-a-remote-interpreter.html

まず、デバッグ実行に必要なgemをいくつかGemfileに追記する。

group :development do
...
gem 'debase'
gem 'ruby-debug-ide'
end

次にDocker for MacをRubyMineで設定する。
設定場所はPreference>Build,Execution,Deployment>Dockerにある。
Image

次にdocker-composeで管理されているappコンテナのRubyをRubyMine側に認識させてやる。
設定場所はPreference>Language & Framework>Ruby SDK and Gemsから+を押してnew remoteを選択。
そして以下のように設定してやる。
Image

設定が済んだらこのボタンを押してGemをRubyMine側で利用できるようにする。

Image

これを押すとRubyMineの一番下のステータスバーでgemを処理しているプログレスバーが出現する。バーが出なくなったらデバッグ実行可能になるので、適当にブレークポイントを設置して、以下のデバッグボタンを押す。

Image

デバッグ実行のログが出てきて、

docker-compose-initialization-with-rails_db_1 is up-to-date
docker-compose-initialization-with-rails_redis_1 is up-to-date
Recreating docker-compose-initialization-with-rails_app_1 ... 
Attaching to docker-compose-initialization-with-rails_app_1
app_1    | Fast Debugger (ruby-debug-ide 0.6.1, debase 0.2.2, file filtering is supported) listens on 0.0.0.0:1234
app_1    | => Booting Puma
app_1    | => Rails 5.2.1 application starting in development 
app_1    | => Run `rails server -h` for more startup options
app_1    | Puma starting in single mode...
app_1    | * Version 3.12.0 (ruby 2.6.1-p33), codename: Llamas in Pajamas
app_1    | * Min threads: 5, max threads: 5
app_1    | * Environment: development
app_1    | * Listening on tcp://0.0.0.0:3000
app_1    | Use Ctrl-C to stop

これで、ブレークポイントを張った部分にコードの処理が到達すると一時停止してデバッグができる。

Image

たまにデバッグ実行が開始されず、server is already running...と出るときがある。
この場合は、コンテナ側の${APP_DIR}/tmp/pids/server.pidを削除すれば解決することが多い。
$ docker-compose run --rm app /bin/bashで入って、$ rm tmp/pids/server.pidとするだけで良い。もっと楽な方法もあるかも。

おわりに

今回構築したRailsアプリのサンプルは以下から参照可能。

github.com

RubyMineはRailsそのもののコードを追っていくときに結構重宝していたけど、これで業務でもモリモリ使えるようになったのでやっていきたい。