セットプチフォッカ

勉強したアウトプット、ときどきフォッカチオ作っていました

RubyMine2021.2.3でdocker compose環境のRailsを動かしてみる

動機

昨今の開発において、常識になっているDocker。

私もそろそろ学ばねばということで、書籍を使って概要を掴みました。

Dockerの仕組み、Dockerfile、Docker Composeの基本はわかったけれど、実際にRailsアプリを動かしてみたらどうなるんだろう?と気になっていたところ、RubyMineの公式がDocker Compose環境でRailsを動かすサンプルリポジトリを公開していたので、Railsアプリの起動までやってみました。

やること

チュートリアル: リモートインタープリターとしての Docker Compose | RubyMineをやる

サンプルリポジトリ

github.com

やってみる

まずはgit clone

f:id:ikmbear:20211115164149p:plain

f:id:ikmbear:20211115164102p:plain

コマンドでも構いませんが、RubyMineを使用しているので、「Get from VCS」からサンプルリポジトリのURLを設定して、cloneを実施します。

DB設定をPostgresqlに変更する

cloneした時点ではDB設定がsqlite3になっているようなので、config/database.ymlを開き、使用するDBを変更します。

f:id:ikmbear:20211115170018p:plain

コンテナを起動する

次にdocker-compose.ymlを開いて、ガターにある矢印アイコンを押します。

f:id:ikmbear:20211115164929p:plain

これで/usr/local/bin/docker-compose -f /Users/tadokoroikuma/RubymineProjects/sandbox/sample_rails_app/docker-compose.yml up -dというコマンドが実行されます。

つまりは、このdocker-compose.ymlをベースとして、バックグラウンドでサービス(コンテナ)を起動するわけです。
これでPostgresqlRubyのコンテナがそれぞれ作成&起動されます。

インタープリターとして、コンテナ上のRubyを指定する

次にRubyMineに対して、コンテナ上のRubyを参照するように設定します。

f:id:ikmbear:20211115184357p:plain

Preferences > Ruby SDK and Gemsを開き、「+」から「New Remote」を選択します。

f:id:ikmbear:20211115184525p:plain

f:id:ikmbear:20211115184601p:plain

続いて、Docker ComposeのwebコンテナのRubyを参照するように設定し、「OK」を押します。

DB作成とマイグレーションを行う

次にrails db:createrails db:migrateを実施します。
これに限らずですが、ターミナルからコンテナに入ってコマンドを直接実行する方法と、RubyMineの機能を使って実行する方法の2種類があるので、それぞれ解説します。

ターミナルからコンテナに入ってコマンドを直接実行する方法

# webコンテナを指定してコマンドを実行する(bashを起動する)
$ docker-compose exec web bash 

# DBの作成を行う(rails db:migrateも同様)
$ rails db:create
Created database 'sample_rails_app_db'
Created database 'sample_rails_app_db_test

RubyMineの機能を使って実行する方法

RubyMineのRunAnythingに包含されている、Rake Taskの実行を用いて起動します。
(rakeタスクのコマンドはrailsコマンドに移管されたのですが、RubyMineの機能を使う場合は、Rakeタスクとして扱う必要があります。というかRailsコマンドで実行しても結局はRakeタスクを叩いているだけなので...)

^を2回押して、RunAnythingを呼び出し、rake --tasksと入力してEnterで実行します。

f:id:ikmbear:20211115185948p:plain

これをしないとどうもDocker環境でRakeタスクが認識できないようで...(これは不便)。

これでRakeタスクが使えるようになったので、RunAnythingからrake db:createを実行します(rake db:migrateも同様)

f:id:ikmbear:20211115190247p:plain

DBが作成されたか確認する

チュートリアルにはありませんが、RubyMineのDBクライアント機能を使って作成されたDBを確認してみましょう。

f:id:ikmbear:20211115190656p:plain

まずはDatabaseツールウィンドウを開いて、「+」ボタンからDataSourceとしてPostgreSQLを選択します。

f:id:ikmbear:20211115191120p:plain

次に接続に必要な情報を以下の通り設定していきます。

項目 設定 説明
Port 54333 docker-compose.ymlで設定したポートフォワードにもとづき、54333を指定します。
Authentication User & Password postgresのコンテナの設定にしたがい、この認証方法を指定します。
User postgres database.ymlで定義した内容に従って、postgresを指定します
Password <空欄> docker-compose.ymlで指定した内容にて、パスワード不要のオプションを設定しているため、空欄にします
Database sample_rails_app_db database.ymlで指定した内容に基づき、この名前のDBへの接続を指定します

入力が完了したら、TestConnectionを行い、問題なければOKを押してください。

f:id:ikmbear:20211115191635p:plain

うまくいけばこのようにDatabaseの中身を確認することができます。

Railsアプリケーションの実行

f:id:ikmbear:20211115191735p:plain

ツールバーの実行対象に「DEVELOPMENT:SAMPLE_RAILS_APP」を指定し、再生ボタンを押します。

f:id:ikmbear:20211115191943p:plain

ブラウザを開き、http://0.0.0.0:3000/にアクセスするとサンプルアプリが起動します。

なおターミナルから実行する場合は以下のようにします。

$ docker-compose exec web bash
$ bin/rails s -b 0.0.0.0

バインドマウントがされているか確認してみる

後述のdocker-composeの設定で、node_modulesを除くすべてのファイルの変更がコンテナ上に反映されます。
実際にソースを少し改変して、反映されることを確認してみましょう。

app/views/static_pages/home.html.erbを開き、23行目にある内容を書き換えてみましょう。

    <h2>
      BindMountTest

      This is the home page for the
      <a href="https://www.railstutorial.org/">Ruby on Rails Tutorial</a>
      sample application.
    </h2>

この状態でアプリをリロードします。

f:id:ikmbear:20211115192819p:plain

たしかに内容が変更されていることが確認できました。

定義ファイルを確認する

ここまでで実際にRailsアプリを立ち上げることができました。ここからはこの環境を作成した定義ファイルを確認していきたいと思います。

docker-compose.yml

まずはdocker-compose.ymlからです。

version: '3'
services:
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      POSTGRES_HOST_AUTH_METHOD: trust
    ports:
      - "54333:5432"
  web:
    build: .
    command: tail -f /dev/null
    volumes:
      - .:/sample_rails_application
      - /sample_rails_application/node_modules
    ports:
      - "3000:3000"
      # Ports required for debugging
      - "1234:1234"
      - "26166:26168"
    depends_on:
      - db

順番に見ていきます。

version: '3'

これは使用するDocker Composeのバージョンを指定しているだけです。
今回はバージョン3を指定しています。

services:
  db:
    # 略
  web:
    # 略

次にservices、つまり使用するコンテナの設定です。今回はdbというコンテナとwebというコンテナの2種類を立てます。
順を追ってみてみましょう。まずはdbコンテナです。

  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      POSTGRES_HOST_AUTH_METHOD: trust
    ports:
      - "54333:5432"

以下各定義内容の説明です。

定義 意味
image:postgres dbコンテナではpostgresqlをイメージとします
volumes docker-compose.ymlからみた相対パスvar/lib/postgresql/dataが呼び出されたら、ホストの./tmp/dbに読み取り、書き込みを行います。これによりデータが永続化できます
environment 環境変数として、POSTGRES_HOST_AUTH_METHODを定義し、その値にtrustを指定します。このオプションはPostgresqlのパスワードが不要になるもので、推奨はされていません(今回は検証用のチュートリアルなので...)
ports ホストのポート54333を5432にポートフォワードします。

volumesの指定は、Dockerの2つある方法のうちの「バインドマウント(Dockerエンジン上ではなく、ホストの指定した箇所に保存する)」の指定で、:を境にホスト:コンテナのように指定されています。

参考:Postgres - Official Image | Docker Hub

続いてwebコンテナを確認します。

  web:
    build: .
    command: tail -f /dev/null
    volumes:
      - .:/sample_rails_application
      - /sample_rails_application/node_modules
    ports:
      - "3000:3000"
      # Ports required for debugging
      - "1234:1234"
      - "26166:26168"
    depends_on:
      - db

こちらも同様に各定義内容の説明です。

定義 意味
build docker-composeから見た相対パス.つまり、カレントディレクトリにあるDockerfileをベースにしてコンテナを作成します。
command コンテナ起動時のコマンドを指定する。tail -f /dev/nullは何もしないで起動を続けるためのコマンドで、RubyMine側でRailsを起動する際に、コンテナ内でRailsが立ち上がっているとプロセスが重複して起動できなくなることを回避するために、このようにしています。
volumes 一行目は、コンテナの./sample_rails_applicationに対して、ホストの.(カレントディレクトリ)をバインドマウントしています。2行目はコンテナ上のnode_modulesディレクトリを名前なしボリュームとしてマウントしています
ports ポートフォワードを定義しています
depends_on dbコンテナに依存することを定義しています

結構ややこしいのが、node_modulesの名前なしボリュームでのマウントです。
まず先の通り、手元のソースの反映はバインドマウントによって、すべてコンテナ上と同期されます。
こうすると、ホスト上とコンテナ上のnode_modulesがそれぞれ同期されてしまうのですが、せっかくコンテナ上で固定したnodeが、ホストによって破壊されることになってしまいます。

そのため、node_modulesのみはバインドの対象外とするのですが、コンテナが破棄されたのちもデータを永続化するため、DockerEngine上に名前なしボリュームとしてマウントする手段をとっています。

参考:VSCode&Docker Volumeにおけるnode_modules問題を解決する

ここまでに記述した内容でたしかにコンテナが起動しているかは、コマンドでも確認することができますが、RubyMineのServicesタブを使って確認することができます。

f:id:ikmbear:20211115182514p:plain
webコンテナのポートフォワード設定を確認

Dockerfile

つづいて、Dockerfileです。

# ruby2.7.2のイメージを使用する
FROM ruby:2.7.2

# yarnのリポジトリを追加します(apt-key add -は標準入力の内容を追加します。つまりcurlで取得した内容を追加します)
RUN curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -

# teeコマンドを使ってyarnのAPTパッケージレポジトリを自分のシステムに追加します
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

# 必要なパッケージをインストールします
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs postgresql-client yarn

# RUN CMD ENTRYPOINT ADD COPYの際の作業ディレクトリを`/sample_rails_application`に指定
RUN mkdir /sample_rails_application
WORKDIR /sample_rails_application

# 必要な初期設定系ファイルのコピー
COPY Gemfile /sample_rails_application/Gemfile
COPY Gemfile.lock /sample_rails_application/Gemfile.lock
COPY package.json /sample_rails_application/package.json
COPY yarn.lock /sample_rails_application/yarn.lock

# 初期設定系のインストール
RUN gem install bundler -v '2.2.15'
RUN bundle install
RUN yarn install --check-files

# イメージに`. /sample_rails_application`の内容を追加する
COPY . /sample_rails_application

# ポート3000で通信する
EXPOSE 3000

このimageを元に、docker-composeのwebコンテナは作成されるようです。

感想

素晴らしい書籍のおかげでDockerの基礎を理解できたのはよいものの、実際にRailsアプリを一から起動するとなると、ややハードルが高かったです。
しかしながら、このチュートリアルはすでに出来上がっているリポジトリを使ってアプリを動かすので、その中継点として非常にちょうどいい題材だと思いました。

一点気になったのが、node_modulesはVolumeTrickを使って名前なしボリュームにマウントしているのに、Gemfileはいいのか?というところです。
なんか同じような類なので、Gemfileもボリュームマウントした方がいい気がするんですが、その話はまた別の機会に調べようと思います。