【備忘録】Docker + docker composeでRailsアプリをDocker化する手順メモ

## はじめに

Railsで開発したアプリをDockerで動かしたいとき、Dockerとdocker composeを使うことで環境構築をコードとして管理できるようになります。

Docker化の手順を順に追って、つまずいたポイントと合わせてまとめました。

---

## 開発環境
- Ruby 3.2.2
- Rails 7.x
- PostgreSQL 12
- Docker / docker compose v2（スペース形式）


## 1. GitHub上のリポジトリをローカル環境へクローン
今回はDocker化がメインのテーマなので既存のRailsアプリを使用します。
Github上で使いたいプロジェクトのリポジトリページを開き、` Use this template` → `Create a new repository `
を選択し、`Repository name*`にリポジトリ名を入力して`Create repository`します。
ターミナルを開いて下記のコマンドを打って、Githubからローカルへプロジェクトを引っ張ってきて、フォルダの中に移動します。
今回はリポジトリ名を` rails-docker ` としています。
```
git clone https://github.com/YOUR-USERNAME/rails-docker
cd rails-docker
```

今回はこのクローンした` rails-docker `ディレクトリの中で、Docker化の作業を進めていきます。
できれば` git checkout -b docker `で`docker`ブランチに作業ブランチを変更しておきましょう。（※そのまま `main` で作業しても問題ありません）

---

## 2. Dockerfileの作成

Dockerfileとは、Dockerコンテナを作るための設計図です。
プログラムやサーバー環境（RubyやRailsなど）を「この順番で入れて、このコマンドを実行して、、」という手順をテキストで記述します。
ターミナルでひとつひとつコマンドを打つより時間もかからず、再現性が高いです。

ではやっていきます。
ターミナル上で`rails-docker`内にいることを確認して、` code Dockerfile ` と打ちエディタで Dockerfileを開きます。（VSCode使用の場合。）
エディタで下記のように入力します。

```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 ./
RUN bundle install
COPY . .
CMD ["bin/rails", "server", "-b", "0.0.0.0"]
```

- `FROM`... Rubyのバージョンを指定します。Gemfile（後述）の中で指定するRailsと互換性のあるバージョンを指定します。
- `RUN`... `apt-get update`でパッケージリストを更新し、`apt-get install`でRailsアプリの実行に必要なライブラリをインストールします。`-qq`は出力されるログを最小限に、 `-y`は全部Yesで進めるオプションです。
- `WORKDIR` ... コンテナ内の作業ディレクトリ。色々な人がこのDockerファイルを使用して環境構築することが想定されるので、絶対パスで指定します。
- `COPY` ... 現在のディレクトリ内容をコンテナへコピー。最初にGemfileとGemfile.lockのみコピーすることでキャッシュを活用できるようにします。
- `CMD` ... コンテナ起動時のデフォルト実行コマンド。後でdocker-compose.ymlで上書きされますが、記述しておいた方がDockerfile単独でも起動できるので安全です。

---

## 3. docker-compose.ymlの作成
docker-compose.ymlは、複数のDockerコンテナ（Rails + DBなど）を一括で管理して起動するための設定ファイルです。
たとえば、RailsアプリとPostgreSQLを一緒に動かす場合に、どのイメージを使って、どのポートで公開して、どんな環境変数を渡すか、などを定義できます。

`rails-docker`直下に `docker-compose.yml`ファイルを作成し、下記のように記述します。

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

  db:
    image: postgres:12
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=myapp_development
    ports:
      - "5432:5432"
```

- `services:` ...  起動する各コンテナを定義します。ここでは `web`（Rails）と `db`（PostgreSQL）の2つで構成されています。

- `web.build`  ... `Dockerfile` があるディレクトリからイメージをビルドします。`.` は「現在のディレクトリ」を意味します。

- `web.command` ...  コンテナ起動時に実行されるコマンドを指定します。ここでは Rails を3000番ポート（Railsのデフォルトポート）で起動します。

- `web.volumes` ...  ローカルのソースコードをコンテナ内にマウントします。ローカル上に置いてあるコードの開発中の変更をコンテナに即時反映できます。

- `web.environment` ...  環境変数の設定を行います。Railsの `database.yml` などで読み込まれます。
-  `web.tty`  ...`rails console` などを対話的に使いたいときに必要です。

- `web.stdin_open`  ...標準入力（STDIN）を開いたままにします。ユーザー入力を受け付けることができるようになります。

- `web.ports` ...  `ホスト`:`コンテナ`の順で指定して、ホストのポートとコンテナのポートをつなげます。この場合`ローカルの http://localhost:3000 にアクセスすると、Railsアプリ（コンテナ内の3000番）できるようになります。

- `web.depends_on` ...  `db` コンテナが先に起動されるように順序を制御します。

- `db.image`  ... PostgreSQLの公式イメージを使用します。`postgres:12`のようにバージョンの指定もできます。

- `db.environment`...   ユーザー、パスワード、DB名などのデータベースの初期設定ができます。これらはRailsの`config/database.yml`の記述と揃える必要があります。

- `db.ports` ...  DBに外部からアクセスできるようにするためのポート設定です。

---
## 4. database.ymlの作成

`database.yml` は、Railsアプリとデータベースをつなぐための設定ファイルです。  
`rails db:create` や `rails db:migrate` はこのファイルの内容に従って処理されます。

`rails-docker/config`直下に`database.yml` ファイルを作成し、下記のように記述します。

```yaml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: postgres
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test
  username: myapp
  password: <%= ENV["MYAPP_DATABASE_PASSWORD"] %>
```

- `adapter:`  ... 使用するデータベースの種類です。

- `encoding:`  ... データベースの文字コードを設定します。通常は `unicode` を指定します。

- `host:`  ... データベースサーバーのホスト名です。`docker-compose.yml` で定義した DB サービス名（ここでは `db`）と一致させる必要があります。これが異なると、RailsからDBに接続できずエラーになります。

- `username:`  ... 接続時に使用するユーザー名です。

- `password:` ...  上記ユーザーに対応するパスワードです。セキュリティを考慮して別途 `.env`ファイルを作って管理することもあります。今回は直接記載しています。

- `pool:`...   データベースへの同時接続数の上限です。スレッド数に応じて調整します。

- `database:` ...  作成されるデータベース名です。開発環境・テスト環境・本番環境でそれぞれ異なる名前を指定します。

---

## 5. Gemfileの作成とGemfile.lock

`Gemfile`は、Railsアプリで使用するRubyのライブラリ（gem）の依存関係をまとめて管理するためのファイルです。
このファイルを使うことで、アプリケーションに必要な機能をgemという形で追加し、それらをチーム全体で共有できるようになります。
`Gemfile`を変更するとDockerのビルドキャッシュが無効になるため、`docker compose up --build`を実行し直す必要があります。

`Gemfile.lock` は、`Gemfile` に書かれた依存関係をもとに 実際にインストールされたgemのバージョンを記録するファイルです。
チーム開発で gem のバージョンを揃えるために重要で、`bundle install` を実行することで自動生成・更新されます。

`rails-docker`直下に `Gemfile`ファイルを作成し、下記のように記述します。


```ruby
source "https://rubygems.org"

ruby "3.2.2"

gem "rails", "~> 7.x"
gem "pg", "~> 1.1"
# 他にも使用したいgemをここに追加していきます
```

- `source:`  ...使用するGemの取得元（リポジトリ）を指定します。

- `ruby:`  ...使用するRubyのバージョンを指定します。Dockerfileにも同じバージョンを指定しておくと整合性が取れます。

- `gem:`...  利用するライブラリ（gem）の名前とバージョンを指定します。`~>` を使うことで柔軟なバージョン指定ができます。




---

## 6. .dockerignoreファイルの作成

`.dockerignore`に記載することで、Dockerイメージを作成する際に含めたくないファイルを除外します。現在作成していないファイルやフォルダでも、とりあえず含めておくと後から作った時に安心です。

`rails-docker`直下に`.gitignore`ファイルを作成し、下記のように記述します。

```text
log/*
tmp/*
*.log
*.swp
.DS_Store
node_modules
```


## 7. コンテナの起動とDB設定

必要なファイルが作成できたのでターミナルに戻り、下記コマンドを打ってコンテナを起動します。
`--build`はDockerイメージを再ビルドしてからコンテナを起動するオプションです。DockerfileやGemfile に変更を加えたあとなど、ビルド内容を更新したいときに使用します。


```bash
docker compose up --build
```


その後、下記コマンドでデータベースの作成とマイグレーションをします。
```bash
docker compose exec web rails db:create
docker compose exec web rails db:migrate
```
`docker compose exec` は、すでに起動しているコンテナの中でコマンドを実行するためのコマンドです。
このとき、どのサービス（コンテナ）に入るかを指定するために、web を付けています。

` rails db:create `は`database.yml` に基づいて、指定されたデータベースを作成します。
` rails db:migrate `はdb/migrate/にあるマイグレーションファイル（設計図）に書かれた内容にしたがって、データベースの構造（テーブルやカラム）を作成・更新します。
「どんなカラムが必要か」「どんな制約があるか」など、アプリで使うデータの形がここで決まります


これでhttp://localhost:3000 にアクセスすると、Railsアプリの起動が確認できるでしょう。
もしhttp*s*://localhost:3000 にリダイレクトされてしまって表示がうまくいかない場合は、ブラウザのキャッシュや履歴を削除してから再読み込みしてみてください。

---

## 8. GitHubにpush

http://localhost:3000 に接続してRailsアプリの起動が確認できたら、GitHub上にpushしてローカルとリモートの状態を同期させておきます。
作業ブランチを分けていない人は`git push origin main` または `git push origin master`でpushします。

``` 
git add .
git commit -m "コミットメッセージ"
git push origin docker
```


また、GitHub ActionsなどのCI環境では `docker-compose` が使えないので `docker compose` の方を使います。テストが通らない時は確認してみてください。

ちなみに、大文字と小文字を間違えて`Docker-compose.yml`などとタイポするとローカルでは動くがGiuhub上のテストが通らないなんてことが起こるので注意してください。
ファイル名の変更をしてもローカル上で同じと認識されて普通にはcommitが通らないので少しめんどくさいことになります。

---

## 参考にしたページ

公式ドキュメント（日本語訳）を参考にしました。

https://docs.docker.jp/compose/rails.html

ぜひハンズオンで学んでみてください。
## おわりに


Docker化につまずいた人の参考になれば幸いです！