壁にぶち当たりながらDockerで環境構築をする（Rails6 + PostgreSQL）

# はじめに
久しぶりに個人開発をするにあたってDockerで環境構築をしようと思いましたが、
- 転職時のポートフォリオに開発環境で使ったときは、使えそうなコードを見つけ、コードの意味を調べて導入するも、あらためて見るとあまり理解できていない
- 業務でもすでに準備されているDockerfileなどにより環境構築し、日頃はコンテナを起動して開発を進めるだけなので理解が深まっていない

ということに気がつき、危機感を覚えました。

というわけで、今回は自分で考えながらDockerでの環境構築に取り組んでみました。
その過程を記事にすることで頭の整理をするとともに、同じような状況の人の気づきになってくれれば嬉しく思います。

ちなみに、当面の目標はRails6 + Vue3 + PostgreSQLの開発環境をDockerで構築することですが、いきなりあれもこれも手を出すことはせず、まずはRails6 + PostgreSQLに絞ることにしました。

**(2022.9.18追加)**
Rails6 + Vue3 + PostgreSQLの開発環境をDockerで構築するまでが完了したので、続きを記事にまとめました。

https://qiita.com/subun33/items/b051f5697482203aa31d

https://qiita.com/subun33/items/b3b48b7fb04113f4a049

# 目次
[1. 開発環境](#1-開発環境)
[2. 今回のゴール](#2-今回のゴール)
[3. この記事で扱わない内容](#3-この記事で扱わない内容)
[4. 本編](#4-本編)
　[4-1. 事前準備](#4-1-事前準備)
　[4-2. アプリを作成](#4-2-アプリを作成)
　[4-3. yarnをインストール](#4-3-yarnをインストール)
　[4-4. 再びアプリを作成](#4-4-再びアプリを作成)
　[4-5. データベースの設定](#4-5-データベースの設定)
　[4-6. localhost:3000にアクセス](#4-6-localhost3000にアクセス)
　[4-7. scaffoldを使って動作確認](#4-7-scaffoldを使って動作確認)
[5. まとめ](#5-まとめ)

# 1. 開発環境
- macOS Monterey 12.3.1
- Docker 20.10.13
- docker-compose 1.29.2

# 2. 今回のゴール
Dockerで以下の開発環境を構築すること
- Ruby on Rails 6.1
- Ruby 3.0
- PostgreSQL

# 3. この記事で扱わない内容
- イメージやコンテナといったDockerの基本的事項について
- コードの詳細な説明について

# 4. 本編
今回、構築したいRails + PostgreSQLの開発環境については、Dockerの公式サイトにチュートリアルがあるので、[こちら](https://docs.docker.com/samples/rails/)をベースに進めていきます。

ただ、このチュートリアルはRuby 2.5、Rails 5とバージョンが古いため、それを今回のゴールに定めたバージョンに変えて構築することがこの記事のポイントです。

## 4-1. 事前準備
まずは作業ディレクトリを準備します。
```bash
$ mkdir docker-sample
$ cd docker-sample
```
公式チュートリアルに沿って、以下のファイルを作業ディレクトリ内に作成します。
この時、導入したいバージョンを忘れずに指定します。

```Dockerfile:Dockerfile
# ここでバージョンを修正
FROM ruby:3.0 

RUN apt-get update -qq \
    && apt-get install -y nodejs postgresql-client

WORKDIR /app
COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock

RUN bundle install

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]
```

```ruby:Gemfile
source 'https://rubygems.org'
# ここでバージョンを修正
gem 'rails', '~> 6.1'
```

```ruby:Gemfile.lock
# 何も書きません
```

```shell:entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /app/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
```

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

## 4-2. アプリを作成
上記ファイルを準備したら、以下のコマンドでアプリを作成します。

```bash
$ docker compose run --no-deps web rails new . --force --database=postgresql
```
このコマンドについて、簡単に解説しておきます。

`docker compose run --no-deps web`は、docker-compose.ymlで定義したwebコンテナで、以後のコマンド（`rails new`）を実行するという意味です。
`--no-deps`オプションは、リンクしているコンテナ（今回であれば`depends_on`で指定されているdbコンテナ）を起動しないというものです。

`rails new . --force --database=postgresql` でアプリを作成します。
`.`は現在のディレクトリにアプリを作成するということを示しています。
`--force`は同名のファイルが存在する場合に上書きするというオプションです。
今回は事前に準備したGemfileとGemfile.lockが現在のディレクトリにあるので、上書きされます。
`--database=postgresql`は見たまんまですが、データベースにPostgreSQLを指定しています。

さて、ここで早速ですが、エラーがでました。内容は以下のとおりです。
```
Yarn not installed. Please download and install Yarn from https://yarnpkg.com/lang/en/docs/install/
```

yarnをインストールしろと怒られました。

そういえば、Rails6からはWebpackerがデフォルトと聞いたことがあります。[（参考）](https://railsguides.jp/6_0_release_notes.html#:~:text=Webpacker%E3%81%8CRails%206%E3%81%AE%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88JavaScript%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%A9%E3%81%AB%E3%81%AA%E3%82%8B)
このWebpackerでyarnが必要となるので、怒られているようです。

## 4-3. yarnをインストール

早速、yarnをインストールしたいので、エラー文にも載っている[公式サイト](https://yarnpkg.com/lang/en/docs/install/)でインストール方法を調べます。

何やらnpmを使う方法と使わない方法があるようです。
今のDockerfileではnpmはインストールしていません。
以後、yarnを使うのであれば、このためだけにnpmをインストールする必要はないと考え、今回は使わない方法で進めることにしました。

:::note info
npmをインストールする方法も試したところ、うまくいきました。
イメージやコンテナを作り直して、あれこれ気軽に試せるところもDockerの魅力ですね！
:::

npmを使わないyarnのインストール方法を確認するためには、OSを選択する必要があるようです。
[docker hub](https://hub.docker.com/_/ruby)を見にいきましたが、すぐには見つけらませんでした。
見つけられないなら、直接コンテナに入って確認することはできないだろうかと思い、OS情報のありかを調べてみます。
どうやら`/etc/os-release`にOS情報が保存されているようです。
それでは、OS情報を調べる目的だけなので、今回のベースイメージである`Ruby 3.0`の最小限のDockerfileを準備し、作成したコンテナでOS情報を調べてみます。

準備したDockerfileはこちら
```Dockerfile:Dockrefile
FROM ruby:3.0

CMD ["/bin/bash"]
```
`CMD ["/bin/bash"]`により、コンテナを起動させたらbashが起動します。

それでは、以下のコマンドでイメージの作成、コンテナの起動をします。
```shell
$ docker image build -t os_check .
$ docker container run --rm -it os_check
```
`-t`オプションは後で管理しやすいようにこのイメージに名前をつけているだけなので、それほど気にしなくて大丈夫です。

無事にコンテナが起動したので、先ほどのOS情報の入っているファイルを見てみます。
```bash
$ cat /etc/os-release
```

出力結果はこちら
```
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
NAME="Debian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
```

このベースイメージのOSはLinuxディストリビューションのDebianであることがわかりました。
これで安心してyarnのインストールが進められます。

せっかくなので、このままコンテナ内でyarnインストールのコマンドを実行してみましょう。
```bash
$ 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 -qq
$ apt-get install -y yarn

$ yarn -v
1.22.19
```
ばっちりインストールできているようなので、Dockerfileに反映させます。
```Dockerfile:Dockerfile
FROM ruby:3.0

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 -qq \
    && apt-get install -y nodejs yarn postgresql-client

WORKDIR /app
COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock

RUN bundle install

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]
```

## 4-4. 再びアプリを作成
再度、以下のコマンドを実行してみます。
```bash
$ docker compose run --no-deps web rails new . --force --database=postgresql
```

今度こそRailsアプリが作成できました！

Gemfileが書き換えられたので、イメージをbuildしておきましょう。
```bash
$ docker compose build
```

## 4-5. データベースの設定
再び、公式チュートリアルに戻り、データベースの設定をします。
作成したRailsアプリのconfig/database.ymlを以下のとおり修正します。
```yml:database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: app_development


test:
  <<: *default
  database: app_test
```

## 4-6. localhost:3000にアクセス
まずは、以下のコマンドでコンテナを起動させ、データベースを作成します。
```bash
$ docker compose up -d
$ docker compose run web rails db:create
```

さて、これで準備は完了です。
[localhost:3000](http://localhost:3000/)にアクセスしてみましょう。
![Ruby_on_Rails.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1581554/bd3902a4-883a-9896-0d5b-ff400ccfc2ce.png)
お馴染みのページが迎えてくれました！やった！

## 4-7. scaffoldを使って動作確認
ちゃんと動いてくれるのかscaffoldを使ってサクッと確認してみます。

先ほどのコンテナは起動させたままで以下のコマンドを実行します。
```bash
$ docker compose exec web bash 
$ rails generate scaffold book title:string
$ rails db:migrate
```

localhost:3000/booksにアクセスすると以下の画面が表示されます。
New Bookからデータを作成し、その後の編集や削除も問題なくできました。
![App.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1581554/52ca250d-0ee5-f2a3-199d-7162f24904e5.png)

# 5. まとめ
今回は公式チュートリアルに少し変更を加えて、Dockerで開発環境を構築をしてみました。
この変更により発生したエラーを調べつつ解消するという過程が、今後に活きてくるのではないかと考えています。

実際、開発をスタートしてから「あー、この部分が足りなかったなー」と感じることもあると思います。
そんな時は今回のように必要なライブラリなどを一つずつ確認しながら対応していきたいと思います。

また、記事にまとめるにあたって、イメージ、コンテナを作っては壊し、作っては壊しを何度も繰り返しました。
Dockerではこういった作っては壊しが手軽にできるので「このコマンドを使ってみよう」「このライブラリをインストールしてみよう」という実験がとてもやりやすく、この点もDockerの魅力の一つだなと感じました。（ローカルでは元に戻すことが難しいので、不安になります…）

途中、脇道に逸れて、`Ruby3.0-alpine`など別のベースイメージを使ったらどうなるのかなども試してみました。
もともとインストールされているライブラリが違ったり、ライブラリをインストールするためのコマンドが違ったりと、今まで意識したことのなかった違いも発見できて、いい勉強となりました。
同時に、同じRuby3.0を使うとしても、どのイメージが最適かといった選定ができるようになるには、まだまだ勉強が必要だなと感じました。


さて、今回の記事はここまでです。
長い記事にお付き合いいただき、ありがとうございました。

次はRails6 + Vue3 + PostgreSQLの開発環境構築を目指して、引き続きDockerで遊んでみようと思います！