「Ruby on Rails 5アプリケーションプログラミング」の環境をdocker-composeで作る。
背景
仕事でrailsを使うことになったがきちんと触ったことが無かったのでGWに試してみることにした。
railsの入門書はこれが良いらしいので買った。書籍ではローカルに環境を構築しているが、dockerを使ったほうが良いだろう。
Ruby on Rails 5アプリケーションプログラミング
- 作者: 山田祥寛
- 出版社/メーカー: 技術評論社
- 発売日: 2017/04/14
- メディア: 大型本
- この商品を含むブログを見る
docker-composeを用いたrailsの環境構築は公式にドキュメントが用意されているが、rubyやrailsのバージョンが本で解説されている動作環境とは異なるので、合わせた環境をdocker-composeで構築してみる。また、本だとDBにSQLiteを使っているが現実的にはMySQLのほうが良いのでここも変えたい。
クイックスタート・ガイド:Docker Compose と Rails — Docker-docs-ja 17.06.Beta ドキュメント
動作環境
本の動作環境はこのようになっている。
- CentOS 7.2.1511
- Ruby 2.4.0-p0
- Ruby on Rails 5.0.1
- SQLite 3.7.17
Docker HubのrubyのイメージはCentOSが無いので妥協する。DBはMySQLを使うことにする。
https://hub.docker.com/_/ruby/
Dockerfile
上にも貼ったdocker-composeの公式のドキュメントを見ながら本の動作環境にバージョンを揃える。
FROM ruby:2.4.0 RUN apt-get update -qq && apt-get install -y build-essential mysql-client nodejs RUN mkdir /myapp WORKDIR /myapp ADD Gemfile /myapp/Gemfile ADD Gemfile.lock /myapp/Gemfile.lock RUN bundle install ADD . /myapp
差分
メモ
apt-get update -qq
の-qq
はエラー以外表示しないオプション。- ダウンロードやインストールのログは大量に出るから、もしコケたときのデバッグがしにくくなるから表示しないようにしてるのかな。
Gemfile
source 'https://rubygems.org' gem 'rails', '5.0.1'
空のGemfile.lockもドキュメントと同様に用意する。
$ touch Gemfile.lock
差分
- railsのバージョンを4.2.0から5.0.1へ変更。
docker-compose.yml
version: '2' services: db: image: mysql environment: MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' web: build: . command: bundle exec rails s -p 3000 -b '0.0.0.0' volumes: - .:/myapp ports: - "3000:3000" depends_on: - db
差分
メモ
- Dockerfileで
Add . /myapp
しているから、docker-compose.ymlで改めてvolumes: .:/myapp
としなくてもいいんじゃ?と思ったが、そんなことなかった。- 前者は
bundle install
するためだけのファイルコピーで、後者はローカルのファイルの変更をコンテナに渡すために必要だった。
- 前者は
- depends_onディレクティブは「dbを先に立ち上げよ。」という意味になるようだ。ただし、コンテナの立ち上がる順序は指定できても、立ち上がりシーケンスが終了したかどうかは確認してくれないようだ。これによって起こる問題は後述する。
- MYSQL_ALLOW_EMPTY_PASSWORDは設定しないとMySQLが立ち上がってくれない。
- 最初はこれを環境変数に設定せずにいたら立ち上がらなかった。
docker logs
でログを確認したところこれを設定しろというメッセージが出ていた。
- 最初はこれを環境変数に設定せずにいたら立ち上がらなかった。
rails new
$ docker-compose run web rails new . --force --database=mysql --skip-bundle
差分
- --databaseオプションで指定するデータベースをpostgresqlからmysqlへ変更。
メモ
- --force は上書き許可オプション。
- 参考: railsコマンド(rails) - - Railsドキュメント
- 冪等性の確保のためにつけてるのかな。
- --skip-bundleは bundle installを行わないオプション。
- bundle installはDockerfileでやるからここではスキップするのかな。
docker-compose build
ここはドキュメントと変わらない。
rails new したことによってrailsのファイルがローカルに振ってきて、Gemfileも更新されている。これを編集してJavascriptのランタイムを入るようにしてからdocker-compose buildする。
ここわかりづらかった。最初の docker-compose run web rails new . --force --database=postgresql --skip-bundle
ではrailsしかインストールされてないミニマルなコンテナがビルドされる。そしてこれはrails newするためだけのコンテナになる。二回目の dockee-compose build
でさっきrails newして降ってきたファイルも取り込んだコンテナが出来上がる。
差分
- なし
データベース接続
ドキュメント通り、docker-compose up
でrails serverが立ち上がる。ブラウザで http://localhost:3000
を確認すると、上手く行っていれば、 Unknown database 'myapp_development'
というエラーが表示される。また、ブラウザで直ぐに確認した場合は Unknown database servber 'db'
というエラーも見える。これは前述した立ち上げ処理の終了まで面倒を見てくれないため、MySQLの起動よりも先にrails serverが立ち上がってしまうことが原因。こっちが出た場合はしばらく待つと前者のエラーに切り替わるはず。
差分
- なし
データベース作成
ドキュメントどおり、 docker-compose run web rails db:create
とすることでデータベースが作成される。改めて docker-compose up
して http::/localhost:3000
にアクセスすると繋がるようになる。
差分
- なし
メモ
なんでdocker-compose runの結果が、改めてdocker-compose upしても保持されたままなの?
まず、docker-compose upしてCtrl-Cで止めた後、再度docker-compose upするとコンテナを作り直す。よって、docker-compose runの結果が保持されることは無いはず。しかし、コンテナにマウントされたボリュームは作り直さないようだ。
マウントしているボリュームは、そのまま保持します
引用元: up — Docker-docs-ja 17.06.Beta ドキュメント
ということなので、MySQLのイメージはどこかにボリュームを持っているらしいことが推測できる。それで調べるとやはり持っているようだ。
実はデフォルトで永続化されるように設定されています。
引用元: Dockerの公式MySQLイメージの使い方を徹底的に解説するよ · DQNEO起業日記
というわけなので docker-compose run web rails db:create
でデータベースが作成されるが、その変更はホスト側のボリュームに書き込まれ、改めて docker-compose up
でコンテナを作り直してもデータベースは作成されたままというわけだった。
データベースを永続化する。
この手順はドキュメントに無い。
以上までやっても docker-compose down
して再度 docker-compose up
すると、 Unknown database 'myapp_development'
というエラーが出る。上述の通り、デフォルトで永続化されてはいるので、そのパスをボリュームにすれば解消される。しかし、ホスト側からは見えづらいパスにボリュームがある(docker inspectするとパスがわかる)ので、明示的にマウントするボリュームは指定しておいた方が使い勝手が良い。
version: '2' services: db: image: mysql environment: MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' volumes: - ./tmp/db:/var/lib/mysql web: build: . command: bundle exec rails s -p 3000 -b '0.0.0.0' volumes: - .:/myapp ports: - "3000:3000" depends_on: - db
差分
- ボリュームを./tmp/dbに指定した。
これで開発環境は整った。