[Docker] Rails7 + postgreSQL + bootstrapの環境構築

## はじめに

この記事では Docker を使って rails7 + PostgreSQL + bootstrap の環境構築を行います｡

## 環境

- macOS Ventura 13.3.1
- Docker 24.0.7
- Docker Compose v2.23.3-desktop.2
- Ruby 3.2.2
- Rails 7.0.8.1
- PostgreSQL 12.18
- bootstrap5 5.3.2

## ファイルの準備

ファイル構成です｡

```
 app
  |-- Dockerfile
  |-- docker-compose.yml
  |-- Gemfile
  |-- Gemfile.lock
```

### Dockerfile

```Dockerfile:Dockerfile
FROM ruby:3.2.2-alpine
RUN apk update && \
    apk add --no-cache gcompat && \
    apk add --no-cache linux-headers libxml2-dev nodejs tzdata postgresql-dev postgresql git bash && \
    apk add --virtual build-packages --no-cache build-base curl-dev
WORKDIR /app
COPY Gemfile Gemfile.lock /app/
RUN bundle install
RUN apk del build-packages
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
```

**--no-cache**  

パッケージインストールにキャッシュを使用しないオプションです｡  
イメージサイズを小さく保ち､常に最新のパッケージがインストールされます｡

**--virtual**  

build-base, curl-dev を build-packages という名前の仮想グループにまとめています｡  
これらのパッケージは`bundle install`時には必要ですが､アプリケーション実行時には不要となります｡  
そのため､`bundle install`の後に`RUN apk del build-packages`で削除しています｡

**WORKDIR**  

作業ディレクトリを/app に設定しています｡  
/app ディレクトリが存在しない場合は自動で作成されます｡

**COPY**  

ローカルの Gemgfile と Gemfile.lock をコンテナ内の/app ディレクトリにコピーします｡  
次の`RUN bundle install`コマンドで Rails がインストールされます｡

**EXPOSE**  

コンテナが外部に公開しているポートが 3000 番である事を示しています｡  
このコマンドが実際にコンテナのポートを外部に公開する訳ではありません｡

**CMD**  

コンテナ実行が実行されると`rails server -b 0.0.0.0`コマンドが自動的に実行され､Rails サーバーが起動します｡`0.0.0.0`とは localhost の事です｡

### docker-compse.yml

```yml:docker-compose.yml
version: '3.9'
services:
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    environment:
      POSTGRES_PASSWORD: password
    ports:
      - "3000:3000"
    volumes:
      - .:/app
    depends_on:
      - db
    stdin_open: true
    tty: true
  db:
    image: postgres:12-alpine
    environment:
      POSTGRES_PASSWORD: password
    volumes:
      - db-data:/var/lib/postgresql/data
volumes:
  db-data:
```

**build:**  

現在のディレクトリ(`.`)にある Dockerfile を使用して web サービスのイメージをビルドします。

**command:**  

1. コンテナを強制終了した場合等に残る pid ファイルを､コンテナ実行時に削除し､サーバーの再起動を妨げる問題を防ぎます｡
2. Rails サーバーをポート 3000 で立ち上げます｡

**environment:**  

web と db で`POSTGRES_PASSWORD`の環境変数に password を設定しています｡

**ports:**  

ローカルのポート3000をコンテナのポート3000にマッピングしています。

**volumes:**  

- web はバインドマウントでローカルのカレントディレクトをコンテナ内の/app ディレクトリにマウントしています｡
- ボリューム db-data を使用してPostgreSQL のデータを永続化します。コンテナを停止または削除してもデータベースのデータが保持されます。

**depends_on:**  

db のコンテナが立ち上がってから web のコンテナを起動します｡

**volumes: db-data:**  

db-data という名前の volume を作成します｡

### Gemfile, Gemfile.lock

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

gem "rails", "~> 7.0.0"
```

- rails は 7.0.0 以上でマイナーバージョンが変わらない範囲(7.1 未満)でインストールされます｡

- Gemfile.lock には何も記述しません｡

## 環境構築

必要なファイルが揃ったので実際に環境構築を行います｡
手順は以下の通りです｡

1. コンテナを実行して Rails アプリの雛形を作成する
1. Gemfile に 必要な gem を追加する
1. docker-compose.yml を再度ビルドして gem をインストールする
1. bootstrap の設定をする
1. config/database.ymlの内容を変更する
1. `db:create`で DB を作成する
1. 動作確認

それぞれの詳細を説明していきます｡

### 1.コンテナの実行と Rails アプリの作成

コンテナの実行は`docker compose run`コマンドによって行うことが出来ます｡  
まだイメージが作成されていないので自動でビルドもしてくれます｡

引数には web サービス(コンテナ)内で Rails アプリの雛形を作成するコマンド`rails new .`を渡しています｡
- `--force`オプション : 既存の Gemfile を上書きする
- `--database=postgresql`オプション : データベースシステムに PostgreSQL を使用する

```bash:bash
docker compose run --rm web rails new . --force --database=postgresql
```

### 2.gem の追加

① で更新された Gemfile に以下の gem の追加を行います｡

```ruby:Gemfile
gem "bootsnap", require: false
gem "bootstrap" #<= 追加
gem "sassc-rails"  #<= コメントアウトを外す
```

ちなみに sassc-rails は非推奨みたいですが､今回は sassc-rails を使います｡
https://github.com/twbs/bootstrap-rubygem/blob/main/README.md

### 3.gem のインストール

この環境では gem のアップデートやインストールは`docker compose build`コマンドを使って行います｡

```bash:bash
docker compose build
```

### 4.bootstrap の設定

bootstrap を利用するためにいくつかコードを変更します｡

- app/assets/stylesheets/application.css を削除する
- app/assets/stylesheets/application.scss を作成して以下を記述する

```scss:application.scss
@import "bootstrap";
```

- app/javascript/application.js に bootstrap を追記する

```javascript:application.js
// Configure your import...
import "@hotwired/turbo-rails"
import "controllers"
import "bootstrap"　//追加
```

- config/importmap.rb に以下の pin を追加する

```ruby:importmap.rb
pin "bootstrap", to: "https://cdn.jsdelivr.net/npm/bootstrap@5/dist/js/bootstrap.bundle.min.js", preload: true
pin "@popperjs/core", to: "https://cdn.jsdelivr.net/npm/@popperjs/core@2/dist/umd/popper.min.js", preload: true
```
  
- bootstrap の動作確認用のページを作成します｡

```bash:bash
docker compose run --rm web bin/rails g contorller hello index
```

app/views/hello/index.html.erb が作成されるので以下のコードを貼り付けます｡

```html:index.html.erb
<h1>Hello#index</h1>
<p>Find me in app/views/hello/index.html.erb</p>
<!-- Example single danger button -->
<div class="btn-group">
  <button type="button" class="btn btn-danger dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
    Action
  </button>
  <ul class="dropdown-menu">
    <li><a class="dropdown-item" href="#">Action</a></li>
    <li><a class="dropdown-item" href="#">Another action</a></li>
    <li><a class="dropdown-item" href="#">Something else here</a></li>
    <li><hr class="dropdown-divider"></li>
    <li><a class="dropdown-item" href="#">Separated link</a></li>
  </ul>
</div>
```

以上で bootstrap の設定は完了です｡

### 5.database.yml の設定を行う

config/database.yml に postgreSQL に接続するための設定を追加していきます｡

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

- host: docker-compose.yml で定義した database のサービス名を指定します
- username: postgreSQL のデフォルトの usernameであるpostgres を指定しています
- password: docker-compose.yml の`POSTGRES_PASSWORD`でセットしたパスワードと同じ値を設定しています

### 6.DB の作成

以下のコマンドで DB を作成します｡

```bash:bash
docker compose run --rm web bin/rails db:crete
```

### 7.動作確認

最後に rails sever を立ち上げて確認してみましょう｡  
`docker compose up -d`でコンテナを実行してlocalhost:3000/index/helloにアクセスします｡

このような画面が表示されれば成功です!  
|　　　　　　　　 　　　　　　　　 　　　　　　　　　　　　　　　　　　　　　|
|:-:|
|![bootstrapが適用された画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551297/6483debe-863a-dcad-ae5d-963aa84711a1.png "Qiita")|
<font color="gray">localhost:3000/index/hello</font>

## 参考

https://zenn.dev/hyzsa/articles/20230906-build-docker-rails7-postgresql