追記
コードの全体像がわかりにくいって意見をもらったので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のコンフィグを書き換えて対応することにしました。
- config.file_watcher = ActiveSupport::EventedFileUpdateChecker
+ config.file_watcher = ActiveSupport::FileUpdateChecker
ゴール
- Dockerfileを使い、親イメージからRailsアプリイメージを構築できる
- docker-composeを用いて複数コンテナの管理ができる
- Railsアプリが開発できる
Railsアプリの作成
Railsアプリを作成するには rails new をしなくてはなりません。そのためにプロジェクトディレクトリを用意します。
(すでに開発中の場合はRailsアプリイメージの構築までとばしてください)
$ mkdir project_name
$ cd project_name
しかしローカルの環境にRubyを導入したくないのです(rbenvとかから開放されたい)。なのでrails newすらDockerを利用します。dockerhubからRubyの公式イメージを取得して実行します。今回は2.3.0を使用しますが、他のバージョンが使用したい場合はTagsを参照してください。
$ docker pull ruby:2.3.0
Gemfileを生成する為にbundle initを実行します。
$ 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にしています。他のバージョンがいい場合は変更してください。
source "https://rubygems.org"
gem 'rails', '4.2.6'
イメージ内にgemを埋め込む為に、Gemfile.lockを生成しておきます。
$ touch Gemfile.lock
ここから、親イメージを読み込んで独自のアプリイメージを構築していきます。
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を修正します。
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
$ 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で管理していきます。
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を変更。
- gem 'sqlite3'
+ # gem 'sqlite3'
+ gem 'mysql2'
sqlite3のdatabaseの設定からmysql2の設定に変更。
urlは環境変数を使用して設定する。
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の設定をする。
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_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
ブラウザから動作確認
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