既存RailsアプリをDocker化する手順

# はじめに
### 環境構築目標
今回構築するDocker環境の目標は以下の通りです。
- railsとnodeをマルチステージビルドでインストールする
- docker-composeを使ってrailsとPostgreSQLを起動する
- http://localhost:3000 にアクセスするとアプリが表示される


### 各種バージョン
- ruby: 3.0.2
- rails: 6.0.3
- node: 16.13
- postgresql: 12.0

### 開発環境
- CPU: Apple M1
- OS: MacOS Ventura バージョン13.3.1

Window等のOSや他CPUでの動作確認は行なっていませんので、予めご了承ください。

※追記
環境構築にあたり、`yarn install`で発生したエラーについて
下記記事にてまとめておりますので、よろしければご参照ください。
(本記事では下記エラーが発生しないよう対応済みですのでご安心ください！)

https://qiita.com/snow_swallow/items/7ce97587dc873b3f1297

# Docker化手順
## 概要
大まかな手順は以下の通りです。
1. Dockerfileを作成
1. docker-compose.ymlを作成
1. Gemfileを編集
1. database.ymlを編集
1. Docker Composeを使用してコンテナを作成・実行
1. localhost:3000にアクセス

## 詳細
### 1. Dockerfileを作成
Dockerfileを用いてイメージを作成します。
Dockerfileには以下の内容を記述します。

```Dockerfile:Dockerfile
# ベースとなるイメージを指定する
FROM node:16.13 as node
FROM ruby:3.0.2

#　nodeイメージからyarn,nodeをコピーする
COPY --from=node /opt/yarn-* /opt/yarn
COPY --from=node /usr/local/bin/node /usr/local/bin/
COPY --from=node /usr/local/lib/node_modules/ /usr/local/lib/node_modules/

RUN ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
    && ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npx \
    && ln -fs /usr/local/lib/node /usr/local/bin/nodejs \
    && ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarn \
    && ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarnpkg

# aptを更新し、postgresql等の必要なパッケージをインストールする
RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    postgresql-client

# myappというフォルダを作成し、myappをワークディレクトリとする
RUN mkdir /myapp
WORKDIR /myapp

# yarnを更新する
COPY package.json /myapp/package.json
COPY yarn.lock  /myapp/yarn.lock
RUN yarn install

# Gemをインストールする
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install

# カレントディレクトリをmyappにコピーする
COPY . /myapp

```

#### ベースとなるイメージを指定する
```Dockerfile
FROM node:16.13 as node
FROM ruby:3.0.2
```
rubyとnodeのイメージを使用します。
`as`を使用することでイメージ名を指定することが可能です。

#### nodeイメージからyarn,nodeをコピーする

`COPY`命令でyarnとnodeをコピーします。
```Dockerfile
COPY --from=node /opt/yarn-* /opt/yarn
COPY --from=node /usr/local/bin/node /usr/local/bin/
COPY --from=node /usr/local/lib/node_modules/ /usr/local/lib/node_modules/
```

`ln`コマンドを用いて、シンボリックリンクを生成します。
オプションに`-f`を指定することで同じファイルがあった場合に上書きを、
`-s`でシンボリックリンクを生成する命令となります。

```Dockerfile
RUN ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
    && ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npx \
    && ln -fs /usr/local/lib/node /usr/local/bin/nodejs \
    && ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarn \
    && ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarnpkg
```


#### myappというフォルダを作成し、myappをワークディレクトリとする
今回の例では、ルートディレクトリに`myapp`というフォルダを作成し、
`myapp`フォルダをワークディレクトリとして設定しています。

```Dockerfile
RUN mkdir /myapp
WORKDIR /myapp
```

#### aptを更新し、postgresql等の必要なパッケージをインストールする
`apt-get update`でaptを更新し、`apt-get install`で必要なパッケージをインストールします。
`-y`オプションを指定することで、インストール時に質問される内容をyesとして、
スキップすることができます。
今回はデータベースにPostgreSQLを使用します。
 
```Dockerfile
RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    postgresql-client
```

#### yarnを更新する
`package.json`と`yarn.lock`をmyappディレクトリにコピーし、
`yarn install`を実行することで、`package.json`の内容をもとにパッケージがインストールされます。

```Dockerfile
COPY package.json /myapp/package.json
COPY yarn.lock  /myapp/yarn.lock
RUN yarn install
```

#### Gemをインストールする
`Gemfile`と`Gemfile.lock`をmyappディレクトリにコピーし、
`bundle install`を実行することで、`Gemfile`の内容をもとにパッケージがインストールされます。

```Dockerfile
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
```

#### カレントディレクトリをmyappにコピーする
カレントディレクトリのファイル・フォルダをmyappにコピーします。

```Dockerfile
COPY . /myapp
```

### 2. docker-compose.ymlを作成
Docker Composeを使用するため、`docker-compose.yml`を作成します。
Docker Composeを使用することで、複数のコンテナの管理を効率的に行うことができます。
`Dockerfile`と同じディレクトリ内に`docker-compose.yml`を作成し、
以下の内容を記述します。


```yml:docker-compose.yml 
# Docker Composeのバージョンを指定する
version: '3'

# serviece以下に各コンテナの設定を記述する
services:

  # webコンテナの設定
  web:
    # カレントディレクトリのDockerfileでビルドする   
    build: .
    # railsサーバーの起動時の設定を行う
    command: bundle exec rails s -p 3000 -b '0.0.0.0' &&
    # ポートの設定を行う(ホストの3000ポートとコンテナの3000ポートを紐付け)
    ports: 
      - '3000:3000'
    # マウントの設定を行う(カレントディレクトリのファイル・フォルダをmyappにマウントする)
    volumes:
      - .:/myapp
    # 環境変数の設定を行う
    environment:
      - 'DATABASE_PASSWORD=postgres'
    # 擬似端末(キーボードによる入力)をコンテナに結びつける
    tty: true
    # 標準入出力とエラー出力をコンテナと結びつける
    stdin_open: true
    # コンテナの依存関係を指定する
    depends_on:
      - 'db'

  # データベースコンテナの設定
  db:
    # postgresの12.0を指定する
    image: postgres:12.0
    # 環境変数の設定を行う
    environment:
      - POSTGRES_PASSWORD=password
```

### 3. Gemfileを編集
今回データベースにはPostgreSQLを使用するため、Gemfileに以下の内容を追記する。
```
gem 'pg'
```

### 4. database.ymlを編集
`config`フォルダ内にrailsのデータベース設定を行う`database.yml`ファイルが存在します。
デフォルトでは`SQLite`の設定となっているため、`PostgreSQL`の設定に変更します。
以下の内容を`database.yml`に記述します。

```yml:config/database.yml
default: &default
  adapter: postgresql
  encodeing: unicode
  host: db
  user: postgres
  port: 5432
  password: password
  pool: 5

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test

production:
  <<: *default
  database: myapp_prodection
```


### 5. Docker Composeを使用してコンテナを作成・実行
ここからは、Docker Composeを使用してコンテナの作成・実行していきます。

#### イメージをビルドする
ターミナルを開いて以下のコマンドを実行し、イメージをビルドします。
```bash
$ docker-compose build 
```

#### データベースの作成を行う
続いて、railsアプリのデータベースの作成を行います。
以下のコマンドを実行します。
```bash
$ docker-compose run web rails db:create db:migrate
```
web以下の`rails db:create db:migrate`で、データベースの作成と反映を行っています。

#### コンテナの作成・起動を行う
最後に、コンテナの作成・起動を行います。
以下のコマンドを実行します。
```bash
$ docker-compose up
```

### 6. localhost:3000にアクセス
[localhost:3000](http://localhost:3000)にアクセスできれば成功です。


# 参考
- [Ruby x Node.js x yarnを利用した静的サイト生成用のDocker imageをできるだけ素朴に構築する](https://aligach.net/diary/2022/0503/how-to-build-multilang-runtime-docker-image/)
- [[軽量化] マルチステージビルドによるrails環境のdocker化](https://qiita.com/NaokiKotani/items/1ebe71cc594b1c3586c9)
- [M1MacのDockerでyarn installがエラーになる件について](https://qiita.com/snow_swallow/items/7ce97587dc873b3f1297)