環境構築からデータ作成まで!Rails x MySQLのDocker Compose環境構築手順

Ruby

こんにちは。Enjoy IT Life管理人の仁科(@nishina555)です。

前回、『ローカル開発環境の構築不要!Dockerを利用したRailsアプリ起動手順』で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アプリの作成手順は大まかに以下のようになります。

Railsアプリの作成手順
  1. webコンテナの構築
    1. Gemfileの用意
    2. railsが実行できるDockerイメージの作成
    3. Railsアプリの作成(ここでMySQLをDBに指定)
    4. docker-compose.ymlにwebコンテナを追加
  2. dbコンテナの構築
    1. docker-compose.ymlにdbコンテナを追加
    2. データベースの作成

以下では各手順について解説していきます。

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のオプションを追加します。
なお、-Bbundle 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アドレスでリクエストがきても応答するようになっています。

Docker環境で起動したアプリケーションに外部からアクセスをする場合、『0.0.0.0』と指定をしないとアクセスできないので気をつけてください。 (Docker環境のlocalhostとローカル環境のlocalhostは指すものが違うため。)

webコンテナの起動方法は以下のようにするとします。

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入門』で入門記事も書いているのでそちらも参考にしていただければと思います。

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と一致させる
今回はRuby on RailsからMySQLへ接続するポートはデフォルトの3306を利用しているため、dbコンテナでポート解放(expose)をする必要はありません。

docker-compose.ymlで追加した内容は以下の通りです。

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コンテナに接続する方法
  • DBコンテナを複数用意する
  • docker起動時に、一般ユーザーにDBへのgrantを実行する

一般ユーザーのでのDB接続方法については今回の記事のゴールではないため、設定方法の紹介にとどめ、手順については割愛します。

具体的な手順につきましては『【Docker】Railsで理解する、複数のMySQLデータベースに一般ユーザーで接続するための環境構築手順』で紹介していますので、興味のある方はこちらをご覧になってください。

【Docker】Railsで理解する、複数のMySQLデータベースに一般ユーザーで接続するための環境構築手順

補足: 環境変数を別ファイルで一元管理する方法

今回作成したdocker-compose.ymlには環境変数が直接記載されています。

データベースの接続情報など、Dockerで利用する環境変数は別ファイルで管理したほうがよりセキュアです。

docker-compose.ymlにはenv_fileというオプションが用意されており、env_fileを利用することで環境変数を別ファイルで管理することができます。

『Dockerで利用する環境変数をenv_fileを利用して一元管理する方法』でenv_fileを利用したリファクタリング方法について紹介していますので興味のある方はご覧になってください。

Dockerで利用する環境変数をenv_fileを利用して一元管理する方法

データベースの作成

Dockerfileおよびdocker-compose.ymlが準備できたので、Dockerコンテナを起動してみましょう。

docker-compose.ymlに記載された起動設定でDockerコンテナを立ち上げる場合は以下のようにします。

$ docker-compose up
docker-compose.ymlに記載されている特定のコンテナのみを立ち上げる場合は`docker-compose up [コンテナ名]`となります。`depend_on`の記載がある場合、依存関係のコンテナも起動します。

今回の場合ですと`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のデータを永続化!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を自動実行させる方法』で紹介しているので是非ご覧になってください。

RailsのDocker環境にEntrykitを導入し、bundle installを自動実行させる方法

この記事がいいなと思いましたらツイッター(@nishina555)のフォローもよろしくお願いします!