[Docker]docker-composeでrailsアプリをコンテナ化する

## はじめに
既存のrailsアプリをdocker,docker-composeを使用してコンテナ化する方法について解説します。
## 前提条件
- ローカル環境
```
pc: m1 mac
os: Venture 13.3.1
```
- 各種version
```
Docker: 24.0.7
Docker Compose: v2.23.3
Ruby: 3.22
Rails: 7.0.6
PostgreSQL: 12.17
```
- 必要知識
  - 基本的なDockerの知識
  - railsのコマンド

## 手順
### プロジェクトをローカルにコピーする
※既にローカルにプロジェクトがある場合は[Dockerfileを用意する](Dockerfileを用意する)までスキップして下さい。
<br>

まずはコンテナ化したいプロジェクトをGitHubからローカルにクローン(コピー)しましょう。
```bash
$ git clone <リポジトリパス>
```
以降の作業はコピーしたディレクトリで行うため`cd`コマンドで移動します。
```zsh
$ cd <ローカルパス>
```
### Dockerfileを用意する
railsのDockerfileを作成していきます。  
今回はrubyのイメージからrailsの環境構築を行います。
```Dockerfile:Dockerfile
FROM ruby:3.2.2
RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    nodejs \
    postgresql-client \
    yarn
WORKDIR /app
COPY Gemfile Gemfile.lock /app
RUN bundle install
```
`FROM` :プロジェクトで使用しているrubyのバージョンを明示的に指定します。
```Dockerfile
FROM ruby:3.2.2
```
`RUN` :コンテナにインストールするパッケージを指定します。
対象のプロジェクトによって内容は異なります。
```Dockerfile
RUN apt-get update && apt-get install -y \
    packages
```
`WORKDIR` :コンテナ内のカレントディレクトリを変更します。
存在しないディレクトリを指定すると自動で作成されます。
```Dockerfile
WORKDIR /app
```
`COPY`: ファイルをコンテナ内にコピーします。
```Dockerfile
COPY Gemfile Gemfile.lock /app
```
`RUN` :コピーしたGemfileを元にgemをインストールします。
```Dockerfile
RUN bundle install
```
### docker-compose.ymlを用意する
Dockerfileが用意できたらdocker-compose.ymlを作成していきます。
```yml:docker-compose.yml
# composeファイル形式のversion
version: '3'

# db-dataというデータvolumeを作成
volumes:
  db-data:

# この下にservoceを定義
services:
  # web service(アプリケーション)を定義
  web: 
    # docker buildと同じ。build-contextを指定
    build: .
    # コンテナ起動後のコマンドを指定
    command: bash -c "rails db:migrate && rails s -b 0.0.0.0"
    # docker run -pと同じ。port番号を指定
    ports:
      - '3000:3000'
    # docker run -vと同じ
    volumes:
      - '.:/app'
    # postgresのpasswordを環境変数で定義
    environment:
      - 'POSTGRES_PASSWORD=postgres'
    # docker run -itと同じ
    tty: true
    stdin_open: true
    # db serviceが起動してから起動する
    depends_on:
      - db
  
  # db serviceを定義
  db:
    # Dockerレジストリのpostgres imageでbuildする
    image: postgres:12
    # 上で宣言したデータvolumeにpostgresのdataをマウント
    volumes:
      - 'db-data:/var/lib/postgresql/data'
    # postgresのpasswordを環境変数で定義
    environment:
      - 'POSTGRES_PASSWORD=postgres'
```
### database.ymlを編集する
railsとpostgresを接続するためrailsアプリの`config/database.yml`を編集します。
```yml:database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  port: 5432
  password: <%= ENV.fetch("POSTGRES_PASSWORD") %>
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
```
databaseのアクセス先を指定します。
今回はdbでpostgresのserviceを定義しているためdbを指定します。
```Dockerfile
host: db
```
postgresにアクセスするusernameを指定します。postgresはデフォルトです。
```Dockerfile
username: postgres
```
postgresのデフォルトのポート番号です。
```Dockerfile
port: 5432
```
user:postgresのパスワードを指定します。
今回はdocker-composeで定義した環境変数を使用しています。
```Dockerfile
password: <%= ENV.fetch("POSTGRES_PASSWORD") %>
```

### コンテナを起動する
docker-composeを実行する事で、railsアプリとpostgresのコンテナを起動することが出来ます。初回起動時はdatabaseを作成する必要があるので、以下のコマンドを実行します。
```bash
$ docker-compose run --rm web rails db:create
```
以降、docker-composeを実行するだけで、railsアプリケーション(localhost:3000)にアクセスする事ができます。
```bash
$ docker-compose up -d
```
:::note warn
runと似たコマンドにexecがありますが、execは既に作成されたコンテナに行うコマンドに対し、runはコンテナが作成されていない場合に使用します。
:::
## おわりに
お疲れ様でした。今回のケースと全く同じという事は無いかもしれませんが、少しでもお役に立てれば幸いです。