Next.js + Railsの開発環境をdocker-composeで構築してみる

今更ながらフロントエンドをNext.js、バックエンドとしてRuby on Railsを使用し、WEBアプリを作る際の初期開発環境構築をしてみたので、コンテナ立ち上げまでの手順を備忘として残します。
docker、node.jsのドキュメントを参考にしています。

node.js
https://nodejs.org/ja/docs/guides/nodejs-docker-webapp

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

# 前提環境
docker、docker-composeはすでにインストール済みとします。
多少バージョンが違っても大丈夫だとは思います。
docker
```
 Engine:
  Version:          23.0.5
```
docker-compose
```
Docker Compose version v2.17.3
```

# ディレクトリ構成

下記の通りのディレクトリ、ファイル構成としています。
```bash:ディレクトリ構成
.
│
├ docker-compose.yml
├ .env               # docker-compose.yml内の環境変数の定義用のファイル
│
├ front              # Next.js用のディレクトリ
│    ├ Dockerfile    # frontのdockerイメージ作成用のファイル
│
├ back               # Ruby on Rail用のディレクトリ
│    ├ Dockerfile    # backのdockerイメージ作成用のファイル
│    ├ Gemfile
│    ├ Gemfile.lock
│
├ docker
│    ├ db            # dbコンテナのvolumeマウント先ディレクトリ
│    │
│    ├ redis
│        ├ data    　　 # redisのvolumeマウント先ディレクトリ  
```

# それぞれのファイル内容
#### docker-compose.yml
フロント側を8080ポート、バックエンド側を3000ポートで公開するようにしました。
またDBの環境変数などは.envファイルに記述し、そこから取得するようにしています。
```yml:docker-compose.yml
version: '3.8'

services: 
  front:                             # Next.js用コンテナ
    build:
      context: ./front/
    volumes:
      - ./front/app:/usr/src/app     # ボリュームのバインド設定
    command: 'yarn dev'              # コンテナ起動時に実行されるコマンド
    ports:
      - "8080:3000"                  # host側の8080ポートをコンテナの3000ポートに対してフォワード
  back:                              # Rails用コンテナ
    build:
      context: ./back/
    ports:
      - 3000:3000
    volumes:
      - ./back:/app                  # ボリュームのバインド設定
    environment:                     # 環境変数の設定　.envファイルから取得
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_HOST: ${MYSQL_HOST}
    depends_on:                      # コンテナの依存設定
      - db
      - redis
  db:                                # Mysql用コンテナ
    image: mysql:8.0.33
    environment:                     # 環境変数の設定　.envファイルから取得
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - "./docker/db:/var/lib/mysql"
  redis:                              # redis用コンテナ
    image: "redis:latest"
    volumes:
      - "./docker/redis/data:/data"

```


#### .env
key=value の形で記述します。docker-compose.ymlファイルと同階層に配置することで、.envファイルのKeyの値をコンテナ起動時に展開してくれます。
なお、bash_profileなどですでに同keyの環境変数が設定されていた場合は、bash_profileの方の値が優先される(はず)ので注意。
```text:.env
MYSQL_ROOT_PASSWORD=password
MYSQL_DATABASE=testdb
MYSQL_PASSWORD=password
MYSQL_USER=root
MYSQL_HOST=db
```

#### front/Dockerfile
こちらはnode.jsのドキュメントを参考にしました。
package.jsonはまだない状態なので、元のimageそのままです。
```Dockerfile:Dockerfile
FROM node:18.16-alpine
WORKDIR /usr/src/app
```

#### back/Dockerfile
こちらはdockerのドキュメントを参考にしました。
今回dbはmysqlのためdefault-mysql-clientをapt-get installしています。
```Dockerfile:Dockerfile
FROM ruby:3.2

RUN mkdir /app
WORKDIR /app
RUN apt-get update -qq && apt-get install -y default-mysql-client
ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock
RUN gem update && \
    bundle install
ADD . /app

CMD ["rails", "server", "-b", "0.0.0.0", "-p", "3000"]
```

#### back/Gemfile
アプリ生成時のrailsのバージョンを指定しておきます
```Gemfile:Gemfile
source "https://rubygems.org"

gem "rails", "~> 7.0.5"
```

#### back/Gemfile.lock
こちらは空ファイルを準備しておけばOK
```text:Gemfile.lock
```
# イメージのビルド
必要なファイルが揃ったらまずはfront、backでそれぞれ作成したDockerfileをもとにターミナルからdockerイメージの作成をします。
```bash:コマンド
docker-compose build
```

# Next.jsのプロジェクト作成
続けてNext.jsのプロジェクト自動生成を行います。
途中で聞かれる質問(TypeScriptを使うかなど)はお好みで。
実行後frontディレクトリ配下に各種ファイルが生成されます。
```bash:コマンド
docker-compose run --rm front yarn create next-app .
```

# Railsのプロジェクト作成
Railsのプロジェクト自動生成を行います。
今回はapiモードでmysqlを使用するため「--api -d mysql」のオプションを付与しています。
実行後backディレクトリ配下に各種ファイルが生成されます。
```bash:コマンド
docker-compose run --rm back bundle exec rails new . --api -d mysql
```

# Railsの設定ファイルの編集
DBの接続設定ファイルをdocker-compose.ymlで設定した値に変更します。
<%= ENV["key"] %> の形式でコンテナに設定した環境変数を取得します。
```yml:database.yml 一部抜粋
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV["MYSQL_USER"] %>
  password: <%= ENV["MYSQL_PASSWORD"] %>
  host: <%= ENV["MYSQL_HOST"] %>

development:
  <<: *default
  database: <%= ENV["MYSQL_DATABASE"] %>
```

# 動作確認
docker-compose upコマンドで全てのコンテナを立ち上げます。
-d オプションでバックグラウンド実行
--build オプションで起動前にimageのビルドを行います。
```bash:コマンド
docker-compose up -d --build
```

うまくいけば下記のように標準出力されます。
```bash:実行後
 ✔ Container db-1     Started
 ✔ Container redis-1  Started
 ✔ Container front-1  Started
 ✔ Container back-1   Started 
```

念の為psコマンドでコンテナの状態を確認します。
```bash:コマンド
docker-compose ps
```
STATUSが「Up」となっていればコンテナが起動されている状態です。
```bash:実行後
NAME      IMAGE                       COMMAND                  SERVICE             CREATED             STATUS              PORTS
back-1    back                        "rails server -b 0.0…"   back                3 minutes ago       Up 3 minutes        0.0.0.0:3000->3000/tcp
db-1      mysql:8.0.33                "docker-entrypoint.s…"   db                  7 days ago          Up 3 minutes        3306/tcp, 33060/tcp
front-1   front   　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　"docker-entrypoint.s…"   front               3 minutes ago       Up 3 minutes        0.0.0.0:8080->3000/tcp
redis-1   redis:latest                "docker-entrypoint.s…"   redis               7 days ago          Up 3 minutes        6379/tcp
```
ここまでくれば、Next.jsとRailsの初期ページが表示できるはずです。
http://localhost:8080
![スクリーンショット 2023-06-06 18.00.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121039/f993c2cb-ef61-0f50-62a5-a421191efdcb.png)
http://localhost:3000
![スクリーンショット 2023-06-06 18.00.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121039/579f28f6-d69d-bf0b-3484-981964f4f80e.png)

# おわり
docker-composeを使う際のディレクトリ構成だったり、
docker-composeの書き方のベストプラクティス的なものに沿っているかは微妙。。
この辺は、人それぞれだったりプロジェクトによりけりなのかなぁ（独り言）