Rails 6 + MySQLの環境構築をDocker composeで作る

Dockerの公式サイトには[Railsアプリケーション用のDocker composeチュートリアル](https://docs.docker.com/compose/rails/)があるが、少し情報が古くRails 6ではうまく動かなかったので、Rails 6で動かすための方法を載せておく。基本は公式チュートリアルの手順に従っているため、Rails 6用に変更したところを中心に補足を入れている。

DBはPostgresではなくMySQLを使う方法を載せておく。

# プロジェクトディレクトリの準備

```sh
mkdir myrailsapp
cd myrailsapp
```

# 設定ファイルの準備

```Dockerfile:Dockerfile
FROM ruby:2.5

## nodejsとyarnはwebpackをインストールする際に必要
# yarnパッケージ管理ツールをインストール
RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
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
RUN apt-get update -qq && apt-get install -y nodejs yarn

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

RUN yarn install --check-files
RUN bundle exec rails webpacker:compile

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

```

Rails 6ではWebpackerの実行のためにyarnが必要になるのでDockerfileにyarnのインストールの手順を加える。`ruby:2.5`は[debianをベースにしたイメージ](https://hub.docker.com/_/ruby/)だが、普通に`apt-get`で`yarn`でインストールすると`0.32+git`という変なバージョンがインストールされてしまう。Rails 6が使用するWebpacker 5.x系は動作にYarn 1.0以上を必要とするため、[yarn公式サイトの指示](https://classic.yarnpkg.com/en/docs/install/#debian-stable)に従って最新版のyarnをインストールできるようにする必要があったため、その処理を追加した。

また、Production環境ではWebpackerのcompileを事前にやっておく必要があるので、yarnのパッケージ更新と`webpacker:compile`を追加した。

postgresは使用しないため除外。


```ruby:Gemfile
source 'https://rubygems.org'
gem 'rails', '~>6'
```

GemfileでRails 6系を指定。このGemfileはRailsプロジェクト作成後にRailsプロジェクトの内容で上書きされることになる。

```sh
touch Gemfile.lock
```

```sh: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 "$@"
```

この辺りは公式のサンプルのままなので説明省略。

```yaml:docker-compose.yml
version: '3'
services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: root
    ports:
      - "3306:3306"
    volumes:
      - ./tmp/db:/var/lib/mysql

  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
```

MySQLを使う形に設定を変更。

# Railsプロジェクトの作成
```sh

docker-compose run web rails new . --force --no-deps --database=mysql
```

`--database=mysql`でDB設定をMySQLにした状態でプロジェクト作成。

なお、Railsのバージョンによってはsassc 2.4.0が依存ライブラリとして指定されるが、sassc 2.2.0以降はWindows以外の環境でのインストールに膨大な時間がかかるため、問題なければ2.1系に下げることで`bundle install`のスピードを高速化できる。https://qiita.com/croquette0212/items/d2f48f30c3ed7dcd0e3c


```sh
docker-compose build
```

# DB接続設定

```yaml:database.yml
development:
  <<: *default
  database: myapp_development
  host: db
  username: root
  password: password

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: myapp_test
  host: db
  username: root
  password: password
```

`development`と`test`がdocker-composeで起動したMySQLイメージ`db`を使うように設定を記述。


# DB作成

```sh
docker-compose run web rails db:create
```

# Webpackerの導入

```sh
docker-compose run web rails webpacker:install 
```

Rails 6からSprocketsに代わりWebpackerが使われるようになったためこのステップが必要に。

# イメージの起動

```sh
docker-compose up
```

# Scaffold
```sh
docker-compose run web rails g scaffold article title:string body:text published_at:timestamp
docker-compose run web rails db:migrate
```

docker-composeを使って開発をする場合は、scaffold含めgenerate系のコマンド、マイグレーションも`docker-compose run web`で実行する。


```sh
docker-compose up
```

![スクリーンショット 2020-09-22 0.07.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14097/d8c70886-e711-2942-fb4e-3d85c36e397e.png)