DockerでRuby2.7.x＋Rails6.0.x＋MySQL8.0.xな環境を構築する

[dockerにあるサンプル](https://docs.docker.com/compose/rails/)で作る環境はRuby2.5＋Rails5＋PostgreSQLと古め且つPostgreSQLだったので、
それぞれ新しいバージョンである Ruby2.7＆Rails6.0＆MySQL8.0の場合で書き換えたものです。
※2020年6月8日時点ではRuby2.7.1, Rails6.0.3.1, MySQL8.0.20になります

## ファイルの用意
1. Dockerfile
- Gemfile
- Gemfile.lock
- entrypoint.sh
- docker-compose.yml
- .env

※ docker及びdocker-composeのインストールは済んでいること
※ アプリケーションの名前は`myapp`とし、変更する場合は適宜置き換える

### 1. Dockerfileの用意
```docker
FROM ruby:2.7
# or latest

# nodejsとmysql-client (default-mysql-client) のインストール
RUN apt-get update -qq && apt-get install -y curl apt-transport-https wget nodejs default-mysql-client

# Yarn のインストール
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
  echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
  apt-get update -qq && apt-get install -y yarn

RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp

# 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"]
```

* `default-mysql-client`のところは`mariadb-client`でも大丈夫な模様（下記参照）
    * [パッケージ: default-mysql-client (1.0.5)](https://packages.debian.org/stable/database/default-mysql-client)
    * [Circle CI で mysql-client が apt-get install できなくなってCI環境が壊れた話](https://yourmystar-engineer.hatenablog.jp/entry/2019/07/15/140644)
* Rails6を使うので`yarn`のインストールが必要


### 2. Gemfileの用意
```ruby
source 'https://rubygems.org'
gem 'rails', '~>6.0.0'
```

### 3. Gemfile.lockの用意
```bash
$ touch Gemfile.lock
```

* 空ファイルを作っておかないと`Dockerfile`で記述した`COPY Gemfile.lock /myapp/Gemfile.lock`が失敗する

```bash
Step 7/14 : COPY Gemfile.lock /myapp/Gemfile.lock
ERROR: Service 'web' failed to build: COPY failed: stat /var/lib/docker/tmp/docker-builder424702045/Gemfile.lock: no such file or directory
```

### 4. entrypoint.shの用意
```bash
#!/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 "$@"
```

* [dockerにあるサンプル](https://docs.docker.com/compose/rails/)のまま

### 5. docker-compose.ymlの用意
```yml
version: '3'
services:
  db:
    image: mysql:8.0 # or lates:
    environment:
      - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
      - MYSQL_USER=${DB_USER}
      - MYSQL_PASSWORD=${DB_PASSWORD}
    volumes:
      - db-data:/var/lib/mysql
    command: --default-authentication-plugin=mysql_native_password
    ports:
      - ${DB_PORT}:3306
  web:
    build: .
    env_file: .env
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
      - bundle-data:/usr/local/bundle
    ports:
      - "3000:3000"
    depends_on:
      - db
volumes:
  db-data:
  bundle-data:
```
* `MYSQL_ROOT_PASSWORD`は最低限指定する必要がある
    * その他`environment`でMySQLに設定できる環境変数は[Docker HubのEnvironment Variables](https://hub.docker.com/_/mysql/)を参照
* 後述の`.env`ファイルで設定した値を参照するために`${変数名}`を記述
* mysql8ではdefaultの認証方式の変更により`--default-authentication-plugin=mysql_native_password`を指定してやる必要がある
    * [MySQL8.0新機能 (caching_sha2_password 認証プラグイン)](https://www.s-style.co.jp/blog/2018/05/1807/)
    * [LinuxベースのDockerからMySQL 8.0に接続するための記述](https://taker.hatenablog.com/entry/2019/10/19/152007)
* Railsでも参照できるように`env_file: .env`を記述
    * DB側は`environment`で指定しているが、`env_file: .env`でも可

### 6. .envの用意
```ini
DB_ROOT_PASSWORD=mysql_root_password
DB_USER=myapp
DB_PASSWORD=myapp_password
DB_PORT=3306
```
* 前述の`docker-compose.yml`で参照するための値を記述

---
以上でファイルの用意は完了です。
ここまで記述したものがこちらになります。
https://github.com/madai0517/rails6-mysql8_docker


---
## 構築実行

### rails new
```bash
$ docker-compose run web rails new . --force --no-deps --database=mysql --webpacker --skip-test
$ sudo chown -R $USER:$USER . # Linuxの場合に必要。Macでやってる場合は不要
```
* DBをmysqlにするので`--database=mysql`に変更。
* MinitestではなくRspecを使うことが多いと思うので`--skip-test`を追加
* `--webpacker`をつけ忘れた場合は後で`$ docker-compose run web rails webpacker:install`を実行する必要がある
* 既にhomebrewでmysqlを導入済みでport: 3306を使用済みだとコケるので`$ brew services stop mysql`で止めておくこと（mysqlの部分は適宜Formulae名に）


### DBの接続設定
```yaml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV['DB_USER'] %>
  password: <%= ENV['DB_PASSWORD'] %>
  host: db

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test

production:
  <<: *default
  database: myapp_production
  username: myapp
  password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>

```
* `.env`で設定したDB情報を記述
* `host`に指定した`db`は`docker-compose.yml`で記述した`db`と同じもの
* ENVの書き方については[ENV[]とENV.fetch()の違い【Rails/Ruby】](https://qiita.com/sukebeeeeei/items/576f109d57218c8397d5)を参照

### 起動、DBユーザーに権限の付与、DBの作成
```bash
$ docker-compose build
$ docker-compose up -d
$ docker-compose exec db mysql -uroot -p -e"GRANT ALL PRIVILEGES ON *.* TO 'myapp'@'%';FLUSH PRIVILEGES;"
$ docker-compose run web rails db:create
```
* コンテナを起動後、`DB_USER`（この場合は`myapp`）に権限を付与
    * 環境変数`MYSQL_USER`を参照したかったが書き方が分からなかった…

---
以上で環境構築は完了です。
http://localhost:3000/ にアクセスすればいつもの画面が見れるはず。
![スクリーンショット 2020-06-08 3.07.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8041/3f228a16-5459-6a16-b66e-2a29c69b0b7e.png)