【Docker】Rails6 API + React（TypeScript）のSPA開発環境を構築する

# はじめに
自分用のメモとして記録しておきます。

https://github.com/taki-21/rails-react-ts-docker

# 1. ディレクトリ構成の確認と必要ファイルの用意
以下のディレクトリ構成になるようにファイルを用意する。
![スクリーンショット 2021-12-19 1.02.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646903/169bddcb-4ec9-4bdd-fe69-ac63f2d773bf.png)

```bash
$ mkdir backend
$ mkdir frontend
$ touch docker-compose.yml
$ touch backend/Gemfile
$ touch backend/Gemfile.lock
$ touch backend/entrypoint.sh
$ touch backend/Dockerfile
$ touch frontend/Dockerfile
```

# 2. docker-compose.ymlの編集

```yml:docker-compose.yml
version: "3"

services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: password
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - mysql-data:/var/lib/mysql
      - /tmp/dockerdir:/etc/mysql/conf.d/
    ports:
      - 3306:3306

  api:
    build:
      context: ./backend/
      dockerfile: Dockerfile
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    image: rails:dev
    volumes:
      - ./backend:/myapp
      - ./backend/vendor/bundle:/myapp/vendor/bundle
    environment:
      TZ: Asia/Tokyo
      RAILS_ENV: development
    ports:
      - 3001:3000
    depends_on:
      - db

volumes:
  mysql-data:
```

# 3. API側の環境構築
## 3-1. 各ファイルの編集

```Dockerfile:backend/Dockerfile
FROM ruby:3.0
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs

ENV APP_PATH /myapp

RUN mkdir $APP_PATH
WORKDIR $APP_PATH

COPY Gemfile $APP_PATH/Gemfile
COPY Gemfile.lock $APP_PATH/Gemfile.lock
RUN bundle install

COPY . $APP_PATH

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
```

```ruby:backend/Gemfile
source 'https://rubygems.org'
gem 'rails', '>= 6.1.4.1'
```

```ruby:backend/Gemfile.lock
# 空のまま
```

```sh:backend/entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
```

## 3-2. Railsアプリケーションの作成

```bash
$ docker-compose run --no-deps api rails new . --force  -d mysql --api
```
### オプションについて
* **`docker-compose run`** に対するオプション
    * `--no-deps`: リンクしたサービス（今回であれば`depends_on`で指定した`db`）を起動しない
* **`rails new`** に対するオプション
    * `--force`（`-f`）: ファイルが既に存在している場合は上書きする
    * `-d`（`--database`）: データベースの種類
    * `--api`: apiモードでアプリケーションを作成

## 3-3. build
`rails new`でGemfileが更新されたので`docker-compose build`を実行する

```bash
$ docker-compose build
```

## 3-4. database.ymlの編集
```yml:backend/config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password # デフォルトだと空欄になっているので変更
  host: db # デフォルトだとlocalhostになっているので変更

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test

production:
  <<: *default
  database: <%= ENV["DATABASE_NAME"] %>
  username: <%= ENV["DATABASE_USERNAME"] %>
  password: <%= ENV["DATABASE_PASSWORD"] %>
  host: <%= ENV["DATABASE_HOST"] %>
```

## 3-5. データベースの作成
<details><summary>データベースを作成しないと以下のようにデータベースがないと怒られます。</summary>
![スクリーンショット 2021-12-23 0.55.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646903/c792a647-25ef-c2d5-9ef9-4d5055162ed8.png)
</details>

```
$ docker-compose run api rails db:create
```
**ー API側の環境構築は以上になります。**

# 4. Front側の環境構築
## 4-1. docker-compose.ymlにfront側の処理を追加

```yml:docker-compose.yml
version: "3"

services:
  db:
    ...

  api:
    ...

  # ↓ 追加
  front:
    build:
      context: ./frontend/
      dockerfile: Dockerfile
    volumes:
      - ./frontend:/usr/src/app
    command: sh -c "cd app && yarn && yarn start"
    ports:
      - "4000:3000"

volumes:
  mysql-data:
```

## 4-2. Dockerfileの編集
```Dockerfile:frontend/Dockerfile
FROM node:14.18-alpine
WORKDIR /usr/src/app
```

## 4-3. build
```bash
$ docker-compose build
```

## 4-4. アプリケーション作成

* アプリケーション名は`app`とする。
* TypeScriptを使いたので最後に`--template typescript` を追加する。
* `node`のバージョンによってはESlint等をインストールする際にエラーが発生する可能性があるので、適宜、`node`イメージのバージョンを変更する。

参考: https://hub.docker.com/_/node

### npmで管理する場合

```bash
$ docker-compose run --rm front sh -c "npx create-react-app app --template typescript"
```

### yarnで管理する場合（<= 今回はこちら）

```bash
$ docker-compose run --rm front sh -c "yarn create react-app app --template typescript"
```

※ `yarn`はnodeのイメージ（`node:14.18-alpine`）にデフォルトで入っているらしいのでインストールは不要。
参考: https://github.com/nodejs/docker-node/blob/b695e030ea98f272d843feb98ee1ab62943071b3/14/alpine3.14/Dockerfile

## 4-5. ディレクトリ構成の確認
以下のように指定した`app`ディレクトリ配下に`node_modules`, `public`, `src`ディレクトリ、`pacage.json`、`tsconfig.json`, `yarn.lock`ファイル等が作成されていることが確認できます。
![スクリーンショット 2021-12-24 4.15.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646903/58fa216a-30bb-e445-316c-07bf86233a7d.png)


# 5. 各コンテナを立ち上げる
```bash
$ docker-compose up -d
```

# 6. 確認
## API側
http://localhost:3001 にアクセス
![スクリーンショット 2021-12-19 2.10.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646903/a5b05557-2cc9-1029-7f84-4e12d8a0fb83.png)

## Front側
http://localhost:4000 にアクセス
![スクリーンショット 2021-12-19 2.10.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646903/2d489527-768b-42b7-c16e-204352c83765.png)

**ー 無事に初期画面が表示されました。**

# 参考

https://docs.docker.com/compose/reference/run/

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

https://create-react-app.dev/docs/getting-started/