Railsプロジェクトを新規作成し、Docker化する

## はじめに

最近、Dockerについて学習したので、実際にRailsの新規プロジェクトを作成し、Docker化する手順について書いていきます。

なお、データベースにはPostgreSQLを、オーケストレーションツールにはdocker-composeを利用します。Docker Containerは1コンテナ1アプリケーションの構成とします。

補足となりますが、私は以下の学習教材を利用してDockerの理解を深めました。もしDockerについて理解を深めたい方はご参考にしていただけるとよいかもしれません。

- [米国AI開発者がゼロから教えるDocker講座 | Udemy](https://www.udemy.com/course/aidocker/)

## 作業手順
1. 必要なファイルの作成
    - Dockerfile
    - docker-compose.yml
    - Gemfile
    - Gemfile.lock
2. Docker Containerの起動とRailsプロジェクトの新規作成
4. database.ymlの編集
5. データベースの接続

## 必要なファイルを作成する

任意の名前のディレクトリ配下に必要なファイルを新規作成し、中身を記述していきます。

※ 任意の名前のディレクトリが、```build context```となるイメージです。

### Dockerfile

Railsで開発するためのDockerfileを作成します。Docker Hub上にあるRubyのDocker Official Imageを使用しています。

なお、Railsバージョンに合うRubyバージョンを使用しないとエラーとなる可能性があるため、ご注意ください。

``` Dockerfile
FROM ruby:3.2.2
RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    nodejs \
    postgresql-client \
    yarn
WORKDIR /myapp
COPY Gemfile Gemfile.lock /myapp/
RUN bundle install
```
### docker-compose.yml

以下２点の懸念から、オーケストレーションツール（docker-compose）を使用しています。

- ```docker run```コマンドの記述が長くなるため
- 複数のDocker Containerを一括で起動したいため

以下の内容をファイルに記述します。なお、Rubyのデフォルトポートは3000番です。

``` yaml
version: '3'

volumes:
  db-data:

services:
  web:
    build: .
    ports:
      - '3000:3000'
    volumes:
      - '.:/myapp'
    environment:
      - 'DATABASE_PASSWORD=postgres'
    tty: true
    stdin_open: true
    command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"

```

念のため、上記ファイル内のパラメータごとの説明を表形式で書いておきます。

|サービス名  |キー名  |内容  |
|:-----------|:-----------|:-----------|
|web  |build  |Dockerfileをビルドするディレクトリ  |
|web  |ports  |ポストフォーワードするHost側、Docker Container側のポート番号  |
|web  |volumes  |マウントするファイルシステムのHost側、Docker Container側のパス  |
|web  |environment  |定義する環境変数名とその値  |
|web  |tty  |出力結果をprettyにするかどうか  |
|web  |stdin_open  |STDIN（入力チャネル）を開くかどうか  |
|web  |command  |Docker Container起動時に実行するコマンド  |

- ports
    - Host側の3000番ポートをDocker Container側の3000番ポートにポストフォーワードしている
- volumes
    - Host側のパスは、他の人の環境でもスムーズにDocker Containerを起動できるよう相対パスで記述するとよい


### Dockerfileとdocker-compose.ymlの違い

- Dockerfile
    - Dokcer Containerの中身を定義するもの
- docker-compose.yml
    - Docker Containerをどのように起動するか定義するもの

### Gemfile

RailsでのWebアプリ開発に必要なgemのリストを記載するファイルです。今回は、Railsをインストールする必要があるため、以下の通りに記述します。

``` plaintext
source 'https://rubygems.org'
gem 'rails', '~> 7.0.6'
```

```bundle install```を実行すると、BundlerがGemfileに記載されたgemをインストールします。Dockerfile内の```COPY```、```RUN```部分とGemfileとの関係ですが、以下の通りとなります。

- ```COPY```
    - HostにあるGemfileと後述のGemfile.lockをDocker Container内にコピーする
- ```RUN```
    - Docker Container内のGemfileを使用して、```bundle install```を実行し、必要なgemをインストールする

### Gemfile.lock

ファイルの作成のみです。前述の```bundle install```コマンドを実行すると、インストールされたgemの一覧が本ファイルに自動的に記述されます。

## Docker Containerの起動とRailsプロジェクトの新規作成

以下のコマンドをCLI上で実行し、Docker Containerを作成、起動します。

``` plaintext
$ docker-compose run --build -d
```

先ほど作成、起動したDocker Containerの中に入るため、以下のコードを実行します。

```plaintext
$ docker-compose exec web bash
```

Dockerコンテナ内で、Railsの新規プロジェクトを作成します。

また、Dockerfile上ですでに`bundle install`を記述しており、```rails.new```で
Gemfileが更新された後にコンテナから抜けて再度Docker Imageをビルドし直すため
`bundle install`はスキップします。 

```plaintext
$ rails new . --force --database=postgresql --skip-bundle
$ exit
$ docker-compose down //すべてのContainerとImageを削除する
$ docker-compose up --build -d
```

ただ、現時点で```localhost:3000```にアクセスしてもデータベースが作成されていない、接続設定がされていないため、データベース関連のエラーが出てしまいます。

## database.ymlの編集

Railsの新規プロジェクトを作成すると、configフォルダ配下にdatabase.ymlが自動で作成されます。database.yml内にデータベースの接続のための設定を追記します。

コメントアウトで「追加行」と記載された部分を追記します。
すべてPostgresSQLのデフォルトのユーザー、パスワード、ポートを使用しています。

また、パスワードの値には、環境変数「```DATABASE_PASSWORD```」を記述し
docker-compose.yml内で記述された同名の環境変数とリンクさせます。

``` yaml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db #追加行
  user: postgres #追加行
  port: 5432 #追加行
  password: <%= ENV.fetch("DATABASE_PASSWORD") %> #追加行
  # For details on connection pooling, see Rails configuration guide
  # https://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
```

### docker-compose.ymlの編集

docker-compose.ymlにおいてもコメントアウト「以下追加」より下の行を追記し、PostgresSQL関連の設定を行います。

``` yaml
version: '3'

volumes:
  db-data:

services:
  web:
    build: .
    ports:
      - '3000:3000'
    volumes:
      - '.:/myapp'
    environment:
      - 'DATABASE_PASSWORD=postgres'
    tty: true
    stdin_open: true
#以下追加
    depends_on:
      - db
    links:
      - db
    command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"

  db:
    image: postgres:12
    volumes: 
      - 'db-data:/var/lib/postgresql/data'
    environment:
      - 'POSTGRES_USER=postgres'
      - 'POSTGRES_PASSWORD=postgres'
```

追記したパラメータの説明を表形式で書いておきます。

|サービス名  |キー名  |内容  |
|:-----------|:-----------|:-----------|
|web  |depends_on  |事前に起動するサービス名（Docker Container名）  |
|web  |links  |web（サービス名 / Docker Container名）からアクセスするサービス名  |
|db  |image  |使用するDocker Image  |
|db  |volumes  |データベースのデータをマウントするHost側のvolume  |


## データベースの接続

前回同様に、Docker Containerを以下のコマンドで起動し、Containerの中に入ります。

```plaintext
$ docker-compose up --build -d
$ docker-compose exec web bash
```

最後に、データベースを作成し、Railsのサーバーを立ち上げて完了です。

```plaintext
$ rails db:create
$ rails s -b 0.0.0.0
```

```localhost:3000```にアクセスしてみると、Railsの画面が表示されているはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2290540/02e3dee1-3c6f-dd43-689e-87acbeafe4aa.png)

## おわりに

個人的に、Dockerを理解するにはかなりの時間がかかりました。ただ、諦めずに何度もUdemyの講座を見返すことで徐々に理解できたのでよかったです。

どなたかの参考になれば、幸いです。