DockerでRailsチュートリアルのローカル開発環境構築（Rails 6 + PostgreSQL + Webpack)


(2020.10.28 更新: docker-compose.ymlのvolume最適化、build時間短縮)

# はじめに

最新(2020.8.5現在)のRailsチュートリアルではRails 6が使用されており、
これに対応した開発環境構築をDockerでやってみたいと思います。

個人開発アプリの開発環境構築の際に、
私は新しいものが好きだから...とRails 6を導入しました。

ところが...このRails 6からJavaScriptのモジュールバンドラーにWebpackが導入されたことにより、
BootstrapやFontawesomeといったツールの導入、管理方法が変わるだけでなく、
そもそも環境構築の際もRails 5では必要ない手順が必要だったりと、
Rails 6とWebpackの壁に盛大にぶつかることとなりました...

せっかくなので、今回の試みを経て、知識、経験の整理定着を図りたいと思います。

また、実際にRailsチュートリアルを脱線しながら再走し、学びを深められたらと思っています。

## 関連記事
第一回(当記事)
DockerでRailsチュートリアルのローカル開発環境構築（Rails 6 + PostgreSQL + Webpack) - Qiita

第二回
[DockerでRailsチュートリアルのローカル開発環境構築 - RSpec導入 & CircleCIでHerokuデプロイ- - Qiita] (https://qiita.com/naokit-dev/items/7036f05c0f236be8806a)

第３回
[DockerでRailsチュートリアルのローカル開発環境構築 - WebpackでBootstrapとFont Awesomeを導入 - - Qiita](https://qiita.com/naokit-dev/items/30bf299cb0d785f9fb2f)

第4回
[コンテナ上のデバッグ環境構築 - DockerでRailsチュートリアルのローカル開発環境構築 - - Qiita](https://qiita.com/naokit-dev/items/00f9f5f4b04014025d37)

個人開発アプリ
[mdClip ＜オンラインmarkdownエディタ＞](https://mdclip.xyz)

以前Rails 5の環境構築をDockerでやってみた記事
[はじめてのDockerでRails開発環境構築 [NGINX + Rails 5 (puma) + PostgreSQL] - Qiita](https://qiita.com/daddy_hacker/items/66d8fd40ce01d1aeea02)



# 環境

- Mac OS (Mojave)
- Docker for Mac
- Ruby 2.6.3-alpine
- Rails 6.0.3
- PostgreSQL 11.0-alpine

# 構成ファイル

以下の５つのファイルをワークスペースに用意します
`Dockerfile`, `docker-compose.yml`, `Gemfile`, `Gemfile.lock`, `entrypoint.sh`



https://github.com/naokit-dev/sample_app_on_docker.git

```
git clone -b create-docker-files https://github.com/naokit-dev/sample_app_on_docker.git
```




## `Dockerfile`

- イメージの軽量化のため"alpine"を使用

- nodejs, yarnはWebpack導入に必要

- `postgresql`だけでは駄目で、`postgresql-dev`も必要

- bundle installの-j4オプションで`bundle install`が高速化される

- bundle install後、不要ファイル削除はイメージサイズの削減に貢献

  

```
FROM ruby:2.6.3-alpine

ENV LANG=ja_JP.UTF-8
ENV TZ=Asia/Tokyo
ENV ROOT=/myapp \
    GEM_HOME=/bundle \
    BUNDLE_PATH=$GEM_HOME
ENV BUNDLE_BIN=$BUNDLE_PATH/bin
ENV PATH /app/bin:$BUNDLE_BIN:$PATH


WORKDIR $ROOT

RUN apk update && \
    apk upgrade && \
    apk add --no-cache \
        gcc \
        g++ \
        libc-dev \
        libxml2-dev \
        linux-headers \
        make \
        nodejs \
        postgresql \
        postgresql-dev \
        tzdata \
        yarn && \
    apk add --virtual build-packs --no-cache \
        build-base \
        curl-dev

COPY Gemfile $ROOT
COPY Gemfile.lock $ROOT

RUN bundle install -j4
#　不要ファイル削除
RUN rm -rf /usr/local/bundle/cache/* /usr/local/share/.cache/* /var/cache/* /tmp/* && \
apk del build-packs

COPY . $ROOT

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["sh", "/usr/bin/entrypoint.sh"]
EXPOSE 3000
```



## `docker-compose.yml`

- macでストレージのアクセスが遅い問題に対応するためvolumeに`:cahed`オプションを追加
- webpack-dev-serverを別コンテナで起動させて、hot reloadに対応させています.
  (ファイル構成に変更があった場合にブラウザのリロード、必要に応じてjavascript周りのコンパイルをしてくれる)
- dbのhost, user, passwordは後で再利用

```yml
version: "3.8"

services:
  db:
    image: postgres:11.0-alpine
    volumes:
      - postgres:/var/lib/postgresql/data:cached
    ports:
      - "5432:5432"
    environment:
      PGDATA: /var/lib/postgresql/data/pgdata
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --locale=ja_JP.UTF-8"
      TZ: Asia/Tokyo
  app:
    build: .
    command: ash -c "rm -f tmp/pids/server.pid && ./bin/rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp:cached
      - rails_cache:/myapp/tmp/cache
      - node_modules:/myapp/node_modules:cached
      - bundle:/bundle:cached
    tmpfs:
      - /tmp
    tty: true
    stdin_open: true
    ports:
      - "3000:3000"
    environment:
      RAILS_ENV: development
      NODE_ENV: development
      DATABASE_HOST: db
      DATABASE_PORT: 5432
      DATABASE_USER: postgres
      DATABASE_PASSWORD: password
      WEBPACKER_DEV_SERVER_HOST: webpacker
    depends_on:
      - db
      - webpacker

  webpacker:
    build: .
    command: ./bin/webpack-dev-server
    volumes:
      - .:/myapp:cached
      - node_modules:/myapp/node_modules:cached
    environment:
      RAILS_ENV: development
      NODE_ENV: development
      WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
    tty: false
    stdin_open: false
    ports:
      - "3035:3035"

volumes:
  rails_cache:
  node_modules:
  postgres:
  bundle:
```



## `Gemfile`

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

## `Gemfile.lock`
空のファイルで良いので`touch`コマンドのみ

```
touch Gemfile.lock
```

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



# 手順

## Rails 6.0.3をインストールします

```
docker-compose run app rails new . --force --no-deps --database=postgresql --skip-bundle
```

## `Gemfile`
Railsチュートリアルに準じて以下のように変更
(Development, test環境でもPostgreSQLを使用するように変更してあります。)

```
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

gem 'rails',      '6.0.3'
gem 'puma',       '4.3.4'
gem 'pg',         '1.1.4'
gem 'sass-rails', '5.1.0'
gem 'webpacker',  '4.0.7'
gem 'turbolinks', '5.2.0'
gem 'jbuilder',   '2.9.1'
gem 'bootsnap',   '1.4.5', require: false

group :development, :test do
  gem 'byebug',  '11.0.1', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
  gem 'web-console',           '4.0.1'
  gem 'listen',                '3.1.5'
  gem 'spring',                '2.1.0'
  gem 'spring-watcher-listen', '2.0.1'
end

group :test do
  gem 'capybara',                 '3.28.0'
  gem 'selenium-webdriver',       '3.142.4'
  gem 'webdrivers',               '4.1.2'
  gem 'rails-controller-testing', '1.0.4'
  gem 'minitest',                 '5.11.3'
  gem 'minitest-reporters',       '1.3.8'
  gem 'guard',                    '2.16.2'
  gem 'guard-minitest',           '2.4.6'
end

group :production do
end

# Windows ではタイムゾーン情報用の tzinfo-data gem を含める必要があります
#gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
```

## Gemfile.lockをアップデート（必要ないかもしれません）

```
docker-compose run app bundle update
```


## webpackerのインストール
Rails 6ではwebpackerが必要ですが、
このままではインストールされていないためインストールします

```
docker-compose run app rails webpacker:install
```


## データベースの設定
`config/database.yml`
host, username, passwordを`docker-compose.yml`と一致させる

```yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  
#省略...
```


## `docker-compose build`
DockerfileやGemfileを変更する度に'build'が必要です
bundle installも実施されるため少し時間がかかります

```
docker-compose build
```

## いよいよコンテナを起動

```
docker-compose up
```


## データベース作成
このままだとDBがないよと言われるので、development環境のDBを作成

```
docker-compose run app rake db:create
```






## 動作確認

`docker ps`で起動しているコンテナ確認
DB, Rails, webpack-dev-serverの3つのコンテナが起動しています。

```
$ docker ps
CONTAINER ID        IMAGE                            COMMAND                  ...              PORTS                              NAMES
1fb4f53d5652        sample_app_on_docker_app         "sh /usr/bin/entrypo…"   ...    0.0.0.0:3000->3000/tcp             sample_app_on_docker_app_1
ccd40c018d53        sample_app_on_docker_webpacker   "sh /usr/bin/entrypo…"   ...    3000/tcp, 0.0.0.0:3035->3035/tcp   sample_app_on_docker_webpacker_1
74392532098a        postgres:11.0-alpine             "docker-entrypoint.s…"   ...    0.0.0.0:5432->5432/tcp             sample_app_on_docker_db_1

```



ブラウザで`localhost:3000`にアクセスすると

お疲れさまでした"Yay! You’re on Rails!"

# Trouble shoot

##`check_yarn_integrity`関連

コンテナを`docker-compose up`で起動したときに出るエラー

```
app_1        | ========================================
app_1        |   Your Yarn packages are out of date!
app_1        |   Please run `yarn install --check-files` to update.
app_1        | ========================================
app_1        | 
app_1        | 
app_1        | To disable this check, please change `check_yarn_integrity`
app_1        | to `false` in your webpacker config file (config/webpacker.yml).
app_1        | 
app_1        | 
app_1        | yarn check v1.16.0
app_1        | info Visit https://yarnpkg.com/en/docs/cli/check for documentation about this command.
app_1        | 
app_1        | 
app_1        | Exiting
sample_app_on_docker_app_1 exited with code 1
```

`config/webpacker.yml`
以下のように変更します

```yml
#...省略

development:
  <<: *default
  compile: true 

  # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules
  check_yarn_integrity: false # true -> falseに変更
  
#省略...
```



## "webpack-dev-server" not found

同じくコンテナを`docker-compose up`で起動したときに出るエラー

```
webpacker_1  | yarn run v1.16.0
webpacker_1  | error Command "webpack-dev-server" not found.
webpacker_1  | info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
sample_app_on_docker_webpacker_1 exited with code 1
```

`package.json`にwebpack-dev-serverが記述されていないはずなので
webpack-dev-serverをインストール

```
docker-compose run app yarn add webpack-dev-server
```


# おまけ
イメージサイズ 477MB
先人の知恵を多々お借りして軽量化したつもりです。
Dockerを学び始めた頃に作ったのイメージは1.5GBほどありました...

```
$docker images
REPOSITORY                       TAG                 IMAGE ID            CREATED             SIZE
sample_app_on_docker_app         latest              84aed607a3d2        31 minutes ago      477MB
sample_app_on_docker_webpacker   latest              84aed607a3d2        31 minutes ago      477MB
```