Ruby on Rails7.2環境をDockerで構築する

## 背景

Hotwireの学習の一環で**Turbo Rails Tutorial**をやりたいと思い、久しぶりに環境構築しました。

https://www.hotrails.dev/turbo-rails

## 構築環境

|項目|バージョン|
|:--:|:--:|
|OS|macOS 15.0.1 Sequoia|
|チップ|Apple Silicon(M3)|
|Docker|27.0.3|
|Ruby|3.3.5|
|Ruby on Rails|7.2.0|
|Node.js|20.18.0|
|postgres|16.4|

:::note info
windows11（WSL2）でも確認済み
:::

## 構築手順

### ① 必要なファイル作成

合計7つのファイルを作成します。

#### 空ファイル

```Command
touch Gemfile.lock
touch yarn.lock
```

#### Docker関連ファイル

- Dockerfile
  - イメージはrubyを使用し、node.jsはコマンドを使ってインストール
  - ホストマシン側でファイルを編集できるようにroot権限以外のユーザーを作成

```Dockerfile
FROM ruby:3.3.5

ARG HOST_UID
ARG HOST_GID
RUN groupadd -g ${HOST_GID} customgroup && \
	useradd -m -u ${HOST_UID} -g customgroup customuser && \
	usermod -aG sudo customuser && \
	echo "customuser ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

WORKDIR /my_app
COPY Gemfile /my_app/Gemfile
COPY Gemfile.lock /my_app/Gemfile.lock

RUN apt-get update -qq \
   && apt-get install -y curl gnupg2 \
   && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
   && apt-get install -y nodejs postgresql-client imagemagick \
   && rm -rf /var/lib/apt/lists/* \
   && npm install --global yarn
   
# 日本語環境設定
RUN apt-get update && apt-get install -y locales task-japanese && \
   locale-gen ja_JP.UTF-8 && \
   localedef -f UTF-8 -i ja_JP ja_JP && \
   apt-get -qqy --no-install-recommends install -y fonts-takao-gothic fonts-takao-mincho && \
   dpkg-reconfigure --frontend noninteractive locales && \
   fc-cache -fv
ENV LANG ja_JP.UTF-8
```
- compose.yaml
  - Turbo Rails Tutorialはシステムテストも実施するため、Chroniumコンテナも追加する[^1]

https://hub.docker.com/r/selenium/standalone-chromium

[^1]: MacのM2に対応しているのはChroniumコンテナのみ

```compose.yaml
services:
  db:
    image: postgres:16.4
    volumes:
      - postgres:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password
  web:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        HOST_UID: ${HOST_UID}
        HOST_GID: ${HOST_GID}
    environment:
      - HOST_UID=${HOST_UID}
      - HOST_GID=${HOST_GID}
    volumes:
      - .:/my_app
      - ./docker/volumes/bundle_data:/usr/local/bundle
    ports:
      - 3000:3000
    depends_on:
      - db
    user: ${HOST_UID}:${HOST_GID}
    tty: true
  chrome:
    image: selenium/standalone-chromium:4.25.0-20241010
    shm_size: 2gb
    ports:
      - 4444:4444
volumes:
  postgres: {}
```

- .env
  - ホストマシンのユーザーIDとグループIDを記載する

```.env
# idコマンドで調べた結果を書く
HOST_UID=1000 
HOST_GID=1000
```

#### Ruby関連ファイル

- コマンドでRuby on Railsをインストールするため、sourceのみ記載する

```Gemfile
source 'https://rubygems.org'
```

#### Node関連ファイル

```package.json
{}
```

#### ファイル構造

最終的に作成したファイルは以下です。

```作成したファイル
├── Dockerfile
├── compose.yaml
├── Gemfile
├── Gemfile.lock
├── package.json
├── yarn.lock
└── .env
```

### ② Ruby on Railsのインストール

コマンドを実行して、Ruby on Railsをインストールします。

#### Chroniumコンテナ以外を立ち上げる

- `docker compose up -d web`

#### webコンテナ（Ruby on Rails）に接続する

- `docker compose exec web bash`

#### Railsをインストールする

- `bundle add rails`

### ③ Ruby on Railsのひな型を作成する

今回はTurbo Rails Tutorialで使用する内容と合わせます。

- `bundle exec rails  new . --force --no-deps --database=postgresql --css=sass --javascript=esbuild`

```フォルダ構造
├── .github
├── app
├── bin
├── config
├── db
├── docker
├── lib
├── log
├── node_modules
├── public
├── storage
├── test
├── tmp
├── vender
├── .dockerignore
├── .env
├── .gitattributes
├── .gitignore
├── .node-version
├── .rubocop.yml
├── .ruby-version
├── compose.yaml
├── config.ru
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── package.json
├── Procfile.dev
├── Rakefile
├── README.md
├── setup_rails.sh
└── yarn.lock
```

:::note info
この時、Dockerfileが上書きされる[^2]ため、①で記載した内容に置き換えてください。
上書きしたDockerfileもDockerfile.prod等のProduction用に残すのも1つです。
:::

[^2]: Ruby on Rails7.1から`rails new`を実行するとProduction用のDockerfileが生成されます。

### ④ 生成されたファイルの編集

サーバーを立ちあげるために、生成されたファイルを編集します。

- config/database.yaml

```database.yaml
  default: &default
  adapter: postgresql
  encoding: unicode
+ host: db
+ username: postgres
+ password: password
  # For details on connection pooling, see Rails configuration guide
  # https://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
```

- Procfile.dev

```Procfile.dev
web: env RUBY_DEBUG_OPEN=true bin/rails server -p 3000 -b '0.0.0.0'
js: yarn build --watch
css: yarn build:css --watch
```

### ⑤ DBの作成

- `rails db:create`を実行

### ⑥ Railsの立ち上げ

- `bin/dev`を実行
- `http://localhost:3000`にアクセスし、以下の画面になっていればOK

![Rails7.2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1895890/cf724a95-2b1a-7e0b-0893-a9292c945b14.png)

## 次回から立ち上げ

以下の順番にコマンドを実行すると立ち上げることができます。
- `docker compose up -d`
- `docker compose exec web bash`
- `bin/dev`

## 【補足】システムテストの設定

今回はChroniumコンテナを作成しているため、以下の設定が必要です。

```application_system_test_case.rb
require "test_helper"

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase

  # Options設定は任意
  options = Selenium::WebDriver::Chrome::Options.new
  options.add_argument('--window-size=1400,1400')
  options.add_argument('--lang=ja')
  options.add_argument('--no-sandbox')
  options.add_argument('--disable-dev-shm-usage')
  options.add_argument('--disable-gpu')

  # リモート接続設定（ホスト名はコンテナ名と同じにする）
  driven_options = {browser: :remote, url: 'http://chrome:4444/wd/hub',options: options}
  driven_by :selenium, using: :headless_chrome, options: driven_options

  setup do
    Capybara.server_host = "0.0.0.0" # すべてのインターフェイスにバインドする
    Capybara.app_host = "http://#{IPSocket.getaddress(Socket.gethostname)}"
    super
  end

  teardown do
    Capybara.current_session.driver.quit
    Capybara.reset_sessions!
  end
end
```

## 改善点

Chroniumコンテナの立ち上げにかなり時間がかかるため、webコンテナに直接Chroniumドライバーを直接インストールするのも1つかなと思います。

## 参考資料

- [Rails 7.2時代の最小構成セットアップ手順](https://zenn.dev/thr3a/articles/3d213b9ae48d66)
- [RailsRails テスティングガイド - システムテスト](https://railsguides.jp/testing.html#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%83%86%E3%82%B9%E3%83%88)
- [Rails7+bootstrap+esbuld+Postgresqlの構成でDockerの開発環境を構築する](https://blog.furu07yu.com/entry/build-docker-rails7-postgresql)
- [List of Chromium Command Line Switches](https://peter.sh/experiments/chromium-command-line-switches/)
- [Capybara APIドキュメント](https://rubydoc.info/github/teamcapybara/capybara/master)
- [Selenium - Chrome固有の機能](https://www.selenium.dev/ja/documentation/webdriver/browsers/chrome/)
- [dockerでvolumeをマウントしたときのファイルのowner問題](https://qiita.com/yohm/items/047b2e68d008ebb0f001)