Docker for Windows で Rails 6 + MySQL 8 の開発環境を構築する

## 背景
Windows で Rails の開発環境を作ろうとすると native extension などの関係で `rails new` した直後ですら `bundle install` が動かなかったりするので自分は結構ヤキモキすることが多いです。

解決策として `rails new` から `rails server` まで全て Docker のコンテナ内で完結するようにしているのですが、忘れがちになってしまう作業が多いのでまとめました。

`Docker for Windows` と掲げていますが `Docker for Mac` でも問題ないと思います。
※ が、Macの場合は素直に構築できると思うので Windowsユーザーをターゲットにしています

## ターゲット
- 初学程度の Docker/docker-compose の知識がある人
- Windows で Rails の開発環境をしたい人

## ゴール
- Windows で Rails 6 + MySQL 8 の開発環境が構築できる
  - Ruby 2.7
  - Rails 6.0.3
  - MySQL 8
- `rails server` で `localhost:3000` にアクセスして `Welcome画面` を出せる

## 環境・ツール
- Windows 10
- Docker for Windows 2.2.0.5
- Windows Terminal ( PowerShell 6.2.3 )
  - docker / docker-compose コマンドが使えること

PowerShell + Windows Terminal は結構おすすめです。Windowsユーザーは是非。
PowerShell は5系でも大丈夫だと思います。

## 環境構築

### `bundle init` で GemFile を作成する

例として `rails_app` ディレクトリを作成して構築していきます。
rubyイメージのコンテナを使い捨てで立てて実行します。
`-v` でマウントするディレクトリを指定しています。PowerShellだと `${pwd}` でカレントディレクトリが展開できるので使用しています。

```powershell
mkdir rails_app
cd rails_app
docker run --rm -v ${pwd}:/app -w /app ruby:2.7 bundle init
```

### Gemfile 編集

rails_app ディレクトリ内に Gemfile が作成されます。
`gem rails` のコメントアウトを外して rails をインストールできるようにします。

```ruby:Gemfile
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "rails"
```

### `rails new` をする

ポイントは `bundle install` と `rails new` を同時に行うことです。
`bundle install` でコンテナ内で rails コマンドを使えるようにし、即座に `rails new` コマンドを叩きます。

オプションは `--skip-webpack-install` 以外は適宜変更してください。今回はDBはmysqlで進めていきます。
webpacker はイメージに yarn が入っていないのでこの時点ではインストールできません。
また `bundle install` も後でまた実行することになるのでこの時点では skip を推奨します。

```powershell
docker run --rm -v ${pwd}:/app -w /app ruby:2.7 /bin/bash -c "bundle install && rails new . --force --database=mysql --skip-test --skip-bundle --skip-webpack-install"
```

ここまでで以下の状態になります。 
commit は適宜行ってください。

```console
PS C:\Users\username\develop\rails_app> ls


    Directory: C:\Users\username\develop\rails_app

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        2020/05/09    22:50                .git
d-----        2020/05/09    22:50                app
d-----        2020/05/09    22:50                bin
d-----        2020/05/09    22:50                config
d-----        2020/05/09    22:50                db
d-----        2020/05/09    22:50                lib
d-----        2020/05/09    22:50                log
d-----        2020/05/09    22:50                public
d-----        2020/05/09    22:50                storage
d-----        2020/05/09    22:50                tmp
d-----        2020/05/09    22:50                vendor
-a----        2020/05/09    22:50            675 .gitignore
-a----        2020/05/09    22:50             11 .ruby-version
-a----        2020/05/09    22:50            130 config.ru
-a----        2020/05/09    22:50           1712 Gemfile
-a----        2020/05/09    22:28           3356 Gemfile.lock
-a----        2020/05/09    22:50            215 package.json
-a----        2020/05/09    22:50            227 Rakefile
-a----        2020/05/09    22:50            374 README.md
```

### Dockerfile の作成

rails_app 直下に作成します。
ruby イメージの中に `yarn` `nodejs` などを入れていきます。

```dockerfile:Dockerfile
FROM ruby:2.7
ENV LANG C.UTF-8

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

RUN apt-get update -qq && \
    apt-get install -y \
      build-essential \
      nodejs \
      yarn

# Rails Setup
RUN mkdir /app
WORKDIR /app
```

### docker-compose.yml の作成

rails_app 直下に作成します。
applicationのコンテナ、webpackerのコンテナ、mysqlのコンテナをそれぞれ作成します。

`mysql8` では認証方式がデフォルトで `caching_sha2_password` の為 `mysql_native_password` に必ず変更してください。5系を使用する場合は変更不要です。

DB永続化や環境変数など `Welcome画面` を出すだけのものには必要ないものも設定していますが、今後スムーズに開発に入れるように入れています。

```yml:docker-compose.yml
version: '3.3'
services:
  app: &app_base
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/app
      - bundle-data:/usr/local/bundle
    ports:
      - 3000:3000
    environment:
      WEBPACKER_DEV_SERVER_HOST: webpacker
  webpacker:
    <<: *app_base
    command: bash -c "rm -rf public/packs && bin/webpack-dev-server"
    ports:
      - 3035:3035
    environment:
      WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
  db:
    image: mysql:8.0
    command: --default-authentication-plugin=mysql_native_password
    ports:
      - 3306:3306
    volumes:
      - db-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: password
    security_opt:
      - seccomp:unconfined
volumes:
  bundle-data:
  db-data:
```

### database.yml の編集

`config/database.yml` にパスワードとホスト名を設定します。
default配下の `password` と `host` を以下のように編集してください。

```yml:database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password
  host: db
```

### `docker-compose up` を実行する

セットアップは不十分ですが、DBを起動するために一旦 docker-compose をバックグラウンドで起動します。

```powershell
docker-compose up -d
```
実行すると appコンテナ と webpackerコンテナ がエラーを吐いて終了しますが、問題ありません。

実行後に `docker-compose ps` をすると以下のようになります。

```console
PS C:\Users\username\develop\rails_app> docker-compose ps
          Name                         Command                State                   Ports
---------------------------------------------------------------------------------------------------------
rails_app_app_1         bash -c rm -f tmp/pids/ser ...   Exit 127
rails_app_db_1          docker-entrypoint.sh --def ...   Up         0.0.0.0:3306->3306/tcp, 33060/tcp
rails_app_webpacker_1   bash -c rm -rf public/pack ...   Exit 127
```

### rails の初期セットアップをする

`rails new` の際にskipしたコマンドを個別に実行し、最後にDBを作成します。

```powershell
docker-compose run --rm app bundle install
docker-compose run --rm app bin/rails webpacker:install
docker-compose run --rm app bin/rails db:create
```

### docker-compose を再起動する

セットアップが完了したので docker-compose を再起動します。
`restart` でも問題ありませんが、バックグラウンド実行だとPumaのBootが完了したかわからないのでconsoleで確認しながら進めます。

```powershell
docker-compose down
docker-compose up
```

各コンテナでエラーが出ず実行できており、最後に以下の表示が出れば問題ありません。

```
app_1        | Puma starting in single mode...
app_1        | * Version 4.3.3 (ruby 2.7.1-p83), codename: Mysterious Traveller
app_1        | * Min threads: 5, max threads: 5
app_1        | * Environment: development
app_1        | * Listening on tcp://0.0.0.0:3000
app_1        | Use Ctrl-C to stop
```

### `localhost:3000` にアクセスする

ブラウザで http://localhost:3000/ にアクセスします。
おめでとうございます！構築完了です！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66369/912100a0-69fe-5d57-9efa-bede56ec461c.png)

## 参考

[丁寧すぎるDocker-composeによるrails5 + MySQL on Dockerの環境構築(Docker for Mac) - Qiita](https://qiita.com/azul915/items/5b7063cbc80192343fc0)
[Rails + webpacker on Dockerの環境をdocker-composeで構築する - RoadMovie](https://tech-mr-myself.hatenablog.com/entry/2019/04/18/213320)
[Docker で MySQL 8.0.4 を使う - Qiita](https://qiita.com/yensaki/items/9e453b7320ca2d0461c7)

……その他各種公式ドキュメント