Docker Composeを使用したRailsプロジェクトのDocker化

本記事では、既存のRailsプロジェクトをDocker Composeを使用してコンテナ化する手順についてまとめています。

### Docker Composeとは？
複数のDockerコンテナを定義し、それらを簡単に起動、停止、接続するためのツールです。
Railsプロジェクトのような複数のコンポーネントで構成されるアプリケーションの開発やテスト、デプロイにおいて、Docker Composeは非常に便利です。
各コンポーネントを個別のコンテナとして扱うことで、開発環境の構築、デバッグ、テスト、本番環境へのデプロイをスムーズに行えます。

### 前提条件
1. DockerとDocker Composeがインストール済みであること
2. Railsプロジェクトが用意してあること(GitHub)

### RailsプロジェクトのDocker化の流れ
1. プロジェクトのクローン
2. docker化に必要なファイルを作成
3. コンテナのビルドと起動


### 手順

#### プロジェクトのクローン
GitHubからプロジェクトをクローンして、作業ディレクトリに移動します。
```
git clone <GitHub リポジトリ URL>
cd <プロジェクトディレクトリ>
```

#### Dockerファイルの作成
RailsアプリケーションをDockerで動かすためのDockerfileをプロジェクトディレクトリ(ルートDir)に作成します。
必要なパッケージのインストールや設定を行います。
```:Dockerfile
FROM ruby:3.2.2
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs

WORKDIR /rails-docker
COPY Gemfile Gemfile.lock /rails-docker/
RUN bundle install

ADD . /rails-docker
```
※ディレクトリ名は任意(今回は/rails-dockerとしています)

#### Docker Composeファイルの作成
``` docker-compose.yml
version: '3'
services:
  db:
    image: postgres:12
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres

  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/rails-docker
    environment:
      - DATABASE_PASSWORD=postgres
    tty: true
    stdin_open: true
    ports:
      - "3000:3000"
    depends_on:
      - db
    links:
      - db

volumes:
  db-data:

```

#### database.ymlファイルの編集
```config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  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 } %>

development:
  <<: *default
  database: myapp_development
```

#### Dockerコンテナのビルドと起動
Docker Composeを使用して、Railsアプリケーションとデータベースを含む全てのサービスをビルドし、起動します。
```
docker-compose build
docker-compose up -d
```
※近年、コマンドはdocker compose upのように-(ハイフン)はつけないで実行することが多いようですが今回参考にした教材には-がついていたためそちらに合わせます。
※もしimageがない場合(初回の実行など)はdocker-compose upだけ実行すればbuildも一緒に行ってくれる。

#### PostgreSQLデータベースのセットアップ
前手順でPostgreSQLデータベースとの接続を確立できたので、必要なデータベースの初期化とマイグレーショをします。
```
docker-compose exec web bash
rails db:create
rails db:migrate
exit
```

#### Railsアプリを起動
ローカルホスト上でRailsアプリを起動します。
```
ブラウザで
http://localhost:3000
へ接続
```

### 作成したファイルの説明
```:Dockerfile
# クローンしたのrailsアプリのGemfileでバージョンを確認する。今回は3.2.2を指定。
FROM ruby:3.2.2

# apt-getパッケージマネージャを使用して、ビルドに必要な依存関係（build-essential、libpq-dev、nodejs）をインストール
# -qq:エラーメッセージ以外の全ての出力を抑制(quiet)する
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs

#　作業ディレクトリを/rails-dockerに変更.
WORKDIR /rails-docker

# GemfileとGemfile.lockファイルをDockerイメージの/rails-docker/ディレクトリにコピー.
# これでbundle install時に、ローカルのGemfile定義が使用される
COPY Gemfile Gemfile.lock /rails-docker/

# Gemfileに記述された依存関係をインストール
RUN bundle install

# ローカルのプロジェクトファイルをDockerイメージの/rails-dockerディレクトリにコピー
ADD . /rails-docker
```
``` docker-compose.yml
version: '3'
services:
  db:
    image: postgres:12
    # データ永続化
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
        # userは記載しない場合はpostgresがデフォルト.今回は記載
      - POSTGRES_USER=postgres
        # 必須項目のため必ず記載
      - POSTGRES_PASSWORD=postgres

  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/rails-docker
    # コンテナ内での環境変数を定義する
    environment:
      - DATABASE_PASSWORD=postgres
    tty: true
    stdin_open: true
    ports:
      - "3000:3000"
    # サービス間の起動順番と終了順番の依存関係を表す
    # webの前にdbが作成
    depends_on:
      - db
    # サービス内のコンテナが別のサービス内のコンテナに簡単にアクセスできるようになる
    links:
      - db

volumes:
  db-data:

```
```config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  # データベースサーバーのホスト名を指定.
  # Docker Composeを使用している場合、dbはデータベースコンテナのサービス名
  host: db
  # データベースに接続する際のユーザー名を指定.postgresはデフォルト
  username: postgres
  # データベースに接続する際のパスワードを環境変数から取得
  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 } %>

```