Dockerのバージョンアップに伴い、
docker-compose.ymlを一部編集しました。
(2021/3/19 18:10 追記)
Dockerを初めて勉強するとき、多くの方が「参考書」や「入門Dockerなどのサイト」で基礎的なところから学習すると思います。
しかし、Dockerとは何なのか、何のイメージも沸いていない状態で、様々な単語を羅列されても、さっぱりわからないという方も多いと思います。( 少なくとも私はそうでした。)
なので極論、基礎すっ飛ばしてまず実際にDockerで仮想環境を構築しながら学んでいこうという話です。
そして何となくでいいのでイメージを掴んでから、基礎を学べば良いと思います。
Dockerを初めて学習する方の中で、Ruby on Railsで開発したアプリケーションをポートフォリオとしている方も多いのではないでしょうか。
なので今回は、Dockerを用いてRuby on Railsの環境構築をする方法 をご紹介いたします。
ですが、超基本的なことだけ先に説明させてください。
( とにかく早く環境構築した人は 前提 まで飛んでください )
Dockerとは
- Dockerエンジンとは -
__Dockerエンジン__とは Dockerイメージの作成 や コンテナの起動 などを行う、いわばDockerの要です。
- コンテナとは -
__コンテナ__とはDockerエンジン上で構築される__仮想環境そのもの__です。
コンテナは、CentOS や Ubuntsなどの OS、Nginx や MySQLなどの ミドルウェア、Rails や WordPressなどアプリケーションまで、様々な環境を構築することができます。
- Dockerイメージとは -
__Dockerイメージ__は、コンテナを作成するための レシピ のようなものです。
CentOS、MySQL、Ruby などなど、他にも数えきれないほどのDockerイメージがあります。
- Dockerfileとは -
__Dockerfile__は、オリジナルのDockerイメージを作成するための設計書のようなものです。
既存のDockerイメージをベースに、パッケージをインストールしたり、ファイルを書き換えたりした、新しいイメージを作成することができます。
- Docker composeとは -

Docker compose とは、複数のコンテナの構築、実行する手順を自動化するツールです。
その手順などを記したファイルがdocker-compose.ymlで、これのおかげで少ないコマンドの実行で複数のコンテナを起動することができます。
環境
- MacOS
- Docker 20.10.5
- docker-compose 1.28.5
- Ruby 2.5.7
- Rails 5.2.4
- MySQL 5.7
前提
-
DockerおよびDocker composeのインストールを済ませてください - これ以降の説明の
myappの部分はご自身のアプリケーション名に変えてください - とにかく早く環境構築したい方は、説明を飛ばし、コピペして コマンドを実行してください。
アプリケーションの作業ディレクトリを作成
mkdirで作業ディレクトリを作成します。
$ mkdir myapp
必要なファイルを用意
ファイル構成は以下のようになります。
myapp
|-- Dockerfile
|-- docker-compose.yml
|-- Gemfile
|-- Gemfile.lock
それでは、先ほど作成したディレクトリの中に以下の各ファイルを作成していきます。
① Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 5.2.4', '>= 5.2.4.2'
③で作成するDockerfile で bundle installする項目があるのですが、そのときにこのGemfileを使います。
② Gemfile.lock
Gemfile.lockの中身は空でOKです。
Gemfile.lock は Gemfileとセットで必要なので、用意します。
③ Dockerfile
FROM ruby:2.5.7
# ベースにするイメージを指定
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs default-mysql-client vim
# RailsのインストールやMySQLへの接続に必要なパッケージをインストール
RUN mkdir /myapp
# コンテナ内にmyappディレクトリを作成
WORKDIR /myapp
# 作成したmyappディレクトリを作業用ディレクトリとして設定
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
# ローカルの Gemfile と Gemfile.lock をコンテナ内のmyapp配下にコピー
RUN bundle install
# コンテナ内にコピーした Gemfile の bundle install
COPY . /myapp
# ローカルのmyapp配下のファイルをコンテナ内のmyapp配下にコピー
Dockerfileは自分オリジナルの イメージ を作成するための設計書のようなものです。
今回はruby:2.5.7という元々誰かが作ったイメージをベースに、Railsのインストールに必要なパッケージをインストールしたり、myappをコピーしたり、bundle installしたりと、様々なもの加えているというわけです。
④ docker-compose.yml
version: '3'
# docker-composeの書式のバージョンを指定します。(原則、最新を指定する)
services:
db:
image: mysql:5.7
environment:
MYSQL_USER: user
MYSQL_ROOT_PASSWORD: pass
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
web:
build: .
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
volumes:
- .:/myapp/
ports:
- 3000:3000
depends_on:
- db
volumes:
mysql_data:
docker-compose.ymlは複数のコンテナを定義し、実行するためのファイルです。
ファイルに書かれている単語をひとつずつ解説してきます。
services:
servicesの中でコンテナを定義します。
今回はMySQL と Railsをコンテナ化するので、名前をわかりやすくdb、webとします。
実際に作成されるコンテナ名は__「アプリケーション名 + サービス名 + 連番」__でmyapp_db_1、myapp_web_1となります。
image:
利用するイメージを指定し、コンテナを構築します。
enviroment:
MySQLに関する環境変数を設定します。(今回はパスワードのみ)
パスワードはご自身で決めて構いません。
ports:
ポート番号に関するを設定します。
MySQL は 3306、 Rails は 3000 というポート番号がデフォルト値として設定されています。
Railsに関してはおなじみのlocalhost:3000の 3000 ですね。
Dockerを使わず、ローカルで環境構築した場合は直接このlocalhost:3000にアクセスすれば良かったわけですが、Dockerを使った場合、コンテナに外部からアクセスすることができないので、ちょっとした工夫が必要です。
そこで登場するのがこのports:です。
ports: は - ローカル側のポート番号 : コンテナ側のポート番号で表します。
今回、ports: - 3000:3000としていますが、説明をわかりやすくするため、仮にもしports: - 9000:3000に設定するとします。
これはコンテナ側の3000ポートに9000ポートでのアクセスを許可するという意味になります。
つまりこの場合、localhost:9000でアクセス可能になるということになります。
volumes:
ボリュームに関する設定をします。
ボリュームとはコンテナにおいて生成されるデータを永続的に保存するためのものです。
MySQLを例にとると、MySQLをコンテナ化し、そこに テーブル や カラム などのデータを保存したとします。
しかし、そのMySQLのコンテナを削除してしまうと、保存してあったテーブル、カラムなどのデータも一緒に削除されてしまいます。
それはまずいので、データだけローカルにも保存しておく、この仕組みをボリュームといいます。
このボリュームを利用すると、仮にコンテナを削除したとしても、新しくコンテナを立ち上げたときに、そのデータを再利用することができます。
ボリュームを保存する方法は大きく分けて2つあります。
1つ目は、保存したいディレクトリをマウントする方法、2つ目は、名前付きボリュームを利用する方法 です。
今回MySQLは2つ目の名前付きボリュームを利用する方法を使います。
この方法は、ローカルに名前付きのボリュームを作成し、そこにコンテナ側の指定したディレクトリを保存するというものです。
名前付きボリュームの場合、volumes: は - ボリューム名 : コンテナ側の保存させたいディレクトリで表します。
今回、ボリューム名 を mysql_dataとし、コンテナ側の保存させたいディレクトリは MySQLのデータ保存場所である/var/lib/mysqlを指定します。
結果、volumes: - mysql_data:/var/lib/mysqlという書き方になります。
そして、version: や services: と同じ段落(トップレベル)の、一番下に書いているvolumes:にボリューム名を記載し、名前付きボリュームであることを明示します。
次にRailsは、1つ目の保存したいディレクトリをマウントする方法を使います。
この方法は、「ローカル側の指定したディレクトリ」と「コンテナ側の指定したディレクトリ」を同期させて、両者を常に同じ状態に保つというものです。
マウントする場合、volumes: は - ローカル側の同期させたいディレクトリ : コンテナ側の同期させたいディレクトリで表します。
今回、volumes: - .:/myapp/としています。
つまり、.( docker-compose.ymlのあるディレクトリ、つまりローカル側のmyapp配下 ) と /myapp/( コンテナ側のmyapp配下 ) を同期するということです。
これにより、コンテナ起動後、ローカルのmyapp配下のファイルなどを編集すると、コンテナ側にも編集が反映されます。
build:
Dockerfileのあるディレクトリをして指定します。( docker-compose.ymlから見て同じディレクトリにDockerfileが存在するので.)
ここのステップで ③で説明したDockerfileを利用しオリジナルのイメージを作成し、コンテナを構築します。
command:
コンテナ起動時の実行されるコマンドを設定します。
今回設定するコマンドは、bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" です。
bash -c ""は コンテナの中に入り""内のコマンドを実行するという命令です。
rm -f tmp/pids/server.pidで rails server が起動していた場合、停止します。( 何らかの原因でrails server がすでに起動していた場合、新しいrails serverが起動できないため。)
bundle exec rails s -p 3000 -b '0.0.0.0'で rails server を起動します。
&&は複数のコマンドを実行するときに用います。
depends_on:
コンテナの作成順序の設定です。
depends_on: - dbは、MySQLのコンテナが起動してからRailsのコンテナを起動するという意味です。
rails new を実行
$ docker-compose run web rails new . --force --database=mysql --skip-bundle
アプリケーションの作業ディレクリに移動し、docker-compose runコマンドでrails newを実行します。
--force : 同じファイルがある場合、上書きする
--database=mysql : データベースにMySQLを指定する
--skip-bundle : bundle install をスキップする。( bundle installはあとで行います。)
このコマンドを実行すると、Dockerfileを元に、イメージとコンテナを作成し、そのコンテナの中でrails newを実行します。
よって、ローカルにも見慣れたファイルが作成されたと思います。
docker-compose build を実行
$ docker-compose build
rails new で Gemfileが更新されたので、bundle installするため、docker-compose buildを実行します。
先ほどのdocker-compose runで作成されたイメージはrails newされる前のGemfileをbundle installしたイメージなので、更新されたGemfileをbundle installしたイメージを再度作成する必要があります。
database.yml を編集
# MySQL. Versions 5.1.10 and up are supported.
#
# Install the MySQL driver
# gem install mysql2
#
# Ensure the MySQL gem is defined in your Gemfile
# gem 'mysql2'
#
# And be sure to use new-style password hashing:
# https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html
#
default: &default
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
# 以下2行を編集
password: pass
host: db
development:
<<: *default
database: myapp_development
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: myapp_test
# As with config/secrets.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
# On Heroku and other platform providers, you may have a full connection URL
# available as an environment variable. For example:
#
# DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase"
#
# You can use this database configuration with:
#
# production:
# url: <%= ENV['DATABASE_URL'] %>
#
production:
<<: *default
database: myapp_production
username: myapp
password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>
password: と host:を編集します。
password: には docker-compose.yml の MYSQL_ROOT_PASSWORD:に書いたパスワードを記載します。
host: には docker-compose.ymlで命名したMySQLのコンテナ名dbを記載します。
docker-compose up -d でコンテナを起動
$ docker-compose up -d
このコマンドでコンテナを起動します。
-dをつけることでバックグラウンドで起動します。
これでコンテナが起動できました。
以下のコマンドでちゃんと起動しているか確認しましょう。
$ docker-compose ps
Name Command State Ports
----------------------------------------------------------------------------------------
myapp_db_1 docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp, 33060/tcp
myapp_web_1 bash -c rm -f tmp/pids/ser ... Up 0.0.0.0:3000->3000/tcp
このように、それぞれのコンテナが UP になっていれば起動成功です。
データベースを作成
以下のコマンドでデータベースを作成します。
$ docker-compose exec web rails db:create
localhost:3000にアクセス
localhost:3000にアクセスし、以下のページが表示されれば DockerによるRailsの環境構築 は完了です。

環境構築後の開発の仕方
これまで通り、ファイルの編集などは基本ローカル環境で行います。
しかしrails g や rails db:migrateなどの railsコマンド はコンテナ内で実行します。
なので、コンテナに入る必要があります。
以下のコマンドでRailsのコンテナに入ります。
$ docker-compose exec web bash
するとプロンプト( ターミナルの左側の部分 )が以下のように変わると思います。( @の後ろはコンテナのID )
root@97b8e3430f3f:/myapp#
これでコンテナの中に入れました。ここで railsコマンド を実行してください。
これまでのローカル環境での開発で、ターミナルで実行していたコマンドは__全てこのコンテナ内で行います__。( gitコマンドはローカル環境で行います )
コンテナを抜けるには、以下のコマンドを実行してください。( もしくはcontrol + d )
$ exit
- ログの表示 -
これまではrails sでサーバーを起動し、ログが表示されていたと思います。
コンテナ内において、ログを表示させる方法は以下の通りです。
コンテナ内に入り、以下のコマンドを実行します。
$ tail -f log/development.log
これでログが表示されます。
ターミナルでタブを3つ開き、「ローカルの操作をするタブ」、「コンテナ内でrailsコマンドなどを実行するタブ」、__「ログを表示するタブ」__に分けると効率的な開発ができるかもしれません。
- デバッグに関して -
開発中にエラーが発生し、controllerを編集し、デバッグしたとします。
この場合、一度サーバーを再起動させると思います。
Dockerの場合、Railsのコンテナ( myapp_web_1 )ごと以下のコマンドで再起動します。
$ docker-compose restart web
しかし、デバッグするたびにいちいちコンテナを再起動するのは非効率です。
なので、サーバー起動中もコードの更新をチェックしてくれるように設定します。
config/environments/development.rbの一番下に記載されている以下を
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
次のように変更します。
config.file_watcher = ActiveSupport::FileUpdateChecker
これで再起動を行わずにデバッグが可能になります。
その他の基本的なDockerのコマンド
・ コンテナの停止、削除
$ docker-compose down
・ コンテナの停止、削除および紐づいている名前付きボリュームの削除
$ docker-compose down -v
※ 永続化しているデータが消えるの要注意 !
・ docker-composeのログを出力
$ docker-compose logs
コンテナがうまく起動しなかったときなどにログを見ると、原因がわかったりします。
・ イメージの一覧
$ docker images
・ イメージの削除
$ docker rmi イメージID
・ 名前付きボリュームの一覧
$ docker volume ls
・ 名前付きボリュームの削除
$ docker volume rm ボリュームの名前
まとめ
最初の内は理解できないことも多いと思います。
ひとつずつ着実に理解していくのではなく、全体的に漠然と理解していくと良いと思います。
この記事を読んでわからないことがあれば、コメントしていただければお答えいたします !
最後まで読んでいただきありがとうございました。
