Rails
docker
docker-compose

RailsアプリをDockerで開発するための手順

More than 1 year has passed since last update.


追記

コードの全体像がわかりにくいって意見をもらったのでgithubにコードおきました。

(りーどみーがわかりにくいとかあればPRいただけると幸せです)

https://github.com/togana/sample-rails-docker

versionとか少し変わってたりDockerfileの中で実行してるコマンドが少し変わったりしてますが、大きく変わっているところはないので参考にしてください!


追記2

docker-machineの共有ファイルを利用している場合、Rails5からファイル変更しても検知できなくなっていました。

ファイルの変更を検知しているのは config.file_watcher という設定です。

ここがバージョンアップに伴い変更されていました。

ActiveSupport::FileUpdateChecker から ActiveSupport::EventedFileUpdateChecker になったようです。コミットログ参照

それぞれ下記のような特性があります。



  • ActiveSupport::EventedFileUpdateChecker はファイルの変更イベントを受けて検知する


  • ActiveSupport::FileUpdateChecker はポーリングしてファイルの変更を監視する

docker-machineの共有ファイルを利用している場合、ファイルの変更イベントが検証した限り、起きないみたいです。

解決策が見つかるまでdevelopmentsのコンフィグを書き換えて対応することにしました。


app/config/environments/developments.rb

- config.file_watcher = ActiveSupport::EventedFileUpdateChecker

+ config.file_watcher = ActiveSupport::FileUpdateChecker


ゴール


  1. Dockerfileを使い、親イメージからRailsアプリイメージを構築できる

  2. docker-composeを用いて複数コンテナの管理ができる

  3. Railsアプリが開発できる


Railsアプリの作成

Railsアプリを作成するには rails new をしなくてはなりません。そのためにプロジェクトディレクトリを用意します。

(すでに開発中の場合はRailsアプリイメージの構築までとばしてください)


プロジェクトディレクトリ生成

$ mkdir project_name

$ cd project_name

しかしローカルの環境にRubyを導入したくないのです(rbenvとかから開放されたい)。なのでrails newすらDockerを利用します。dockerhubからRubyの公式イメージを取得して実行します。今回は2.3.0を使用しますが、他のバージョンが使用したい場合はTagsを参照してください。


ruby

$ docker pull ruby:2.3.0


Gemfileを生成する為にbundle initを実行します。


Gemfile生成

$ docker run --rm -v "$PWD":/usr/src/project_name -w /usr/src/project_name ruby:2.3.0 bundle init



オプションに関する補足

docker run オプション image名:タグ名 実行コマンド

--rm: 実行後のコンテナを削除します。指定しない場合はゴミが残り続けます。
-v: ホストのディレクトリをコンテナ内のディレクトリにマウントします。"$PWD"はカレントディレクトリを意味します。
-w: ワーキングディレクトリを指定します。

そして、下記のようにGemfileを編集します。

今回はversionを4.2.6にしています。他のバージョンがいい場合は変更してください。


Gemfile

source "https://rubygems.org"

gem 'rails', '4.2.6'

イメージ内にgemを埋め込む為に、Gemfile.lockを生成しておきます。

$ touch Gemfile.lock

ここから、親イメージを読み込んで独自のアプリイメージを構築していきます。


Dockerfile

FROM ruby:2.3.0

ENV APP_ROOT /usr/src/project_name

WORKDIR $APP_ROOT

RUN apt-get update && \
apt-get install -y nodejs \
mysql-client \
postgresql-client \
sqlite3 \
--no-install-recommends && \
rm -rf /var/lib/apt/lists/*

COPY Gemfile $APP_ROOT
COPY Gemfile.lock $APP_ROOT

RUN \
echo 'gem: --no-document' >> ~/.gemrc && \
cp ~/
.gemrc /etc/gemrc && \
chmod uog+r /etc/gemrc && \
bundle config --global build.nokogiri --use-system-libraries && \
bundle config --global jobs 4 && \
bundle install && \
rm -rf ~/.gem



補足情報

FROM: 親イメージの指定

ENV: 環境変数の設定
WORKDIR: ワーキングディレクトリの指定
COPY: ファイルをイメージにコピー
RUN: コマンド実行

ビルドしてイメージを作成します。

$ docker build -t developer_name/project_name .


オプションに関する補足

docker build .: カレントディレクトリのDockerfileをビルド

-t: 開発者名/プロジェクト名でイメージ名が作成される

イメージができているか確認します。

$ docker images

REPOSITORY TAG IMAGE ID CREATED SIZE
developer_name/project_name latest 4d1652dba54d 12 minutes ago 826.2 MB
ruby 2.3.0 7ca70eb2dfea 5 days ago 725.4 MB

イメージができたので、railsの雛形をrails new . で生成します。Gemfileがコンフリクト起こすのでYを選択して上書きしましょう。

$ docker run --rm -it -v "$PWD":/usr/src/project_name developer_name/project_name rails new . -BT

exist
identical README.rdoc
identical Rakefile
identical config.ru
identical .gitignore
conflict Gemfile
Overwrite /usr/src/project_name/Gemfile? (enter "h" for help) [Ynaqdh] Y


オプションに関する補足

-it: コンテナのプロセスに対してttyを割り当てる

-BT: bundle install 無し、テストツール無し(minitest使わないこと多いので)


Railsアプリイメージの構築

アプリケーションのイメージを作っていきます。

必要なツールを導入、ソースコードを焼き付けて、デフォルトでwebサーバーの起動が行えるようにDockerfileを修正します。


Dockerfile

FROM ruby:2.3.0

ENV APP_ROOT /usr/src/project_name

WORKDIR $APP_ROOT

RUN apt-get update && \
apt-get install -y nodejs \
mysql-client \
postgresql-client \
sqlite3 \
--no-install-recommends && \
rm -rf /var/lib/apt/lists/*

COPY Gemfile $APP_ROOT
COPY Gemfile.lock $APP_ROOT

RUN \
echo 'gem: --no-document' >> ~/.gemrc && \
cp ~/
.gemrc /etc/gemrc && \
chmod uog+r /etc/gemrc && \
bundle config --global build.nokogiri --use-system-libraries && \
bundle config --global jobs 4 && \
bundle install && \
rm -rf ~/.gem

COPY . $APP_ROOT

EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]


ビルドしてイメージを再作成します。

$ docker build -t developer_name/project_name .

アプリを動作させるだけならイメージにソースコードが埋め込まれてるので下記で動作します。


実行コマンド

$ docker run -d -p 3000:3000 developer_name/project_name


開発時は、コンテナにローカルディレクトリをマウントすると、ソースコードの変更が即座に反映されるのでいいと思います。

コンフィグなどサーバーの再起動が必要な場合はrestartする必要があります。


開発時実行コマンド

$ docker run -d -p 3000:3000 -v "$PWD":/usr/src/project_name developer_name/project_name



restart方法

$ docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
05a6270674cc developer_name/project_name "rails server -b 0.0." 6 seconds ago Up 5 seconds 0.0.0.0:3000->3000/tcp goofy_lumiere
$ docker restart 05a6270674cc
05a6270674cc


docker-composeによるコンテナ管理

先ほど作ったDockerfileをdocker-composeで管理していきます。


docker-compose.yml

version: '2'

services:
app:
build: .
environment:
RAILS_ENV: development
ports:
- '3000:3000'
volumes:
- .:/usr/src/project_name

docker-compose.ymlができたら再ビルドする。

$ docker-compose build

ボリューム設定やポート設定が、docker-compose.yml内に記述されているので、起動コマンドがすっきりしてるのがわかる。

$ docker-compose up -d


オプションに関する補足

-d: バックグラウンド実行


ここからはDBのコンテナを立てて連携していく。今回はmysqlを使うことにする。

sqlite3からmysql2にGemfileを変更。


Gemfile

- gem 'sqlite3'

+ # gem 'sqlite3'
+ gem 'mysql2'

sqlite3のdatabaseの設定からmysql2の設定に変更。

urlは環境変数を使用して設定する。


config/database.yml

default: &default

adapter: mysql2
encoding: utf8
port: 3306
pool: 5
timeout: 5000
url: <%= ENV['DATABASE_URL'] %>
development:
<<: *default
database: db_development

test:
<<: *default
database: db_test

production:
<<: *default
database: db_production


docker-compose.ymlにmysqlの設定をする。


docker-compose.yml

version: '2'

services:
app:
build: .
environment:
RAILS_ENV: development
DATABASE_URL: mysql2://root:pass@mysql:3306
MYSQL_ROOT_PASSWORD: 'pass'
ports:
- '3000:3000'
volumes:
- .:/usr/src/project_name
links:
- mysql
mysql:
image: mysql:5.7.10
environment:
MYSQL_ROOT_PASSWORD: 'pass'
ports:
- '3306:3306'
volumes:
- mysql-data:/var/lib/mysql
volumes:
mysql-data:
driver: local

develop環境のためパスワードをハードコーディングしてるがプロダクション環境では環境変数に記述すること。


mysqlのパスワードの設定

MYSQL_ROOT_PASSWORD: 'pass'


DBの設定ができたので再ビルドして実行してみる。

$ docker-compose build

$ docker-compose up -d

appコンテナとmysqlコンテナが動作しているか確認する。

$ docker-compose ps

Name Command State Ports
-------------------------------------------------------------
vagrant_app_1 rails server -b 0.0.0.0 Up 0.0.0.0:3000->3000/tcp
vagrant_mysql_1 /entrypoint.sh mysqld Up 0.0.0.0:3306- >3306/tcp

ブラウザから動作確認


err

Unknown database 'db_development'


データベースが生成されていないのでアプリケーションエラーがでている。

実際にデータベースを作成してエラーが解消されることを確認する。

$ docker-compose run --rm app rake db:create

ブラウザから動作確認問題なく動いていることがわかる。

上記のようにrakeコマンドを使ってマイグレーション等も行えるので活用すると開発がスムーズに行える。


不要なプロセスとイメージを削除する

ステータスがexitedのプロセスを削除して、イメージ名が<none>のイメージを削除するワンライナー

$ docker ps -aq -f "status=exited" | xargs docker rm -v && docker images -q -f "dangling=true" | xargs docker rmi && docker volume ls -qf dangling=true | xargs docker volume rm


docker1.13以上を使っている方はこちら

$ docker system prune -f


僕がよく使うコマンド

# DB作りたくなったら

$ docker-compose run --rm app rake db:create

# マイグレーションしたくなったら
$ docker-compose run --rm app rake db:migrate

# seed実行したくなったら
$ docker-compose run --rm app rake db:seed

# コントローラー作成したくなったら(controller_nameを変更してどうぞ)
$ docker-compose run --rm app rails generate controller controller_name

# Model作成したくなったら(model_nameを変更してどうぞ 引数にname:stringとかでnameカラムを作れます。)
$ docker-compose run --rm app rails generate model model_name name:string

# ルーティング変更したくなったら(config/routes.rbを編集後実行)
$ docker-compose run --rm app rake routes