DockerでRails6(MySQL, Webpacker)を動かす。

Docker練習第二弾。Rails5は結構やっていたのでRails6を動かしてみようと思います。

## 環境
- Docker 19.03.13
- docker-compose 1.27.4
- Windows10 Pro

## ディレクトリ構成

```
rails_test/
    ┝ Dockerfile
    ┝ docker-compose.yml
    ┝ Gemfile
    ┝ Gemfile.lock
    ┝ environments/
              └ db.env
```

## Dockerfile

```Dockerfile:Dockerfile
FROM ruby:2.6

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

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 && apt-get install yarn

RUN mkdir /rails_test
WORKDIR /rails_test

ADD Gemfile /rails_test/Gemfile
ADD Gemfile.lock /rails_test/Gemfile.lock

RUN bundle install

ADD . /rails_test
```

- **FROM** Ruby2.6をプルします。
- **RUN** Railsに必要なNode.jsとyarnをインストール。 作業ディレクトリの作成。
- **WORKDIR** 作業ディレクトリの指定をします。
- **ADD** ローカルのGemfileとGemfile.lockをコンテナにコピーします。
- **RUN** バンドルインストールをします。
- **ADD** Gemfileをローカルに反映します。

今回webpackerを使うためにyarnをインストールするのポイントでした。

## docker-compose.yml
今回のメインのdocker-compose.ymlの設定です。

```docker-compose.yml
version: '3'
services: 
  app:
    build: .
    volumes: 
      - .:/rails_test
    command: bash -c "rm -f tmp/pids/server.pid && rails s -b 0.0.0.0"
    ports:
      - 3000:3000
    environment:
      WEBPACKER_DEV_SERVER_HOST: webpacker
    env_file: 
      - ./environments/db.env
    depends_on: 
      - db
  webpacker:
    build: .
    environment:
      NODE_ENV: development
      RAILS_ENV: development
      WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
    volumes: 
      - .:/rails_test
    command: ./bin/webpack-dev-server
    ports:
      - 3035:3035
  db:
    image: mysql:5.7
    volumes: 
      - rails-db:/var/lib/mysql
    env_file: 
      - ./environments/db.env
volumes: 
  rails-db:
```
各コンテナの設定を説明します。

```yml:app
app:
  build: .
  volumes: 
    - .:/rails_test
  command: bash -c "rm -f tmp/pids/server.pid && rails s -b 0.0.0.0"
  ports:
    - 3000:3000
  environment:
    WEBPACKER_DEV_SERVER_HOST: webpacker
  env_file: 
    - ./environments/db.env
  depends_on: 
    - db
```

Railsの設定です。

- **volumes** ローカルのディレクトリとマウントします。これによりコンテナ作成時にローカルでの変更点が反映されます。
- **command** サーバーを立ち上げています。ポイントとしてdocker-compose downでコンテナを削除した際にserver.pidがローカルに残るため再度コンテナを作成した際にサーバーが立ち上げられなくなるため最初にserver.pidを削除します。
- **depends_on** MySQLのコンテナとの起動順序を定義します。Railsが先に立ち上がるとDBと接続できないと怒られます。
- **env_file** ここではDBのユーザーネームなど定義します。今回はrootユーザーで行うためルートユーザーのパスワードだけ設定しておきます。
- **environment** webpackerの設定です。

```yml:webpacker
webpacker:
  build: .
  environment:
    NODE_ENV: development
    RAILS_ENV: development
    WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
  volumes: 
    - .:/rails_test
  command: ./bin/webpack-dev-server
  ports:
    - 3035:3035
```
Webpackerの設定です。ほとんど公式に書いてある通り([Github](https://github.com/rails/webpacker/blob/master/docs/docker.md))にしただけなので、特に言うこともありませんがここでもvolumesをRailsと合わせておかないとwebpack-dev-serverが見つけられなくなります。

```yml:db
db:
  image: mysql:5.7
  volumes: 
    - rails-db:/var/lib/mysql
  env_file: 
    - ./environments/db.env
volumes: 
  rails-db:
```

MySQLの設定です。rails-dbという名前付きボリュームにてDBのデータを永続化しています。また設定は別に用意(db.env)しています。もしrootユーザーでなく新しいユーザーを作りたい場合にはdb.envに書き加えます。

```/environments/db.env
MYSQL_ROOT_PASSWORD=password
```

とりあえずこれだけ書いておきます。

## Gemfile
この二行だけ書き加えます。Gemfile.lockは何も書きません。

```Gemfile:Gemfile
source 'https://rubygems.org'
gem 'rails', '6.0'
```

## アプリ作成
まずはrails newをしてアプリを作ります。

```
$ docker-compose run --rm app rails new . --force -d mysql
```
ディレクトリを新たに作らずにDBはMySQLを使い、Gemfileのオーバーライドをするコマンドを入力しています。ここで自分はyarnを入れていなかったためにwebpackerが途中でインストールが止まりました。

## database.ymlの設定
ローカルにできたdatabase.ymlファイルにdocker-compose.ymlで設定したものに書き換えます。

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

development:
  <<: *default
  database: rails_test_development

test:
  <<: *default
  database: rails_test_test

production:
  <<: *default
  database: rails_test_production
  username: rails_test
  password: <%= ENV['RAILS_TEST_DATABASE_PASSWORD'] %>
```
defaultのところを書き換えています。今回MYSQL_USERとMYSQL_PASSWORDは設定していないのでrootとpasswordになりますが、db.envにて設定できるようにしています。

## コンテナ起動
これでRailsがMySQLに接続できるようになりました。コンテナを全て起動しましょう。

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

コンテナが3つ(app, db, webpacker)停止していなければ成功かと思われます。

```
$ docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS                    NAMES
ab56f7caf110        rails_test_app         "bash -c 'rm -f tmp/…"   2 minutes ago       Up 2 minutes        0.0.0.0:3000->3000/tcp   rails_test_app_1
eb345957801c        mysql:5.7              "docker-entrypoint.s…"   3 minutes ago       Up 2 minutes        3306/tcp, 33060/tcp      rails_test_db_1
a64c29f979c1        rails_test_webpacker   "./bin/webpack-dev-s…"   13 minutes ago      Up 2 minutes        0.0.0.0:3035->3035/tcp   rails_test_webpacker_1
```

## DB作成
最後にdb:createして http://localhost:3000 にてデフォルト画面が見れたら成功です。

```
$ docker-compose exec app rails db:create
```

## 所感
Qiitaの記事を見ながらコピペでやってできた気になっていましたが、改めて自分で書いてみるとMySQLに接続できなかったりWebpackerが起動できなかったりとトラブル続出でした。
また、node_modulesをコンテナの中に置いておくのってセンスが無い気もしました。何かいいアイデアがありそう。

とはいえひと月前までコピペでなんとなくやっていたところも大分自分の言葉で説明できるようになりました。
次は本番環境想定してnginxとの連携とかですかね...。