RailsチュートリアルアプリをDockerでコンテナ化してみる

November 29, 2019

ちょっと実践的なDockerの練習というテーマで「Railsチュートリアルのアプリをコンテナ化」をしてみました。今回はその時のことをまとめていきたいと思います。 ちなみに、Docker初心者の方でも分かりやすいように書いていくので、ご心配なく。

Railsチュートリアルのアプリを準備する。

これがないと始まらない。ということで、RailsチュートリアルのアプリをGitHubからダウンロード(クローン)してきます。   GitHub - yasslab/sample_apps: Railsチュートリアルの各章が終わった状態を集めたリポジトリです。

$ git clone https://github.com/yasslab/sample_apps.git

このリポジトリは、かなり親切でRailsのバージョン毎・チュートリアルのステップ毎のアプリが全て入っています。 この中から今回は、「Rails5.1/チュートリアルの最終的な成果物」のアプリを使います。

対象のアプリ: sample_apps/5_1_2/ch14

また、アプリ全体の見通しを良くするために、使わないものを削除して少しディレクトリを整理しておきます。

$ mkdir sample_app
$ cp -p sample_apps/5_1_2/ch14/* sample_app/
$ rm -fr sample_apps

Dockerfileを作成してアプリをコンテナ化する。

まずは、1つのコンテナにDB(デフォルトでSQLite)とWebサーバを入れて起動できるようにDockerfileを作成していきます。

sample_app/Dockerfile

FROM ruby:2.6.3
ENV APP_ROOT /sample_apps

# 作業ディレクトリをアプリのルートディレクトリにしています。
WORKDIR $APP_ROOT

# 各種必要なパッケージをインストールしていきます。
RUN apt-get update && \
    apt-get install -y mysql-client \
                       apt-transport-https \
                       --no-install-recommends && \
    curl -sL https://deb.nodesource.com/setup_10.x | bash - && \
    apt-get install -y nodejs && \
    rm -rf /var/lib/apt/lists/* # キャッシュを削除してイメージの容量を減らします。

# bundle install するためにローカルのGemfileをコンテナ内にコピーします。
COPY Gemfile $APP_ROOT
COPY Gemfile.lock $APP_ROOT

# bundle install を行っていきます。
RUN echo 'gem: --no-document' >> ~/.gemrc && \ # bundle install を高速化するための設定。
    cp ~/.gemrc /etc/gemrc && \
    chmod uog+r /etc/gemrc && \
    bundle config --global build.nokogiri --use-system-libraries && \ # nokogiriの最新版をダウンロードする際に起こる問題の解消。
    bundle config --global jobs 4 && \
    bundle install && \
    rm -rf ~/.gem

# アプリの資源を全てコンテナにコピーします。
COPY . $APP_ROOT/

# コンテナのポート8888を開放します。
EXPOSE 8888

# pumaをポート8888で起動します。
CMD ["rails", "server", "-b", "0.0.0.0", "-p", "8888"]

Dockerfileが作成できたので、「イメージの作成⇒コンテナの起動⇒DBのマイグレート」まで行っていきます。

# イメージの作成
$ docker build -t [ユーザー名]/[プロジェクト名] .

# 作成したイメージのID確認
$ docker images

# イメージをバックグラウンドで起動
$ docker run -d -p 8888:8888 [イメージのID]

# コンテナのIDを確認
$ docker ps

# コンテナ内でDBのマイグレートを実行
$ docker exec [コンテナのID] rails db:migrate

これでコンテナ化の作業は完了です。ブラウザから http://localhost:8888 にアクセスしてアプリに接続できるはずです。

Docker Composeでコンテナ環境を作成する。

次は、WebとDBのコンテナを分割して、Docker Composeで管理していきます。 Docker Composeでは、docker-compose.yml が必要なため、下記のように作成します。

sample_app/docker-compose.yml

version: '2'
services:
  # アプリ用のコンテナ設定
  app:
    build: .
    # アプリで必要な環境変数を設定
    environment:
      RAILS_ENV: development
      DATABASE_URL: mysql2://root:pass@db:3306
      MYSQL_ROOT_PASSWORD: 'pass'
    # ローカル:コンテナのポートをマッピング
    ports:
      - '8888:8888'
    # ローカルの更新内容をコンテナに自動反映するために、ローカルのディレクトリをコンテナにマウントする
    volumes:
      - .:/sample_apps
    # dbコンテナの起動後に起動する
    depends_on:
      - db

  # DB用のコンテナ設定
  db:
    image: mysql:5.7.25
    environment:
      MYSQL_ROOT_PASSWORD: 'pass'
    ports:
      - '3306:3306'
   # コンテナを落としてしまうとデータがなくなってしまうため、ローカルにデータ領域を作成してコンテナにマウントする
    volumes:
      - mysql-data:/var/lib/mysql
volumes:
  mysql-data:
    driver: local

お気づきかもしれませんが、DBをSQLiteからMySQLに変更しています。これは、実際の業務で使用されているDBがMySQLが多いという理由からです。 docker-compose.ymlとしては、これで完成なのですが、DBを変更しているため、この状態で起動してもエラーがでちゃいます。

DBをSQLiteからMySQLに切り替える。

やる事は3つ。コンテナはほぼ関係ないです。

  • MySQL用のGemをインストール
  • database.ymlの修正
  • 一部ソースの修正

既にGemfileにはSQLite、PostgreSQL用のGemが記載されていますので、それらを削除してMySQL用のGemを追加します。(開発、本番両方の環境でMySQLを使用します。)

sample_app/Gemfile

+ gem 'mysql2',       '0.4.10'
- gem 'sqlite3', '1.3.13'
- gem 'pg', '0.18.4'

続いてdatabase.ymlを修正します。

sample_apps/config/database.yml

default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000
  url: <%= ENV['DATABASE_URL'] %>

development:
  <<: *default
  database: db_development

test:
  <<: *default
  database: db_test

production:
  <<: *default
  database: db_production

DBへの接続は環境変数で管理します。これでDBの切り替えが完了です。

後は、テストコードやfixutresで使用されている日付の保存形式を修正する必要があります。Time.zon.now()2.years.ago()で日付をDBに保存している箇所があるので、これをTime.zon.now().to_s(:db)2.years.ago().to_s(:db)に変更します。to_s(:db)でDBに保存できる形式に日付を変更してくれます。 また、修正箇所は下記のように見つけるとかんたんです。

$ grep -r -E "Time.zone.now|years.ago" sample_app/*

まとめ

駆け足になってしまいましたが、以上になります。Railsチュートリアルアプリのコンテナ化は、手軽に個人でもできて、かなり勉強になるので是非やってみて下さい。