こんにちは。Enjoy IT Life管理人の仁科(@nishina555)です。
前回、『ローカル開発環境の構築不要!Dockerを利用したRailsアプリ起動手順』でDocker環境でRailsアプリを起動させる手順について紹介をしました。
上記の記事ではデータベースはSQLiteを利用していました。
しかし、実際のアプリケーション開発ではMySQLやPostgreSQLを使うのが一般的です。
今回はDocker Composeを利用してRuby on RailsとMySQLをDokcer環境に構築する手順について紹介をします。
今回のゴールは『MySQLをデータベースに利用しているRailsアプリのDocker化をし、実際にレコードが保存できていることを確認する』となっています。
以下のような方の参考になればと思います。
- Docker ComposeでRuby on RailsとMySQLのコンテナをうまく連携させたい
- MySQLやPostgreSQLを利用しているRuby on RailsをDocker環境に構築したい
なお、今回の開発環境はDocker DesktopがインストールされたmacOS、rubyのバージョンは2.6.3、MySQLのバージョンは5.7となっています。
目次
今回作成するRailsアプリの構成について
まずはじめに今回作成するRailsアプリの全体像について説明します。
今回作成するRailsアプリはRuby on Railsが実行されるwebコンテナとMySQLが実行されるdbコンテナの2つのコンテナで構成されています。
記事中の『Railsアプリ』という言葉は『webコンテナとdbコンテナをあわせたWebシステム全体のこと』と定義します。
図で表現すると以下のようになります。
今回作成するRubyアプリの全体像
図にも記載していますが、Ruby on RailsからMySQLへ接続する際の設定は以下のようにします。
Ruby on RailsのDB接続設定
変数 | 値 |
user | root |
password | pass |
port | 3306(デフォルト) |
host | db |
ディレクトリ構成は以下のようになります。
rails_app
├── Dockerfile
├── docker-compose.yml
├── Gemfile
├── README.md
├── Rakefile
├── app
├── bin
├── config
├── config.ru
├── db
├── lib
├── log
├── package.json
├── public
├── storage
├── test
├── tmp
└── vendor
また、作成するRailsアプリの名前はrails_appとします。なお、webコンテナとdbコンテナはDocker Composeを利用して連携させます。
Railsアプリの作成
Railsアプリの作成手順は大まかに以下のようになります。
- webコンテナの構築
- Gemfileの用意
- railsが実行できるDockerイメージの作成
- Railsアプリの作成(ここでMySQLをDBに指定)
- docker-compose.ymlにwebコンテナを追加
- dbコンテナの構築
- docker-compose.ymlにdbコンテナを追加
- データベースの作成
以下では各手順について解説していきます。
webコンテナの構築
Docker環境のrubyを利用してRailsアプリを作成します。
Docker環境にRuby on Railsを作成するための詳細解説は『ローカル開発環境の構築不要!Dockerを利用したRailsアプリ起動手順』で紹介していますので、わからない部分は適宜参考にしていただければと思います。
Gemfileの用意
まずは作業ディレクトリの作成・移動をします。
$ mkdir rails_app && cd rails_app
Docker環境のrubyを利用してローカル環境にGemfileを用意します。
『-v pwd
:/rails_app』でローカル環境とDocker環境のディレクトリをマウントしているため、Docker環境で作成されたGemfileがローカル環境にも作成されます。
$ docker run --rm -v `pwd`:/rails_app -w /rails_app ruby:2.6 bundle init
作成されたGemfileの『gem “rails”』の部分のコメントアウトを外します。
$ vim Gemfile
Gemfile
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
- # gem "rails"
+ gem "rails"
railsが実行できるDockerイメージの作成
次に、ローカル環境のGemfileを利用して、gemがインストールされたDockerイメージを作成します。
まず、以下のようなDockerファイルを用意しましょう。
$ vim Dockerfile
Dockerfile
# ruby:2.6のDockerイメージをベースとして利用
FROM ruby:2.6
# 作業ディレクトリを/rails_appに指定
WORKDIR /rails_app
# ローカルのGemfileをDokcerにコピー
COPY Gemfile /rails_app/Gemfile
# /rails_appディレクトリ上でbundle install
RUN bundle install
上記のDockerfileをビルドしてDockerイメージを作成します。
以下のコマンドでカレントディレクトリに存在するDockerファイルからrails_appというDockerイメージを作成します。
$ docker build -t rails_app .
ビルド終了後、以下のようにDockerイメージが作成されていればOKです。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
rails_app latest 0c43deb8095d 8 seconds ago 953MB
Railsアプリの作成(ここでMySQLをDBに指定)
ここまででrailsが実行できるDockerイメージが作成できました。
次に、作成したDockerイメージを利用してrails new
を実行し、Railsアプリの雛形を作成します。
今回はMySQLをデータベースに利用するため-database=mysql
のオプションを追加します。
なお、-B
はbundle install
を省略するオプションです。
$ docker run --rm -v `pwd`:/rails_app rails_app rails new . -B --database=mysql
rails new
実行後、Ruby on Railsからデータベースへ接続するための設定ファイルが作成されますので、以下のように修正します。
$ vim config/database.yml
config/database.yml
default: &default
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
- password:
+ password: pass # MYSQL_ROOT_PASSWORDと同じにする(後述)
- host: localhost
+ host: db # MySQLのコンテナ名と同じにする(後述)
docker-compose.ymlにwebコンテナを追加
ここまででRuby on Railsの雛形を作成することができました。
Dockerファイルを以下のように編集し、Ruby on Railsが起動するDockerイメージを作成します。
Dockerfile
FROM ruby:2.6
+ RUN apt-get update -qq && apt-get install -y nodejs
WORKDIR /rails_app
COPY Gemfile /rails_app/Gemfile
RUN bundle install
+ # Start the main process.
+ CMD ["rails", "server", "-b", "0.0.0.0"]
なお、rails serverでは-bでバインドするIPアドレス(応答をするIPアドレス)の指定できます。
Dockerファイルでは0.0.0.0(ワイルドカード)を指定しているため、どのIPアドレスでリクエストがきても応答するようになっています。
webコンテナの起動方法は以下のようにするとします。
- ローカル環境とDocker環境をマウント(ディレクトリの同期)させる
- ローカル環境の3000番ポートとDocker環境の3000番ポートをマッピングさせる
- Dockerイメージはdocker-compose.ymlと同ディレクトリのDockerfileからビルドする
今回、RailsアプリはDocker Composeを利用して起動させます。
ですので、docker-compose.ymlを作成し、webコンテナの起動設定をdocker-compose.ymlに記載します。
vim docker-compose.yml
docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- '3000:3000'
volumes:
- .:/rails_app
なお、Docker Composeについて詳しく知らない方向けに『Docker Composeとは?Dockerコマンドとの比較で理解するDocker Compose入門』で入門記事も書いているのでそちらも参考にしていただければと思います。
Dockerfileを編集したのでDockerイメージの再ビルドをします。docker-composeコマンドを利用してビルドするには以下のようにします。
$ docker-compose build
以下のようにrails_app_web
というDockerイメージが作成されていればOKです。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
rails_app_web latest 2cbf2fd1905c About a minute ago 1.03GB
rails server
の実行をするイメージができたので試しにdocker-compose up
でRailsアプリを起動をしてみましょう。(この時点ではうまくいきません)
$ docker-compose up
localhost:3000
につなぐと以下のように『Unknown MySQL server host ‘db’』と表示されます。
上記のエラーは、Ruby on Railsのconfig/database.ymlで設定した『db』というホストが見つからずデータベースに接続できないことを意味しています。
dbコンテナの構築
先ほどのエラーメッセージで表示された『db』というホストは、これから作成するdbコンテナにあたります。
以下ではdbコンテナの作成手順について紹介します。
docker-compose.ymlにdbコンテナを追加
Ruby on Railsで定義したDB接続の設定をもとにdocker-compose.ymlを以下のように編集します。
$ vim docker-compose.yml
docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- '3000:3000'
volumes:
- .:/rails_app
+ depends_on:
+ - db
+ db: # Railsのhostと一致させる
+ image: mysql:5.7
+ environment:
+ MYSQL_ROOT_PASSWORD: 'pass' # Railsのpasswordと一致させる
docker-compose.ymlで追加した内容は以下の通りです。
- MYSQL_ROOT_PASSWORDとRailsのpasswordを一致させる
- dbのコンテナ名とRailsのhostを一致させる
- webコンテナのdepends_onにdbコンテナを設定する
MySQLのDockerイメージではrootユーザーのパスワードは、MYSQL_ROOT_PASSWORDという変数(設定必須)で定義されています。1
今回はrootユーザーでDB接続をするので、Ruby on Railsで設定したpasswordとMYSQL_ROOT_PASSWORDを一致させる必要があります。
depends_on
はコンテナの依存関係を表現します。今回の場合ですとwebコンテナはdbコンテナに依存していることを意味します。
Dockerコンテナを起動する順番もdepends_on
で制御することができます。今回の場合は『dbコンテナ(依存先) → webコンテナ(依存元)』の順番で起動することになります。
補足: rootユーザー以外でDB接続をする場合
Ruby on Railsで開発をする際はdevelopment環境のDBに加え、テストコードで利用されるtest環境のDBも用意する必要があります。
rootユーザーの場合は、両DBへのアクセス権限がもともと用意されています。
もし、一般ユーザーでDB接続をする場合は、MySQLのDockerイメージに対して以下の変数を設定する必要があります。
MySQLの変数とその役割について
変数 | 役割 |
MYSQL_USER | ユーザー名 |
MYSQL_PASSWORD | ユーザーパスワード |
MYSQL_ROOT_PASSWORD | rootパスワード |
MYSQL_DATABASE | ユーザーの接続対象データベース |
一般ユーザーの場合はrootユーザーと違い、MYSQL_DATABASEで接続するデータベース名を設定する必要があります。
しかし、1コンテナにつきMYSQL_DATABASEは1つしか定義できないため、development環境のDBとtest環境のDBの2つのDBに一般ユーザーで接続するには少し工夫が必要です。
一般ユーザーで2つのDBに接続できるようにする方法は例えば以下の2つがあります。
- DBコンテナを複数用意する
- docker起動時に、一般ユーザーにDBへのgrantを実行する
一般ユーザーのでのDB接続方法については今回の記事のゴールではないため、設定方法の紹介にとどめ、手順については割愛します。
具体的な手順につきましては『【Docker】Railsで理解する、複数のMySQLデータベースに一般ユーザーで接続するための環境構築手順』で紹介していますので、興味のある方はこちらをご覧になってください。
補足: 環境変数を別ファイルで一元管理する方法
今回作成したdocker-compose.ymlには環境変数が直接記載されています。
データベースの接続情報など、Dockerで利用する環境変数は別ファイルで管理したほうがよりセキュアです。
docker-compose.ymlにはenv_fileというオプションが用意されており、env_fileを利用することで環境変数を別ファイルで管理することができます。
『Dockerで利用する環境変数をenv_fileを利用して一元管理する方法』でenv_fileを利用したリファクタリング方法について紹介していますので興味のある方はご覧になってください。
データベースの作成
Dockerfileおよびdocker-compose.ymlが準備できたので、Dockerコンテナを起動してみましょう。
docker-compose.ymlに記載された起動設定でDockerコンテナを立ち上げる場合は以下のようにします。
$ docker-compose up
今回の場合ですと`docker-compose up web`を実行するとwebコンテナと、webコンテナが依存するdbコンテナの両方が起動します。
Dockerコンテナが起動したらlocalhost:3000
にアクセスしてみます。
ただし、この時点ではdbコンテナは用意しましたがRuby on Railsで利用するデータベースはまだ用意していないため、以下のように『Unknown database ‘rails_app_development’』というエラーが表示されます。
dbコンテナで起動しているMySQLに接続してデータベースの一覧を確認すると、確かにRuby on Railsで利用するデータベース(ここではrails_app_development)がないことがわかります。
docker-compose.ymlに記載したコンテナに対してコマンドを実行したい場合はdocker-compose exec [コンテナ名] [コマンド]
で実行可能です。
$ docker-compose exec db mysql -uroot -ppass
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)
webコンテナに対してマイグレーションを実行し、データベースを作成します。
$ docker-compose exec web rake db:create
Created database 'rails_app_development'
Created database 'rails_app_test'
マイグレーション実行後、データベースが作成されたことがわかります。
$ docker-compose exec db mysql -uroot -ppass
mysql> show databases;
+-----------------------+
| Database |
+-----------------------+
| information_schema |
| mysql |
| performance_schema |
| rails_app_development |
| rails_app_test |
| sys |
+-----------------------+
6 rows in set (0.00 sec)
Railsアプリの挙動確認
データベースの作成も終わったので、改めてlocalhost:3000
にアクセスしてみましょう。
以下のような初期画面が見れればOKです。
次に、実際に画面からデータを作成し、MySQLにレコードが保存されるか確認してみましょう。
今回はEventというモデルをサンプルデータとして作成してみます。まずはscaffold
で画面を作成します。
$ docker-compose exec web rails g scaffold event title:string
scaffold
でマイグレーションファイルが追加されたので、マイグレーションを実行します。
$ docker-compose exec web rake db:migrate
マイグレーション実行後、localhost:3000/events/new
にアクセスします。
画面から適当にデータを作りましょう。今回は『sample』というレコードを作りました。
rails console
で確認するとデータが保存されていることがわかります。
$ docker-compose exec web rails c
irb> Event.first
=> #<Event id: 1, title: "sample", created_at: "2019-08-05 12:59:12", updated_at: "2019-08-05 12:59:12">
mysql
で確認してもデータが保存されていることがわかります。
$ docker-compose exec db mysql -uroot -ppass -D rails_app_development
mysql> select * from events;
+----+--------+---------------------+---------------------+
| id | title | created_at | updated_at |
+----+--------+---------------------+---------------------+
| 1 | sample | 2019-08-05 12:59:12 | 2019-08-05 12:59:12 |
+----+--------+---------------------+---------------------+
1 row in set (0.00 sec)
なお、今回の記事で紹介しているDocker環境ではデータベース情報に対してデータの永続化をおこなっていません。
そのため、コンテナを削除してしまうとデータベースおよびテーブル情報も削除されてしまいます。
コンテナを削除してもデータベース情報は保持し続けるようにする方法は『Dockerのデータを永続化!Data Volumeの理解から始める環境構築入門』で紹介していますので、こちらもあわせてご覧になっていただければと思います。
ローカルのクライアントアプリからDocker環境のDBに接続する方法
ここまででMySQLをデータベースに利用しているRailsアプリのDocker化をすることができました。
データベースのレコードはmysqlコマンドを叩けばわかるのですが、ローカル環境のクライアントアプリでデータベースの内容が見れれば便利ですよね。
ここではローカル環境のクライアントアプリからDocker環境のデータベースに接続する方法を紹介します。
なお、MySQLに接続するクライアントアプリは評判の高いSequel Proを利用します。
まずはローカル環境とDocker環境のポートフォワーディングを設定するため、docker-compose.ymlに以下の行を追加してDockerコンテナを起動しなおしましょう。
$ vim docker-compose.yml
docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- '3000:3000'
volumes:
- .:/rails_app
depends_on:
- db
db: # Railsのhostと一致させる
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: 'pass' # Railsのpasswordと一致させる
+ ports:
+ - '3306:3306'
Squel Proの接続設定を行います。設定は以下のようになります。
DB接続設定
変数 | 値 |
ホスト | 127.0.0.1 |
ユーザー名 | root |
パスワード | pass |
データベース | rails_app_development |
ポート | 3306 |
接続がうまくいくと、先ほどコンソールから確認したDocker環境のデータがローカル環境のSequel Proで見ることができます。
まとめ: 複数コンテナの一元管理はdocker-composeを利用
以上でMySQLをデータベースに利用しているRailsアプリのDocker化の紹介をおわります。
なお、今回の記事では紹介しませんでしたが、Entrykitと呼ばれるツールを導入することでコンテナ起動時にコマンドの自動実行が可能になります。
例えば、今回紹介したDocker環境に対してbundle installを自動実行するように設定すれば、より快適な開発環境になります。
EntrykitをRailsのDocker環境に導入する手順については『RailsのDocker環境にEntrykitを導入し、bundle installを自動実行させる方法』で紹介しているので是非ご覧になってください。
この記事がいいなと思いましたらツイッター(@nishina555)のフォローもよろしくお願いします!