LoginSignup
14
16

More than 3 years have passed since last update.

Railsチュートリアルの開発環境を Docker でもっと便利にしなイカ!?

Last updated at Posted at 2020-02-20

【概要】

本記事では、Gem の永続化、データベース及びテスト用メールサーバをコンテナ化していきます。
Railsチュートリアルの開発環境を Docker にしてみなイカ?の続きです。

さて、前回の記事では、Railsチュートリアルの Sample App をコンテナ化しました。
しかし、実際の開発にあたり、煩わしい部分や便利になるところがありますので、改善していきましょう。



【本文】

□ 事前準備

  • 前回の記事と同じファイル及びディレクトリ構成です。
docker/Dockerfile
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'
docker-compose.yml
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
bash(省略可)
 # (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 を追加

docker-compose.yml
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

bash
 # 前の手順で、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 は、通常のディレクトリに存在せず、別の場所に存在しています。なお、ディレクトリ内を探検してみると、コンテナも保存していることがわかります。
bash(省略可)
 # 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 の編集

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 に変更します。
  • なお、ここでは差分表示していませんので、全て置換してください。
config/database.yml
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 の構成もついでに変更しておきます。
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 コンテナの起動

bash
 # コンテナと volume を削除してから起動
 $ docker-compose down -v
 $ docker-compose up -d

○ 5: データベースの作成

bash
 $ 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コンテナに入って実際に確認したい場合は、次の通り操作してみてください。
bash(省略可)
 # DB コンテナに入る
 $ docker-compose exec db ash

 # PostgreSQL に登録されている Sample App のデータベースに接続
 $ psql -U root -d sample_app_development

 # テーブル生成の確認
 sample_app_development=$ \dt
 sample_app_development=$ exit

□ メールサーバのコンテナ化

■ 現状の問題点

■ 解決策

○ 1: docker-compose.yml の編集

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: 開発環境のメール設定を編集

config/environments/development.rb
...
  # 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

□ 特記事項

■ 注意事項

  • 今のところ特になし

■ 使用イメージ、ライブラリ

□ おまけ: DB を MySQL にしたい場合

○ 1: Dockerfile の編集

docker/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 の編集

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 の編集

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 の編集

Gemfile
...

-  gem 'pg', '0.18.4'
+  gem 'mysql2', '~> 0.4.4'
...

  1. docker-compose stopstartなら作業状態を継続できることから、Gem もインストール済で作業を継続できる。 

14
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
16