November 29, 2019
ちょっと実践的なDockerの練習というテーマで「Railsチュートリアルのアプリをコンテナ化」をしてみました。今回はその時のことをまとめていきたいと思います。 ちなみに、Docker初心者の方でも分かりやすいように書いていくので、ご心配なく。
これがないと始まらない。ということで、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
まずは、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 にアクセスしてアプリに接続できるはずです。
次は、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を変更しているため、この状態で起動してもエラーがでちゃいます。
やる事は3つ。コンテナはほぼ関係ないです。
既に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チュートリアルアプリのコンテナ化は、手軽に個人でもできて、かなり勉強になるので是非やってみて下さい。