Docker × Rails(api) × React × MySQLを使用した環境構築

##背景
アプリケーション開発をチームで行うにあたり、メンバー共有用及び備忘録としてまとめました。

**■Dockerとはなんぞや？って方は下記を視聴推奨**
https://www.youtube.com/watch?v=VIzLh4BgKck
※2倍速だと約7分で視聴可能
↓
**■時間なくて見れないから、Dokcerを何かに例えてって言う方へ**
例：冷凍チャーハン
以下の手順でチャーハンを食べる、つまり仮想環境で作業する。
![スクリーンショット 2021-03-24 3.31.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540447/f56b5a2a-ca55-39fd-77ac-57d27f0eb7b1.png)






**■Dokcerのインストールしてないよって方は下記を参照推奨**
https://qiita.com/kurkuru/items/127fa99ef5b2f0288b81
※インストールまでできればOK

##この記事の目標
Dockerを活用し、仮想環境で開発を行える知識を得る。

##開発環境
ruby 2.6.5
rails 6.0
bundler 2.1.4
node 14
mysql 5.6


##最終的なディレクトリ階層を理解する
■Rails（api）の中身
![スクリーンショット 2021-04-09 2.14.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540447/367a06c5-8550-bf4c-4bef-a77cf964a4bc.png)


■React（frontend）の中身
![スクリーンショット 2021-04-09 2.14.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540447/e55d5168-f823-0a35-d082-9410b4c9c18c.png)


■全体
![スクリーンショット 2021-04-09 2.14.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540447/05e2dc5a-1ea8-84d2-bb2a-5bc5f90d42e9.png)



##1. 各ファイルの作成

まず、アプリケーションの土台となるディレクトリを作成し、作成したディレクトリに移動する。

```
$ mkdir testApp　←実際にアプリケーションを作成する場合は、任意の名前にする
$ cd testApp
```


Rails→api、React→frontendと別々に管理した方が使い分けしやすいので、
まずapiのディレクトリを作成。

```
testApp % mkdir api
```

api用のファイル一式と、Dockerfileを作成する。

```
testApp % rails new api --api --database=mysql --skip-test --skip-turbolinks
testApp % touch api/Dockerfile
```

無事に終了したら、この時に作成された**database.yml**を編集する。

```database.yml
default: &default
  adapter: mysql2
  encoding: utf8  ##utf8へ修正
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password  ##追加
  host: db  ##追加

development:
  <<: *default
  database: testapp_development  ##アプリ名へ修正

test:
  <<: *default
  database: testapp_test  ##アプリ名へ修正

production:
  <<: *default
  database: testapp_production  ##アプリ名へ修正
  username: testapp  ##アプリ名へ修正
  password: <%= ENV['TESTAPP_DATABASE_PASSWORD'] %>  ##アプリ名へ修正
```


frontend用のファイル一式（frontendディレクトリも同時に生成される）、Dockerfileを作成する。

```
testApp % npx create-react-app frontend
testApp % touch frontend/Dockerfile
```

それぞれのDockerfileを起動させるための、**docker-compose.yml**を作成する。

```
testApp % touch docker-compose.yml
```

手動で行うディレクトリ、ファイル作成はひとまず完成。
それぞれのファイルの説明は後ほど行う。

##2. Dockerfileの記述
〜おさらい〜
Dockerfileとは、仮想環境を構成するための情報を記載するファイル。
このファイルが無いとイメージが作れない、つまり、冷凍チャーハンを用意できない。
Dockerfile＝チャーハンのレシピ

まずはapiのDockerfileから記述する。

```api/Dockerfile.
FROM ruby:2.6.5
WORKDIR /testApp/api

RUN gem install bundler -v 2.1.4
```

続いて、frontのDockerfileを記述。

```frontend/Dockerfile.
FROM node:14.15.1-alpine  
WORKDIR /usr/src/app
```


##3. docker-compose.ymlの記述
今回、Dockerfileを２つ作成した。
しかし、これらを１つ１つビルドし、イメージを作成するのも面倒だ。
先ほどのチャーハンの例だと、１つ１つ調理し、冷凍チャーハンにするということになる（現実なら普通だが）

そこで**docker compose**の登場である。
これは、複数あるDockerfileを一括でビルド、ラン（コンテナ化）させることができる。
チャーハンの例だと、複数段あって温め順等を設定できる高機能な調理器具になる。

```docker-compose.yml
version: "3.8"
services:
  api:
    build: ./api
    command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle install --path=vendor/bundle && bundle exec rails s -p 3000 -b '0.0.0.0'"
    ports:
      - "3000:3000"
    volumes:
      - ./api:/testApp/api
    depends_on:
      - db
    tty: true
    stdin_open: true
  frontend:
    build: ./frontend
    command: sh -c "yarn start"
    ports:
      - "8000:3000"
    volumes:
      - ./frontend:/usr/src/app
    depends_on:
      - api
  db:
    platform: linux/x86_64
    image: mysql:5.6
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: root
    ports:
      - "3306:3306"
    volumes:
      - db-volume:/var/lib/mysql
volumes:
  db-volume:


```

##4. ビルドを行う

ここまで準備ができたら、下記コマンドでビルドする

```
testApp % docker-compose build
```

##5. コンテナを作成する
ここまで来たら、あとはコンテナを起動するだけ。
Railsの「rails s」の要領で下記を実行する。

```
# -dはバックグラウンドで稼働するという意味
testApp % docker-compose up -d
```

これで問題なく終了すれば、下記のURLでそれぞれのブラウザが開くか確認。

```
Rails　→　localhost:3000
※この時点ではデータベースを作成していないためエラーが出るが、無視して進む

React　→　localhost:8000
※Reactのマークが出ていればOK
```

最後に下記コマンドでデータベースを作成する

```
testApp % docker exec testApp_api_1 rails db:create
```

基本的に、railsコマンドを入力したい場合は、下記のように行う。

```
①コンテナがup（動いている）のが前提
②docker exec コンテナ名 rails ~~
```

もしrails db:createができずに下記のエラーが出たら

```
OCI runtime exec failed: exec failed: container_linux.go:367: starting container process caused: exec: "rails": executable file not found in $PATH: unknown
```

まずコンテナにログインする

```
testApp % docker ps
コンテナID   testapp_frontend   "docker-entrypoint.s…"   About a minute ago   Up About a minute   0.0.0.0:8000->3000/tcp   testapp_frontend_1
コンテナID   testapp_api        "/bin/sh -c 'rm -f t…"   About a minute ago   Up About a minute   0.0.0.0:3000->3000/tcp   testapp_api_1
コンテナID   mysql:5.6                    "docker-entrypoint.s…"   About a minute ago   Up About a minute   0.0.0.0:3306->3306/tcp   testapp_db_1
```

ここで「testapp_api」のコンテナIDをコピー

```
testApp % docker exec -it コンテナID bash
root@コンテナID:/testApp/api# 　←コンテナへログイン中
```

ログインできたら、下記コマンドでDBを作成する

```
root@コンテナID:/testApp/api# rails db:create
```

もし、「bash: rails: command not found」となってしまったら下記コマンドで再度実行

```
root@コンテナID:/testApp/api# bundle exec rails db:create
```


###ローカルホスト動かないよって方
①まずプロセスが動いているか確認する

```
$ docker-compose ps
```

下記のように１つだけしか動いていなければ、NG。
本来なら、testApp_api_1、testApp_frontend_1の２つも追加で動いていてほしい。

```
testApp % docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                    NAMES
32b0b01d40b9   mariadb:10.0   "docker-entrypoint.s…"   2 minutes ago   Up 2 minutes   0.0.0.0:3306->3306/tcp   testApp_db_1
```

②ログを確認する

```
# -dをつけないことで、ログを出力させる
$ docker-compose up
```

すると、下記のように**apiのところでyarnのインストールが求められている**ことがわかった。

![スクリーンショット 2021-03-24 10.30.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540447/72f9171d-cb86-5477-d1fe-737ae8c90562.png)


③yarnをインストールする

```
$ docker-compose run api yarn install --check-files
```

無事完了したら、再度下記コマンドを実行し、プロセスが動いているか確認する。

```
$ docker-compose up -d
$ docker ps
```

動いていたら、

http://localhost:3000/
http://localhost:8000/
へ再度アクセス（反映されるまで少し時間かかるかも）
動いていなかったら、再度ログや今までの記述に誤りがないか確認する。


##7. コンテナを停止する
使わなくなったり環境の編集が行われたら、下記の手順で停止する。

```
# プロセス確認
docker ps

# プロセスを止める
docoker stop プロセスの名前

# 再度コンテナを起動させる
docker-compose up -d
```

##8. 軽量化について
dockerではよく動いていないコンテナやイメージが残り続けます。
PCの容量を圧迫する原因になりかねないので、
下記のコマンド「prune」を使って定期的にオブジェクトを削除して軽量化する。


```
■ イメージの prune
docker image prune -a
※既存のコンテナ～使われていないイメージすべてを削除するには、 -a フラグを使う。

■ コンテナの prune
docker container prune
docker container prune --filter "until=24h"
※--filter フラグを使うと、範囲を制限できる。たとえば、以下のコマンドは24時間より以前に停止したコンテナのみを削除する。

■ ボリュームの prune
docker volume prune
docker volume prune --filter "label!=keep"
※--filter フラグを使うと、範囲を制限できる。たとえば、以下のコマンドは keep ラベルがないボリュームのみを削除する。

■ ネットワークの prune
docker network prune
docker network prune --filter "until=24h"
※--filter フラグを使うと、範囲を制限できる。たとえば、以下のコマンドは24時間より以前のネットワークのみを削除する。

■ 全てを prune
docker system prune
※docker system prune コマンドは、イメージ、コンテナ、ネットワークを削除（prune）

docker system prune --volumes
※Docker 17.06.1 以上でボリュームも削除したい場合は、 --volumes フラグを使う。
```