Docker 公式ドキュメントの Rails Quickstart 完全解説

Docker の公式ドキュメントに Rails の Quickstart があります:
[Quickstart: Compose and Rails | Docker Documentation](https://docs.docker.com/compose/rails/)

こちらのドキュメントを参考に Rails を始める方が増えており、
Tetatail で結構な件数の質問に回答しました。

この Quickstartは説明が不十分だったり間違いがあったりで
Rails に慣れてないと行間を読んで正しい手順を踏むことは難しいでしょう。

この記事で指摘している Quickstart の間違いなどは、すでに GitHub 上で PR を投稿しています。
[Yukihiko shinoda/update quickstart rails by yukihiko-shinoda · Pull Request #11215 · docker/docker.github.io](https://github.com/docker/docker.github.io/pull/11215)

ここで Rails の Quickstart についてまとめて解説しておきます。

## この Quickstart を実施するために必要なツール

- Docker Desktop:
  - [Docker Desktop for Windows - Docker Hub](https://hub.docker.com/editions/community/docker-ce-desktop-windows)
  - [Docker Desktop for Mac - Docker Hub](https://hub.docker.com/editions/community/docker-ce-desktop-mac/)

## この解説を理解するために必要な基礎知識

この Quickstart を理解するためには、次の基礎知識が必要です:

- エディターでテキストファイルを編集可能
  - [ファイルやプログラムの編集には Visual Studio Code を使おう | ultra code](https://futureys.tokyo/lets-use-visual-studio-code-for-editing-file-or-program/)
  - [どうやって Visual Studio Code を日本語化したらいいの？ | ultra code](https://futureys.tokyo/how-should-i-translate-vscode-in-japanese/)
  - [まず Visual Studio Code でファイルを編集してみよう | ultra code](https://futureys.tokyo/first-lets-edit-file-with-vscode/)
- Docker のコマンドを使ってサービスを実行する程度の基礎知識
  - [Orientation and setup | Docker Documentation](https://docs.docker.com/get-started/)
  - [Docker overview | Docker Documentation](https://docs.docker.com/get-started/part2/)
- Docker Compose の基礎知識
  - [Docker Desktop で 開発用 Web アプリを起動してみよう | ultra code](https://futureys.tokyo/lets-run-the-web-application-for-development-by-docker-desktop-and-access-it-by-browser/)
  - [docker-compose.yml の内容を理解しよう | ultra code](https://futureys.tokyo/lets-understand-contents-of-docker-compose-yml/)

## 理解するために必要ないツール・知識

- Rails チュートリアル
    - Rails チュートリアルで解説されていることと、この Quickstart で得られる知識は、大部分が別の内容です
    - この Quickstart を理解する上で必要な Rails に関する知識はこの記事内で解説します

## この Quickstart は何をするためのものなの？

この Quickstart ガイドでは、Docker Compose を使用して Rails / PostgreSQL アプリを設定および実行する方法を理解します。

## この Quickstart はどんなことをしているの？

この Quickstart は次の手順を実施します:

1. [Define the project]
   - Docker イメージのビルドに必要なファイルを作成します
2. [Build the project]
   - 手順 1. で準備したファイルをもとに Rails の実行環境を起動して Rails を動かし新規 Rails アプリケーションを作成します
     - このとき自動的に Docker イメージがビルドされます
   - 新規作成した Rails アプリケーションを使い Docker イメージをビルドし直します
3. [Connect the database]
   - Rails の実行環境とデータベースのサービスを一度に起動します
4. [View the Rails welcome page!]
   - ブラウザで Rails の welcome page が表示されることを確認します

### この Quickstart が理解しづらい原因

この Quickstart は手順 1. [Define the project] で準備するファイルやその内容が
相当な量であるにも関わらず、それらの目的についての解説が不十分であるためです。

- 上記の手順 1. [Define the project] で準備するファイルの内容を最初からすべて理解することは難しいです
  - 準備するファイルの内容が次の目的を兼ねる内容になっており、どちらの目的のための内容なのか分かりづらいため
      - 手順 2. [Build the project] で行う新規 Rails アプリケーション作成のための内容
      - 最終的な Rails アプリケーション実行のための内容
- 手順 2. [Build the project] で Rails の実行環境のためのイメージを再度ビルドし直しています
- 手順 2. のコマンドが誤っています
- `Dockerfile` に不要な内容が含まれています
- `docker-compose.yml`
    - 誤っています
    - 不要な内容が含まれています

以降に、各手順で注意すべきポイントを解説します。

## 各手順の解説

### [Define the project]

この手順では、Docker イメージのビルドに必要なファイルを作成します。

この手順を終えると、プロジェクトのディレクトリー内には次のようなファイルがあるはずです:

```text
project/
+---docker-compose.yml
+---Dockerfile
+---entrypoint.sh
+---Gemfile
+---Gemfile.lock
```

ここで、初めてこの Quickstart を実施する人は
次の理由により混乱してしまうことでしょう:

- ファイルの内容が誤っていたり、不要な内容が含まれているため
- ここで準備するファイルやその内容が相当な量であるにも関わらず、それらの目的についての解説が不十分であるため

そこで、この記事では `Dockerfile` と `docker-compose.yml` の内容を次のように修正します:

```docker:Dockerfile
FROM ruby:2.5
RUN apt-get update -qq && apt-get install -y nodejs
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
```

```yaml:docker-compose.yml
version: '3.8'
services:
  db:
    image: postgres
    environment:
      POSTGRES_PASSWORD: password
  web:
    build: .
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
```

#### 各ファイルの目的の整理

ここで準備するファイルは次の 2 つの目的のためです:

- 手順 2. [Build the project] で行う新規 Rails アプリケーション作成のため
- 手順 3. [Connect the database] で行う最終的な Rails アプリケーション実行のため

どのファイルがどの目的のためのファイルなのかは、次の通りです:

ファイル|目的
---|---
docker-compose.yml|手順 2, 3.
Dockerfile|手順 2, 3.
entrypoint.sh|手順 3.
Gemfile|手順 2.
Gemfile.lock|手順 2.

#### まず、手順 2. のために必要なファイルだけを準備できないの？

手順 2. のために必要なファイルだけを準備すると、次のようになります:

##### `Dockerfile`

手順 2. で必要な処理以外をコメントアウトします:

```docker:Dockerfile
FROM ruby:2.5
# RUN apt-get update -qq && apt-get install -y nodejs
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
# COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
# COPY . /myapp

# Add a script to be executed every time the container starts.
# COPY entrypoint.sh /usr/bin/
# RUN chmod +x /usr/bin/entrypoint.sh
# ENTRYPOINT ["entrypoint.sh"]

# # Start the main process.
# CMD ["rails", "server", "-b", "0.0.0.0"]
```

###### `Gemfile` を `COPY` して `bundle install` を実行する理由

この後の手順 2. では `rails new` というコマンドを使い、
新規 Rails アプリケーションを作成します。

`rails new` コマンドを使うためには、Rails をインストールする必要があります。
Rails は「Gem パッケージ」という形式で配布されています。
「Gem パッケージ」とは、Ruby のプログラムを配布するための形式です。

参考: [パッケージ - 意味・説明・解説 : ASCII.jpデジタル用語辞典](https://yougo.ascii.jp/caltar/%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8)

Gem パッケージをインストールするためには、
`bundler` という、Gem パッケージを一度にインストールするためのツールの
 `bundle install` コマンドを使います。

`bundle install` コマンドは `Gemfile` というファイルを参照してパッケージをインストールします。

ここで出てきた命令一覧:

- [RUN | Dockerfile reference | Docker Documentation](https://docs.docker.com/engine/reference/builder/#run)
- [COPY | Dockerfile reference | Docker Documentation](https://docs.docker.com/engine/reference/builder/#copy)

###### `/myapp` を新規作成して `WORKDIR` に指定する理由

ここで、`/myapp` ディレクトリーを作成して作業ディレクトリーとして指定しています。
これは、ベースイメージとして指定されている `ruby:2.5` は、規定の作業ディレクトリーである `/` には
大切なディレクトリーがたくさんあり、
Rails アプリケーションのコードを展開したり、後述する「`bind` マウント」を行うと
様々な不具合が起きるためです。

ベースイメージの作業ディレクトリーを調べる方法は次の通りです:

```docker
FROM ruby:2.5
RUN pwd
```

```console
$ docker-compose build
db uses an image, skipping
Building web
Step 1/2 : FROM ruby:2.5
 ---> a5c07a6f3bb6
Step 2/2 : RUN pwd
 ---> Running in 43f0381dd652
/
```

参考: [Answer: Is it possible to show the \`WORKDIR\` when building a docker image?](https://stackoverflow.com/a/37783583/12721873?stw=2)


ここで出てきた命令一覧:

- [WORKDIR | Dockerfile reference | Docker Documentation](https://docs.docker.com/engine/reference/builder/#workdir)
- [FROM | Dockerfile reference | Docker Documentation](https://docs.docker.com/engine/reference/builder/#from)

##### `Gemfile`

`Gemfile` は Quickstart で提示されている内容をそのまま準備します:

```ruby:Gemfile
source 'https://rubygems.org'
gem 'rails', '~>5'
```

`Gemfile` には、次のような情報を記述します:

- Gem パッケージをどこからダウンロードしてインストールするか
- インストールする Gem パッケージの一覧

この場合は標準の Gem パッケージ配信サーバーである https://rubygems.org から
Rails のバージョン 5 をインストールします。

参考: [Bundler: The best way to manage a Ruby application's gems](https://bundler.io/gemfile.html#gemfiles)

##### `docker-compose.yml`

続いて、`docker-compose.yml` です。

手順 2. で必要な処理以外をコメントアウトします:

```yaml:docker-compose.yml
version: '3'
services:
  # db:
  #   image: postgres
  #   environment:
  #     POSTGRES_PASSWORD: password
  web:
    build: .
    volumes:
      - .:/myapp
    # ports:
    #   - "3000:3000"
    # depends_on:
    #   - db
```

###### build 命令の解説

`db` サービスはここでは必要ありませんので、コメントアウトしました。
また、`web` サービス内で手順 2. で必要な設定は `build` と `volume` のみです。

`build` は、先ほど作成した `Dockerfile` と `Gemfile` を使ってイメージをビルドするために必要です。
`build: .` という指定は、
この `web` サービスのコンテナーのもととなるイメージとして、
`docker-compose.yml` があるディレクトリーに存在するファイルを
コンテキストとしてビルドしたイメージを使うことになります。

参考: [build | Compose file version 3 reference | Docker Documentation](https://docs.docker.com/compose/compose-file/#build)

###### volumes 命令の解説

`volumes` は、この設定がないと、
手順 2. で `rails new` コマンドで作成する新規 Rails アプリケーションが、ホスト側のディレクトリーに残りません。

`- .:/myapp` という `volumes` の設定は
ホスト側の `docker-compose.yml` があるディレクトリーをコンテナー内のディレクトリツリーの `/myapp` に関連付けます。
こうすることで、手順 2. で実行する `rails new` コマンドで作成された新規 Rails アプリケーションが
ホスト側のディレクトリーに出力されるようになります。

このような volume の設定を `bind` といいます。

参考:
[Use bind mounts | Docker Documentation](https://docs.docker.com/storage/bind-mounts/)
[volumes | Compose file version 3 reference | Docker Documentation](https://docs.docker.com/compose/compose-file/#volumes)

##### まとめ

手順 2. のために必要なファイルだけを準備すると、次のようになります:

```text
project/
+---docker-compose.yml
+---Dockerfile
+---Gemfile
```

`Dockerfile` の中で、手順 2. に不要なコードをコメントアウトしたので
`Gemfile.lock` は必要なくなりました。

### [Build the project]

#### 新規 Rails アプリケーションの作成

冒頭で実行する次のコマンドについて解説します:

```console
docker-compose run web rails new . --force --no-deps --database=postgresql
```

このコマンドは間違っており、正しくは次のコマンドです:

```console
docker-compose run --no-deps web rails new . --force --database=postgresql
```

上記のコマンドは `docker-compose --no-deps run web` と `rails new . --force --database=postgresql` に分けて考えます。

##### `docker-compose --no-deps run web`

上記のコマンドは `docker-compose.yml` で定義した `web` サービスのコンテナーを起動して実行します。

`docker run` コマンドの書式は次の通りです:

```console
docker-compose run [options] [-v VOLUME...] [-p PORT...] [-e KEY=VAL...] [-l KEY=VALUE...] SERVICE [COMMAND] [ARGS...]
```

`--no-deps`: Compose ファイル内の `depends_on` などで関係づけられたサービスを開始しません

つまり、`--no-deps` は `docker-compose.yml` 内の `db` サービスのコンテナーを起動しないための設定です。
(この時点ではデータベースは必要ないため)

`docker-compose run` コマンドは、
コンテナーを起動するためのイメージがまだビルドされていなければ、自動的にビルドを行います。

`Dockerfile` 内で、コンテナーを実行したときの `CMD` が定義されていますが、
このトレーニングのように `docker-compose run` コマンドでサービス名の後にコマンドを記述した場合は
`CMD` の内容は無視され、代わりにコマンドラインから入力した内容が実行されます。

##### `rails new . --force --database=postgresql`

このコマンドは新規 Rails アプリケーションを作成します。

`rails new` コマンドの書式は次の通りです:

```console
rails new APP_PATH [options]
```

`-f`, [`--force`]: すでに存在するファイルを上書きします
`-d`, [`--database=DATABASE`] 選択したデータベース向けに初期設定します
選択肢: `mysql` / `postgresql` / `sqlite3` / `oracle` / `frontbase` / `ibm_db` / `sqlserver` / `jdbcmysql` / `jdbcsqlite3` / `jdbcpostgresql` / `jdbc`
省略時: `sqlite3`

このコマンドによって `Gemfile` を新規 Rails アプリケーション用の内容に上書きするので
`--force` オプションがない場合、`Gemfile` を上書きするかどうかの確認が表示されます。

#### イメージの再ビルド

手順 1. で、この解説記事に従って `entrypoint.sh` をまだ作成していない場合は、この時点で作成します:

```bash
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
```


また、`Dockerfile` の一部コメントアウトしていた部分を、次のようにコメントインします:

```docker:Dockerfile
FROM ruby:2.5
RUN apt-get update -qq && apt-get install -y nodejs
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
# COPY . /myapp

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
```

まだコメントアウトしたままの命令がありますが、
これらは手順 4. を終えた後、Rails アプリケーションのイメージを作成するときに必要となる命令です。
手順 4. を終えた後で解説します。

次のコマンドを実行すると、イメージが再ビルドされます:

```console
docker-compose build
```

##### `Dockerfile` の解説

###### `ENTRYPOINT` に Shell Script を設定し `CMD` でコマンドを指定する理由

Rails Server の二重起動を防ぐための排他ファイルである `server.pid` が
何らかの原因で残ってしまった場合を想定して、
コンテナー起動時、Rails Server を起動する前に
`entrypoint.sh` 内で `server.pid` を削除する処理を実行しています。

ここで重要なのは、`ENTRYPOINT` と `CMD` 命令を使った Dockerfile のデザインパターンで
イメージからコンテナーを起動したときの規定の動作を定義していることです。

`ENTRYPOINT` と `CMD` を両方とも配列形式で与えると、すべての配列の要素を並べたコマンドが実行されます。

参考: [Understand how CMD and ENTRYPOINT interact | Dockerfile reference | Docker Documentation](https://docs.docker.com/engine/reference/builder/#understand-how-cmd-and-entrypoint-interact)

この例のように、`ENTRYPOINT` に Shell Script を設定し、
その Shell Script のコード内の最後で `exec "$@"` を呼び出す設計は
Dockerfile のデザインパターンとして広く使われています。
憶えておいて損はありません。

こうすることで、`CMD` で指定したコマンドを実行する前に何らかの初期化処理を実行することができるのです。

参考:

- [Docker - How to use an entrypoint script to initialize container data at runtime](https://success.docker.com/article/use-a-script-to-initialize-stateful-container-data)
- [Exec form ENTRYPOINT example | Dockerfile reference | Docker Documentation](https://docs.docker.com/engine/reference/builder/#exec-form-entrypoint-example)
- [Bourne Shell Builtins (Bash Reference Manual)](https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html#Bourne-Shell-Builtins)
- [Unix / Linux - Special Variables - Tutorialspoint](https://www.tutorialspoint.com/unix/unix-special-variables.htm)

ここで出てきた命令一覧:

- [CMD | Dockerfile reference | Docker Documentation](https://docs.docker.com/engine/reference/builder/#cmd)
- [ENTRYPOINT | Dockerfile reference | Docker Documentation](https://docs.docker.com/engine/reference/builder/#entrypoint)

###### `bundle install` の前に `Gemfile.lock` を `COPY` する理由

このようにしないと、イメージを再ビルドしたときに、
Rails アプリケーションが開発時とは異なる動作や不具合を起こす可能性があります。
開発時とは異なるバージョンの Gem パッケージを使って動作することになる可能性があるためです。

Gemfile と Gemfile.lock は、どちらもインストールする Gem パッケージを管理するためのファイルですが、
それぞれ次のように扱います:

- `Gemfile`
    - 開発者が目的に合わせて内容を編集します
    - プロジェクトが直接呼び出す Gem パッケージを定義します
    - 特別な理由がない限りバージョンを固定しません
- `Gemfile.lock`
    - 開発者が直接編集せず、`Gemfile` の内容をもとにコマンドでコンパイルして更新します
    - プロジェクトが直接呼び出さない Gem パッケージも含めた、すべての必要な Gem パッケージが定義されます
    - すべてのパッケージがバージョンまで固定されています

`bundle install` コマンドを実行したとき、`Gemfile.lock` がなく、`Gemfile` のみが存在すると、
`Gemfile` で指定されている Gem パッケージの最新バージョンがインストールされます。

`Gemfile.lock` が存在し、`Gemfile` に特に更新がなければ
`Gemfile.lock` の内容に従って、開発時と同じバージョンの Gem パッケージがインストールされます。

参考:

- [Summary | Bundler: The best way to manage a Ruby application's gems](https://bundler.io/rationale.html#summary)
- [bundle install | Bundler: The best way to manage a Ruby application's gems](https://bundler.io/bundle_install.html)

###### `nodejs` をインストールする理由

Rails は JavaScript 実行環境である Node.JS を利用しており、
`nodejs` をインストールしていないと、Rails Server 実行時に次のようなエラーが発生します:

```console
web_1  | /usr/local/bundle/gems/execjs-2.7.0/lib/execjs/runtimes.rb:58:in `autodetect': Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)
```

##### どうして再ビルドが必要なの？

`rails new` コマンドで上書きされた `Gemfile` には、
Rails 本体の他にも Rails アプリケーションを動作させるために必要な多くのパッケージが記載されています。

しかし、先ほどの `docker-compose run` コマンドでビルドされたイメージには、
Rails 本体しかインストールされていません。
これは、`docker-compose run` コマンドでビルドを行ったときの `Gemfile` には Rails しか記述されていなかったためです。

なお、ここで次のような記述があります:

> This, and changes to the Gemfile or the Dockerfile, should be the only times you’ll need to rebuild.

この意味についてはすべての手順を終えた後で解説しますので、
学習効率を考慮して、ひとまず次の手順に進むことをお奨めします。

### [Connect the database]

ここでは 2 つの設定を行っています:

- Rails アプリケーションのデータベース接続設定を `db` サービスに接続できるように変更
- `db` サービスに Rails アプリケーションが利用するデータベースを新規作成

#### Rails アプリケーションのデータベース接続設定を `db` サービスに接続できるように変更

手順 2. [Build the project] で生成された `config/database.yml` の `default: &default` に次の要素を追加します:

```yaml:config/database.yml
  host: db
  username: postgres
  password: password
```

この設定を行わずに `docker-compose up` を実行しても、ブラウザーでアクセスすると次のようなエラーが表示されます:

```html
PG::ConnectionBad
could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
```

![2020-08-06_13h03_31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267233/0a356d1e-1c8b-9c1b-5488-9041207906ec.png)

このエラーメッセージは、Rails アプリケーションが
ローカルに PostgreSQL が起動している前提で接続しようとして失敗していることを示しています。

この場合、`config/database.yml` を更新してブラウザーを再読み込みするだけではエラーは解消しません。
これは、Rails がデータベース接続設定を読み込むのは Rails Server の起動時であるためです。
データベース接続設定を読み込み直すためには、
ターミナルで `Ctrl + C` を入力してコンテナーを停止し、`docker-compose up` を再度実行します。

#### `db` サービスに Rails アプリケーションが利用するデータベースを新規作成

`docker-compose.yml` があるディレクトリーでもう 1 つターミナルを開き、次のコマンドを実行します:

```console
docker-compose run web rake db:create
```

この設定を行わずに `docker-compose up` を実行しても、ブラウザーでアクセスすると次のようなエラーが表示されます:

```html
ActiveRecord::NoDatabaseError
FATAL: database "myapp_development" does not exist
```

![2020-08-06_13h06_15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267233/5bfc31b2-6ea4-2f26-e8cb-2b18576ed6b1.png)

### [View the Rails welcome page!]

次のような画面が表示されましたでしょうか？:

![2020-08-06_14h41_27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267233/3163e21b-1ebd-2837-123f-942a0f0c41d3.png)

## この後の開発フローについて

### [Stop the application](https://docs.docker.com/compose/rails/#stop-the-application)

この Quickstart では、アプリケーションを停止するときは、[docker-compose down](https://docs.docker.com/compose/reference/down/) の利用が奨められています。
この記事に従って `docker-compose.yml` を修正した場合、
`docker-compose down` を実行すると、データベースの内容にアクセスできなくなります。

データベースの内容を保持したいときは、
`docker-compose up` を行ったターミナルで `Ctrl + C` を入力するか、
別のターミナルで [docker-compose stop](https://docs.docker.com/compose/reference/stop/) を実行すると、次に起動したときもデータベースの内容は保持されます。

基本的には、開発のためのデータを保持する必要がある場合は PC 内に残すのではなく、
[factory_bot](https://github.com/thoughtbot/factory_bot) をはじめとする
`データベースフィクスチャー`を定義するための Gem パッケージを使ってコードに残します。
データの再利用の必要性を感じたら、コードに残す習慣をつけましょう。

参考: [テストフィクスチャとは何？ Weblio辞書](https://www.weblio.jp/content/%E3%83%86%E3%82%B9%E3%83%88%E3%83%95%E3%82%A3%E3%82%AF%E3%82%B9%E3%83%81%E3%83%A3)

### [Rebuild the application](https://docs.docker.com/compose/rails/#stop-the-application)

ここでは、イメージの再ビルドが必要な場合について述べられています。
ここで述べられている「イメージの再ビルドが必要な場合」とは、
「開発者が開発中に再ビルドを行うこと」を指しており、
「本番環境にデプロイするためのイメージをビルドすること」は指していません。

このことを理解するために、まずは「本番環境にデプロイするためのイメージをビルドすること」を考えてみます。

#### 本番環境にデプロイするためのイメージのビルド

手順 1. [Define the project] で `Dockerfile` コメントアウトしていた場合は、`COPY . /myapp` をコメントインします:

```docker:Dockerfile
FROM ruby:2.5
RUN apt-get update -qq && apt-get install -y nodejs
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
```

`COPY . /myapp` は Rails アプリケーションのコードをビルドしたイメージに含めます。
本番環境では GitHub などのバージョン管理リポジトリーからコードをデプロイするのではなく、
Docker Hub などの `Docker レジストリー`から事前にビルドしたイメージをデプロイすることを想定しています。

参考: [Docker Registry | Docker Documentation](https://docs.docker.com/registry/)

##### `Gemfile`, `Gemfile.lock` と一緒に一度に `COPY` してはいけないの？

`Gemfile`, `Gemfile.lock` と `COPY` を分けているのは、`bundle install` に時間がかかるためです。

Docker イメージのビルドは、命令毎に結果がキャッシュされるので、
変更の可能性が低い命令、実行に時間がかかる命令は
なるべく `Dockerfile` の上に記述した方が再ビルド時の効率が良くなります。

`COPY . /myapp` 以降の命令はそれほどビルドに時間がかかりません。
もし、`/myapp` を `Gemfile`, `Gemfile.lock` と一緒に一度に `COPY` していた場合、
`COPY . /myapp` 以降の命令を再ビルドしたいときであっても、
Rails アプリケーションのコードを更新していると `bundle install` が実行されることになり
ビルドに時間がかかってしまいます。

#### 開発者が開発中に再ビルド

Quickstart では、手順 2. [Build the project] で
この Docker を使った Rails 開発のワークフローに関する重要なことが述べられていまました:

> This, and changes to the Gemfile or the Dockerfile, should be the only times you’ll need to rebuild.

これは、日本語訳すると、大体次のような意味です:

このときと、`Gemfile` もしくは `Dockerfile` を変更したときだけが、再ビルドが必要なタイミングとなるはずです。

これは、`docker-compose.yml` の `volume` で `/myapp` をホストから `bind` マウントしているので
開発のためにコンテナーを起動したとき、イメージに含めた Rails アプリケーションのコードではなく
ホスト側の最新の Rails アプリケーションのコードが実行されるためです。

## Q & A

### Docker 公式ドキュメントの Quickstart に関する質問

#### `rails new` コマンドでファイルが生成されたログが出力されるのに、実際にファイルが生成されません

Windows 10 でバージョンが 1709 までだと、
Docker Compose で相対パスを指定したときに想定通りにマウントされない不具合があるようです。
Windows Update の適用をおすすめします。
[Ruby on Rails - dockerでRuby on Railsコンテナ起動時、Could not find public_suffix-4.0.5 in any of the sources エラー｜teratail](https://teratail.com/questions/277106)

### ディレクトリーの名前を `myapp` 以外に変更すると `rails new` コマンド実行時にエラーが発生します

正確にすべてのディレクトリー名を変更すればエラーは発生しません。
どこかで書き換えを誤っていないか確認しましょう。
[Ruby on Rails - ローカルディレクトリにrails newをしてもファイルが存在しない｜teratail](https://teratail.com/questions/274942)

### Docker と Rails による Web サービス実行に関する質問

#### エラーメッセージの意味が何を指しているのかわかりません

複数のサービスを起動するときはコンテキストとなるディレクトリーを分けて設計していますか？
コンテキストとなるディレクトリーを分けると、原因の範囲が狭まり調査しやすくなります。
[Ruby on Rails - Docker + Railsの No such file or directory エラーの解決方法について｜teratail](https://teratail.com/questions/278349)

#### パッケージが足りないエラーが表示されるので gem install しても直りません

Docker が動いているコンピューターに gem をインストールしていませんか？
`gem` のインストールはコンテナ内で行う必要があります。
[Ruby on Rails - docker-compose upがうまくいかない｜teratail](https://teratail.com/questions/276807)

#### db サービス以外のデータベースに接続したいです

Rails の `database.yml` と環境変数でデータベース接続情報を変更して起動します。
[MySQL - [Docker, Rails, Nginx, MySQL, EC2]docker-compose run app rails db:createするとエラーが出る。｜teratail](https://teratail.com/questions/276340)

#### multi-stage builds にしたら bundle: not found でイメージがビルドできなくなりました

ベースイメージが Ruby のイメージではないステージで `ruby` や `bundler` を使おうとしていませんか？
[Ruby on Rails - "docker-compose build"実行時のエラー "Service 'web' failed to build: The command '/bin/sh -c bundle instal"｜teratail](https://teratail.com/questions/276281)

#### Gemfile はあるのに bundle install すると Could not locate Gemfile というエラーメッセージが表示されます

コマンドは Docker が動いているコンピューターで実行しているか、
それともコンテナの中で実行しているのかを確認し、
そこに `Gemfile` があるかを確認しましょう
[GitHub - Dockerを用いたローカル環境構築で、Could not locate Gemfileが表示される｜teratail](https://teratail.com/questions/262816)

#### Dockerで起動したサーバーにlocalhost:3000でアクセスすると「このページは動作していません。」と表示されます

Docker で起動した web サービスのログを確認して、ログが増えるかどうかを確認します。
増えなければ、ファイアウォールの問題などの可能性があります。
[MySQL - dockerで起動したサーバーにlocalhost:3000でアクセスしようとするが「このページは動作していません。」と返ってくる｜teratail](https://teratail.com/questions/275743)

[Define the project]: https://docs.docker.com/compose/rails/#define-the-project
[Build the project]: https://docs.docker.com/compose/rails/#build-the-project
[Connect the database]: https://docs.docker.com/compose/rails/#connect-the-database
[View the Rails welcome page!]: https://docs.docker.com/compose/rails/#view-the-rails-welcome-page