DockerでつくるRails6 + Vue3 + PostgreSQLの開発環境

# はじめに
ここまで以下の記事のとおり、**Rails6 + PostgreSQL**、**Vue3**の開発環境をDockerでそれぞれ構築してきました。

https://qiita.com/subun33/items/822c236e97e8ee85f664

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

今回は、それぞれ構築したこの開発環境をDocker Composeを使って、まとめて管理できるようにしていきたいと思います。

# 目次
[1. ディレクトリの構成](#1-ディレクトリの構成)
[2. 完成したファイル](#2-完成したファイル)
[3. front/Dockerfileの修正](#3-frontdockerfileの修正)
[4. dockercompose.ymlの修正](#4-dockercomposeymlの修正)
[5. 動作確認](#5-動作確認)
[6. さいごに](#6-さいごに)

# 1. ディレクトリの構成
準備したファイルは以下のとおり、各ディレクトリに配置しています。
```
.
├── api
│   ├── Gemfile
│   ├── Gemfile.lock
│   ├── Dockerfile
│   └── entrypoint.sh
├── front
│   └── Dockerfile
└── dockercompose.yml
    
```

# 2. 完成したファイル
今回は最初に完成形のファイルをお示しした上で、前回までの記事で作成したファイルをどのように考え、どのように修正したかという点について、述べていきたいと思います。

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

gem 'rails', '~> 6.1'

```

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

```docker:./api/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"]
```

```sh:.api/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:./front/Dockerfile
FROM node:16.16

RUN apt-get update -qq && yarn global add @vue/cli

WORKDIR /app
COPY . /app

CMD ["yarn", "serve"]
```

```yaml:./dockercompose.yml
version: "3.9"
services:
  db:
    image: postgres
    volumes:
      - postgresql_data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password

  api:
    build: ./api
    volumes:
      - ./api:/app
    ports:
      - "3000:3000"
    depends_on:
      - db

  front:
    build: ./front
    volumes:
      - ./front:/app
    ports:
      - "8080:8080"

volumes:
  postgresql_data:

```

# 3. front/Dockerfileの修正
[前回の記事](https://qiita.com/subun33/items/b051f5697482203aa31d)で作成していたDockerfileでは、最後のコマンドを
```Dockerfile
CMD ["/bin/bash"]
```
としていましたが、次のように修正しました。
```Dockerfile
CMD ["yarn", "serve"]
```

Dockerfileの`CMD`はコンテナ起動時に実行するコマンドです。
[前回の記事](https://qiita.com/subun33/items/b051f5697482203aa31d)では、コンテナ内でコマンドを実行してVueのインストールなどを試していたので、bashを起動させるようにしていましたが、実際に開発を進める時は、コンテナを起動させるとVueプロジェクトのサーバーに立ち上がってほしいので、上記のように修正しました。

# 4. dockercompose.ymlの修正
今回、先に作成していたRails6 + PostgreSQLの環境にVue3を追加するにあたって、主に修正したのはdockercompose.ymlとなります。

もともと作成していたdockercompose.ymlは次のとおりです。
```yaml:dockercompose.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
```

### dbコンテナのvolumes
以前の設定では、DBのデータはホスト側の./tmp/dbにマウントしていましたが、`volumes`を使ってDocker側で管理することにしました。
Docker側で管理することのメリットは[公式](https://matsuand.github.io/docs.docker.jp.onthefly/storage/volumes/#:~:text=%E3%83%9C%E3%83%AA%E3%83%A5%E3%83%BC%E3%83%A0%E3%81%AF%E3%83%90%E3%82%A4,%E5%AE%9F%E7%8F%BE%E3%81%97%E3%81%BE%E3%81%99%E3%80%82)で以下のとおり説明されています。
>- ボリュームはバインドマウントよりも、バックアップや移行が容易です。
>- ボリュームは Docker CLI コマンドや Docker API を利用して管理することができます。
>- ボリュームは Linux と Windows 上のコンテナーにおいて動作します。
>- ボリュームは複数コンテナー間にて安全に共有できます。
>- ボリュームドライバーを用いると、リモートホスト上、あるいはクラウドプロバイダー上のボリュームに保存できるようになります。保存の際にはボリューム内データを暗号化することができ、その他にも種々の機能を利用することができます。
>- ボリュームを新たに生成すると、その内容はコンテナーがあらかじめ用意していた内容になります。
>- Docker Desktop 上のボリュームは、Mac や Windows ホストからのバインドマウントに比べて、より高い性能を実現します。

次のとおり`volumes`を作成、作成した`volumes`へのマウントを行っています。
```yaml
# volumesを作成
volumes:
  postgresql_data:
```
```yaml
# volumesへのマウント
 volumes:
      - postgresql_data:/var/lib/postgresql/data
```


### apiコンテナのcommandを削除
dockercompose.ymlから次の箇所を削除しました。
```yaml
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
```

dockercompose.ymlの`command`は`docker compose`でコンテナを起動したとき、Dockerfileの`CMD`を上書きして実行されます。

そのため、同じことを書いているのであれば、Dockerfileの
```yaml
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"]
```
と、dockercompose.ymlの
```yaml
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
```
はいずれか一方でいいのではないかと考えました。

開発時のコンテナの起動は`docker compose`使うつもりだったので、当初は「dockercompose.ymlに書いてあったら十分だな。Dockerfileを修正しよう。」と考えていましたが、[公式](https://docs.docker.jp/engine/reference/builder.html#cmd-entrypoint:~:text=Dockerfile%20%E3%81%AF%E3%80%81%E5%B0%91%E3%81%AA%E3%81%8F%E3%81%A8%E3%82%821%E3%81%A4%E3%81%AE%20CMD%20%E3%81%8B%20ENTRYPOINT%20%E5%91%BD%E4%BB%A4%E3%82%92%E6%9B%B8%E3%81%8F%E3%81%B9%E3%81%8D%E3%81%A7%E3%81%99%E3%80%82)で以下の記述を見つけたため、dockercompose.ymlを修正することにしました。
>Dockerfile は、少なくとも1つの CMD か ENTRYPOINT 命令を書くべきです。

この点については自分なりに調べてこの結論を出しましたが、Docker Composeを使うときもこの公式の記述は適用すべきなのかは自信が持てずにいます。
「dockercompose.ymlに書くべきだ」や「両方に書くべきだ」という意見がありましたら、ぜひコメントをお願いします。

### frontコンテナの追加
本来はここがメインになるはずでしたが、いざやってみると、
- `build`でDockerfileのパスを指定
- `volumes`でマウント先の設定
- `ports`でコンテナの8080ポートをホストの8080ポートに公開
ということを淡々と設定したのみであっさりと完了しました。

## 5. 動作確認
まずはDockerのイメージを作成→Railsアプリを作成→Gemfileが更新されたのでイメージの再作成をします。
```shell
$ docker compose build
$ docker compose run --rm --no-deps api rails new . -f -T --api --database=postgresql
$ docker compose build
```

次にdatabase.ymlの設定をして、DBを作成します。
```yaml:database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: api_development


test:
  <<: *default
  database: api_test
```


```shell
$ docker compose run --rm api rails db:create        
```

これでRailsアプリの作成は完了したので、次にVueプロジェクトを作成します。
```shell
$ docker compose run --rm front vue create .

Vue CLI v5.0.8
? Generate project in current directory? (Y/n) Y
? Please pick a preset: (Use arrow keys)
❯ Default ([Vue 3] babel, eslint) 
? Pick the package manager to use when installing dependencies: (Use arrow keys)
❯ Use Yarn 
```

さて、準備ができたの、コンテナを起動させます。
```shell
$ docker compose up
```

[localhost:3000](http://localhost:3000/)、[localhost:8080](http://localhost:8080/)にアクセスすると、それぞれのウェルカムページが出迎えてくれます。

無事、DockerでRails6 + Vue3 + PostgreSQLの開発環境を構築することができました！

### おまけ
Rails、Vueのウェルカムページが出迎えてくれたので、大丈夫な気がしつつも、本当にこれで開発を進めていける環境が構築できたのか少し不安な気持ちもありました。
なので、以前、某教材で作成したRails + Vueのリアルタイムチャットアプリにこの開発環境を導入してみることにしました。
アプリ内に、今回作成したDocker関連のファイルを配置して、コンテナを起動させてみます。

…特に問題なく動作する。大丈夫そうだ。

## 6. さいごに
これで長かった環境構築も一段落しました。
ここから先は実際に開発を進める中で必要に応じて、変更を加えていきたいと思います。

一方で、まだDockerを使ってやってみたいなと思うことは残っています。
今、関心があるのは、ベースイメージにslimを使って自分で必要なライブラリをインストールし、イメージサイズを抑えるということです。

こんな風に実際に手を動かす中で関心のあることは増えていくのですが、すべてに手を出していたら時間がいくらあっても足りません。
やっぱり、目的意識を持つことが、学習効率をとっても、時間効率をとっても重要だなとしみじみと感じているところです。