【概要】
本記事では、Gem の永続化、データベース及びテスト用メールサーバをコンテナ化していきます。
Railsチュートリアルの開発環境を Docker にしてみなイカ?の続きです。
さて、前回の記事では、Railsチュートリアルの Sample App をコンテナ化しました。
しかし、実際の開発にあたり、煩わしい部分や便利になるところがありますので、改善していきましょう。
- 開発環境
- macOS Mojave: 10.14.6
- Docker Engine: 19.03.5
- Docker Compose: 1.25.4
- コンテナ環境
- Alpine Linux: 3.11.3
- Ruby: 2.4.9
- Rails: 5.1.2
- 参考: 学習情報 URL
【本文】
□ 事前準備
- 前回の記事と同じファイル及びディレクトリ構成です。
FROM ruby:2.4.9-alpine3.11
ENV LANG C.UTF-8 \
TZ Asia/Tokyo
ENV BUILD_PACKAGES="build-base" \
DB_PACKAGES="sqlite-dev postgresql-dev" \
RAILS_PACKAGES="tzdata nodejs imagemagick" \
FAVORITE_PACKAGES="less"
RUN apk update && \
apk upgrade && \
apk --update --no-cache add \
${BUILD_PACKAGES} \
${DB_PACKAGES} \
${RAILS_PACKAGES} \
${FAVORITE_PACKAGES}
WORKDIR /app
COPY Gemfile \
Gemfile.lock \
/app/
RUN bundle install --jobs=4
# https://github.com/bundler/bundler/issues/6154
ENV BUNDLE_GEMFILE='/app/Gemfile'
version: '3'
services:
app:
build:
context: .
dockerfile: ./docker/Dockerfile
ports:
- 3000:3000
command: bundle exec rails s -p 3000 -b 0.0.0.0
volumes:
- ./:/app:cached
stdin_open: true
tty: true
□ Gem の永続化
■ 現状の問題点
- 結論から言うと、現状では Gemfile を編集する度に
docker-compose build
をする必要があリます。つまり、大量の Gem を初めから全てbundle install
することになる煩わしい状態なのです。 - 理由としては、コンテナの構造によるものであり、この問題点を体験したい場合、事前準備の条件で以下の通り動かしてみると良いでしょう。1
# (1) コンテナのイメージ構築(ここのイメージがコンテナ構築時のデフォルト設定)
$ docker-compose build
# (2) コンテナを構築及び起動
$ docker-compose up -d
# (3) Gemfile に Gem を追加
...
group :development, :test do
...
gem 'pry-rails'
gem 'pry-byebug'
end
...
# (4) 起動済のコンテナ内で bundle install
$ docker-compose exec app bundle install
# (5) 追加した Gem が確認できる( /usr/local/bundle/ にインストールされている)
$ docker-compose exec app gem list
# (6) コンテナを削除
$ docker-compose down
# (7) コンテナを起動すると...
$ docker-compose up
> Starting sample_app_app_1 ... done
> Attaching to sample_app_app_1
> app_1 | Could not find pry-byebug-3.4.3 in any of the sources
> app_1 | Run `bundle install` to install missing gems.
> sample_app_app_1 exited with code 7
# (8) 結果
- pry-byebug が見つからないため、bundle install が必要な状態です。
- つまり、コンテナの状態が(1)に戻ってきていることがわかります。
■ 解決策
-
前述の通り原因は、
起動したコンテナ内で bundle install
するだけで、設計図となるコンテナのイメージ自体に反映されていないことです。 -
解決策としては、Gem をコンテナ外で管理することです。コンテナを削除しても、Gem が存在する場所に影響しません。ここでは、コンテナ外に Gem の保管場所( = volume )を作成して、コンテナ内の Gem を保存する場所と繋げます( = mount )。
-
参考URL
○ 1: docker-compose.yml に data volume を追加
version: '3'
services:
datastore:
image: busybox
+ volumes:
+ - bundle_install:/usr/local/bundle
app:
build:
context: .
dockerfile: ./docker/Dockerfile
ports:
- 3000:3000
command: bundle exec rails s -p 3000 -b 0.0.0.0
volumes:
+ - bundle_install:/usr/local/bundle
- ./:/app:cached
stdin_open: true
tty: true
+ volumes:
+ bundle_install:
○ 2: 再度 bundle install
# 前の手順で、Gemfile に Gem を追加していましたら、該当箇所を一度コメントアウトしてください。
# (1) コンテナのイメージ構築
$ docker-compose build
# (2) コンテナを構築及び起動
$ docker-compose up -d
# (3) Gemfile に Gem を追加
...
group :development, :test do
...
gem 'pry-rails'
gem 'pry-byebug'
end
...
# (4) 起動済のコンテナ内で bundle install
$ docker-compose exec app bundle install
# (5) コンテナを削除
$ docker-compose down
# (6) コンテナを起動すると正常に動作しています。
$ docker-compose up
○ 3: どこに保存されたのか?
- では、データの保管先を確認してみましょう。
- 追加した volume は、通常のディレクトリに存在せず、別の場所に存在しています。なお、ディレクトリ内を探検してみると、コンテナも保存していることがわかります。
# Moby VM にアクセス
$ screen ~/Library/Containers/com.docker.docker/Data/vms/0/tty -s
# 〜VM内操作へ移行〜
# 以下のコマンドにより、`sample_app_bundle_install/`の存在が確認できる。
$ ls /var/lib/docker/volumes/
# この Volume は、data_store で定義した通り、`/usr/local/bundle`を保存する。
$ ls /var/lib/docker/volumes/sample_app_bundle_install/_data
# 終了時
$ `controll` + `A` + `K`
$ yes
○ 4: Gem の永続化完了
- ここまでで、Gem の永続化が完了したため、次はデータベースをコンテナ化します。
- もちろん、データベースをコンテナ化したとしても、コンテナの削除時にデータベースに保存したデータは失われてしまいます。そのため、Gem の永続化と同様にデータベースも併せて永続化していきます。
□ データベースのコンテナ化
■ 現状の問題点
-
Sample App における本番環境のデータベースは PostgreSQL で、開発環境は SQLite を使用しています。しかし、基本的に開発環境と本番環境は、同一化が求められます。「開発環境では動いたのに本番で動かない(絶望)」ということ自体があってはなりません。
-
しかし、これを実践しようとしてローカル環境で開発するとき、逐一 PostgreSQL を起動したり、プロジェクトによって異なるバージョンを用意する必要があったり、煩わしい部分があるため、コンテナ化します。
-
参考URL
■ 解決策
○ 1: docker-compose.yml の編集
version: '3'
services:
datastore:
image: busybox
volumes:
- bundle_install:/usr/local/bundle
+ - db_data:/var/lib/postgresql/data
+ db:
+ image: postgres:12.2-alpine
+ volumes:
+ - db_data:/var/lib/postgresql/data
+ ports:
+ - 5432:5432
+ environment:
+ POSTGRES_USER: root
+ POSTGRES_PASSWORD: pass
app:
build:
context: .
dockerfile: ./docker/Dockerfile
ports:
- 3000:3000
command: bundle exec rails s -p 3000 -b 0.0.0.0
volumes:
- bundle_install:/usr/local/bundle
- ./:/app:cached
+ depends_on:
+ - db
+ environment:
+ APP_DATABASE_HOST: db
+ APP_DATABASE_USERNAME: root
+ APP_DATABASE_PASSWORD: pass
stdin_open: true
tty: true
volumes:
bundle_install:
+ db_data:
○ 2: config/database.yml の編集
- Rails の DB 設定は、SQLite のままであるため、PostgreSQL に変更します。
- なお、ここでは差分表示していませんので、全て置換してください。
default: &default
adapter: postgresql
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
host: <%= ENV.fetch("APP_DATABASE_HOST") { '127.0.0.1' } %>
port: <%= ENV.fetch("APP_DATABASE_PORT") { '5432' } %>
username: <%= ENV.fetch("APP_DATABASE_USERNAME") { 'root' } %>
password: <%= ENV.fetch("APP_DATABASE_PASSWORD") { 'pass' } %>
development:
<<: *default
database: sample_app_development
test:
<<: *default
database: sample_app_test
production:
<<: *default
database: sample_app_production
○ 3: Gemfile の編集
- 開発環境を PostgreSQL に変更するため、現在の Gemfile の構成もついでに変更しておきます。
...
group :development, :test do
- gem 'sqlite3', '1.3.13'
gem 'byebug', '9.0.6', platform: :mri
gem 'pry-rails'
gem 'pry-byebug'
end
...
- group :production do
- gem 'pg', '0.18.4'
- end
+ gem 'pg', '0.18.4'
...
○ 4: DB コンテナの起動
# コンテナと volume を削除してから起動
$ docker-compose down -v
$ docker-compose up -d
○ 5: データベースの作成
$ docker-compose exec app rails db:create
$ docker-compose exec app rails db:migrate
$ docker-compose exec app rails db:seed
○ 6: DB のコンテナ化完了
- これで、DB のコンテナ化が完了です。
- DBコンテナに入って実際に確認したい場合は、次の通り操作してみてください。
# DB コンテナに入る
$ docker-compose exec db ash
# PostgreSQL に登録されている Sample App のデータベースに接続
$ psql -U root -d sample_app_development
# テーブル生成の確認
sample_app_development=$ \dt
sample_app_development=$ exit
□ メールサーバのコンテナ化
■ 現状の問題点
-
特にありませんが、一つのコマンド打つだけで見れるのですから、あったら便利ですよね。
-
また、メールのデータも永続化できますが、ここで残すメリットを感じられなかったため、実装していません。永続化したい方は、参考URL等を確認しつつ実装してみてください。要領は前項までと同じです。
-
参考URL
■ 解決策
○ 1: docker-compose.yml の編集
version: '3'
services:
datastore:
image: busybox
volumes:
- bundle_install:/usr/local/bundle
- db_data:/var/lib/postgresql/data
db:
image: postgres:12.2-alpine
volumes:
- db_data:/var/lib/postgresql/data
ports:
- 5432:5432
environment:
POSTGRES_USER: root
POSTGRES_PASSWORD: pass
app:
build:
context: .
dockerfile: ./docker/Dockerfile
ports:
- 3000:3000
command: bundle exec rails s -p 3000 -b 0.0.0.0
volumes:
- bundle_install:/usr/local/bundle
- ./:/app:cached
depends_on:
- db
environment:
APP_DATABASE_HOST: db
APP_DATABASE_USERNAME: root
APP_DATABASE_PASSWORD: pass
stdin_open: true
tty: true
+ smtp:
+ image: mailhog/mailhog
+ ports:
+ - '8025:8025'
volumes:
bundle_install:
db_data:
○ 2: 開発環境のメール設定を編集
...
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = true
- config.action_mailer.delivery_method = :test
- host = 'railstutorial-yasulab.c9users.io'
- config.action_mailer.default_url_options = { host: host, protocol: 'https' }
+ config.action_mailer.delivery_method = :smtp
+ config.action_mailer.smtp_settings = {
+ address: "smtp",
+ port: "1025",
+ }
+ config.action_mailer.default_url_options = { host: "localhost:3000" }
...
○ 3: メールサーバのコンテナ化完了
- これで、メール確認が容易になりました。試しに SignUp してみるとアクティベーションメールが以下のURLに届きます。
- http://localhost:8025
□ 特記事項
■ 注意事項
- 今のところ特になし
■ 使用イメージ、ライブラリ
- ruby - Docker Hub
- busybox - Docker Hub
- postgres - Docker Hub
- mailhog/MailHog: Web and API based SMTP testing
- mysql - Docker Hub
□ おまけ: DB を MySQL にしたい場合
○ 1: Dockerfile の編集
...
ENV BUILD_PACKAGES="build-base" \
- DB_PACKAGES="postgresql-dev" \
+ DB_PACKAGES="mysql-client mysql-dev" \
RAILS_PACKAGES="tzdata nodejs imagemagick" \
FAVORITE_PACKAGES="less"
...
○ 2: docker-compose.yml の編集
...
+ db:
+ image: mysql:5.7
+ volumes:
+ - db_data:/var/lib/mysql
+ ports:
+ - 3306:3306
+ environment:
+ MYSQL_ROOT_PASSWORD: pass
- db:
- image: postgres:12.2-alpine
- volumes:
- - db_data:/var/lib/postgresql/data
- ports:
- - 5432:5432
- environment:
- POSTGRES_USER: root
- POSTGRES_PASSWORD: pass
...
○ 3: config/database.yml の編集
default: &default
adapter: mysql2
charset: utf8mb4
collation: utf8mb4_bin
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
host: <%= ENV.fetch("APP_DATABASE_HOST") { '127.0.0.1' } %>
port: <%= ENV.fetch("APP_DATABASE_PORT") { '3306' } %>
username: <%= ENV.fetch("APP_DATABASE_USERNAME") { 'root' } %>
password: <%= ENV.fetch("APP_DATABASE_PASSWORD") { 'pass' } %>
development:
<<: *default
database: sample_app_development
test:
<<: *default
database: sample_app_test
production:
<<: *default
database: sample_app_production
○ 4: Gemfile の編集
...
- gem 'pg', '0.18.4'
+ gem 'mysql2', '~> 0.4.4'
...
- 参考URL
- MySQL の charset 等について、上記の通り設定しない場合は以下の問題が発生するため、utf8mb4 を設定しておく( Rails のデフォルトだと utf8 になる )。
- Rail5.1 だと、mysql2 のバージョンを指定する必要がある。
-
docker-compose stop
とstart
なら作業状態を継続できることから、Gem もインストール済で作業を継続できる。 ↩