前回の記事では、Dockerのイメージやコンテナといった基礎についてまとめました。
今回はより実践的な内容として、RubyとPostgreSQLでRuby on Railsアプリケーションの開発環境をつくる方法について書きます。 また、あわせてBundlerでインストールしたGemをキャッシュする方法についてもまとめます。
この記事の内容により、Dockerさえインストールすればすぐに開発環境ができあがるようになります。 ぜひ試してみてください。
動作環境
この記事の内容は、次の各バージョンで動作を確認しています。
- macOS 10.12.2
- Docker 1.12.3
- Ruby on Rails 5.0.0.1
- PostgreSQL 9.6.1
コンテナ
Railsアプリケーションを動かすには、最低限Webサーバ(5系だとPumaなど)とデータベースサーバを動かす2つのコンテナが必要になります。 また、Dockerの性質上、この2つだけだとGemfileが更新される度にすべてのGemをインストールすることになるため、Gemのキャッシュ用のコンテナも用意します。
この3つのコンテナの役割について、以下に簡単にまとめます。
1. webコンテナ
RubyやPostgreSQLクライアントなど、Railsアプリケーションの動作に必要なライブラリをインストールします。 また、ホストマシンから参照できるようにポートも開放します。
このコンテナにはRubyの公式イメージを利用すると便利です。
2. dbコンテナ
PostgreSQLの動作に必要なライブラリのインストールや設定を行ないます。 こちらもPostgreSQLの公式イメージを利用すればよいでしょう。 このDockerfileはGitHubから参照できます。
3. bundleコンテナ
Dockerfileは命令を行単位でキャッシュしますが、1行でも変更があると以降すべての命令が実行されます。
このため、Dockerfileでbundle installすると、Gemを追加する度にすべてのGemをインストールし直すことになってしまいます。
これを回避するために、Gemをインストールするためのコンテナを別途用意します。
構築手順
以下に、RubyとPostgreSQLでRailsアプリケーションの開発環境をつくる手順について説明します。
なお、ここではホストマシンにRubyをインストールする必要のないよう、webコンテナをとおしてRailsプロジェクトを生成しています。 開発中のプロジェクトがすでにある場合は、当該の箇所を読み飛ばしてください。
1. Dockerfileを書く
まず、webコンテナをつくるためのイメージを作成します。 Docker HubにRubyの公式イメージがあるので、これをベースに処理を書いていきます。
Dockerfile:
FROM ruby:2.3.3
RUN apt-get update -qq && \
apt-get install -y build-essential libpq-dev nodejs
RUN mkdir /app
WORKDIR /app
ADD Gemfile .
ADD Gemfile.lock .
ENV BUNDLE_JOBS=4 \
BUNDLE_PATH=/bundle
RUN bundle install
ADD . .
各命令の詳細は、公式ドキュメントをご覧ください。
上記でbundle installしていますが、これはまずはじめにGemfileをとおしてrailsをインストールするために書いています。
このGemをとおしてrails newでプロジェクトを生成します。
これにより、ホストマシンにRubyをインストールする必要がなくなります。
既存のプロジェクトがある場合はこの行は省略して構いません。
2. Docker Composeの設定を書く
次に、Docker Composeの設定例を以下に示します。
version: '2'
services:
web:
build: .
command: bundle exec rails s -p 3000 -b 0.0.0.0
container_name: web
depends_on:
- db
ports:
- 3000:3000
stdin_open: true
tty: true
volumes:
- .:/app
volumes_from:
- bundle
db:
image: postgres
bundle:
image: busybox
volumes:
- /bundle
各オプションの詳細は公式ドキュメントにありますが、簡単に説明しておきます。
webコンテナのdepends_onは、このコンテナの前に起動するコンテナを指定します。
また、volumes_fromで他のコンテナのボリュームをマウントします。
ここではGemの保存先として/bundleをマウントしています。
stdin_openとttyは必須ではありませんが、pryでデバッグする際は必要になりす。
db/bundleコンテナは設定がほとんどありませんが、これはDocker Hubのイメージを利用しているため設定が少なく済んでいます。 特にbundleコンテナはただのボリュームなので、busyboxという最小構成のコンテナを生成するイメージを利用しています。
3. Gemfile
webコンテナをとおしてrails newするために、あらかじめGemfileとGemfile.lockをつくっておく必要があります。
前述のとおり、既存のプロジェクトがある場合は次に進んでください。
Gemfile:
source 'https://rubygems.org'
gem 'rails', '5.0.0.1'
$ touch Gemfile.lock
4. Dockerfileをビルドする
次に、先ほど作成したDockerfileをDocker Composeをとおしてビルドします。 これがwebコンテナ用のイメージとなります。
$ docker-compose build
5. Railsプロジェクトを生成する
webコンテナを立てる準備ができましたので、これをとおしてRailsプロジェクトを生成します。 こうすることで、ホストマシンにRubyをインストールすることなくRailsアプリケーションの開発を始められます。
$ docker-compose run web rails new . --force --database=postgresql --skip-bundle
6. データベースの接続設定をする
開発環境(あるいは必要であればテスト環境も)データベースの参照先がコンテナになるので、database.ymlも修正する必要があります。
以下を参考に設定を行ないます。
config/database.yml:
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development: &development
<<: *default
database: app_development
username: postgres
host: db
test:
<<: *development
database: app_test
hostにはdbコンテナ名を書きます。
postgresイメージは、デフォルトでユーザ名がpostgres、パスワードは空となっています。
変更した場合はこれも設定します。
7. Gemをインストールする
Railsプロジェクトを生成した際に--skip-bundleしたので、ここでGemをインストールします。
次のようにwebコンテナをとおしてインストールすることで、Dockerfileで設定したとおり/bundleディレクトリにインストールすることができます。
$ docker-compose run web bundle install
8. データベースをつくる
一般的なRailsアプリケーション開発のフローと同じく、まずはじめにデータベースをつくる必要があります。 これもwebコンテナをとおして行ないます。
$ docker-compose run web rake db:create
9. 各コンテナを起動する
これですべての準備が整いました。
次のコマンドでweb/db/bundleコンテナが立ち上がり、http://localhost:3000にアクセスすることで初期画面が表示されます。
$ docker-compose up
実用例
サーバを立ち上げる
先ほど「docker-compose upで立ち上げる」と書きましたが、これにより表示されるターミナル上の出力は各コンテナのログで、読み込み専用となります。
このため、binding.pryを仕込んでもターミナル上で実行することはできません。
これを解決するために、次のように各コンテナをバックグラウンドで動かしつつwebコンテナにだけアタッチします。
こうすることで、通常のrails sのようなふるまいをし、pryによるデバッグも可能となります。
$ docker-compose up -d && docker attach web
なお、ここでいうwebというのはdocker-compose.ymlのcontainer_nameで指定した値です。
コンソールを立ち上げる
rails cによるコンソールの立ち上げも、同じようにwebコンテナをとおして行ないます。
$ docker-compose run web bundle exec rails c
サーバを停止する
docker-compose upで起動したコンテナをCtrl+Cで終了しても、各コマンドは正常に終了しません。
この弊害としてserver.pidが残り、次回以降rails sするときにエラーが出てしまいます。
各コンテナを正しく終了するには、次のコマンドを実行する必要があります。
$ docker-compose stop
参考記事
- docker-compose で Rails 環境を構築する
- Dockerfileを書く時の注意とかコツとかハックとか
- Quickstart: Compose and Rails
- How to cache bundle install with Docker
おわりに
Dockerを利用すると、ホストマシンにはDocker以外なにもインストールすることなく開発環境を構築することができます。 Railsプロジェクトにこれを準備しておくと、新しいメンバーもすぐに開発に参加できるため、開発がよりスムーズになると思います。
次回以降で、RedisやElasticsearchといったサービスのコンテナを導入する方法について書きたいと思います。