DockerでRails 6 + postgresの開発環境構築

DockerでRails6の開発環境を構築するときにやったこと。
# 対象
dockerについてなんとなく知っていて、これからrails,postgreでwebアプリの開発環境を用意しようとしている方向けです。
自分の備忘録も兼ねて書いてみました。

# 環境
ホスト環境
macOS Caterina 10.15.5
Docker version 20.10.2

構築しようとする環境
ruby 2.7
postgresql 10.7
rails 6.1.1

# rails用のDockerfileの作成
Dockerfileの作成します。DockerfileとはDockerイメージを実行する際に実行するコマンドをまとめたものです。

```
$ touch Dockerfile
```
上記で作成したDockerfileに以下のコマンドをお好きななエディタで記述していきます。
まずはベースとなるイメージを設定します。ベースはruby2.7のイメージを使用し、nodeのイメージは後ほど使用します。
また、環境変数LANGを設定します。

```:Dockerfile
FROM node:14.10 AS node
FROM ruby:2.7
ENV LANG "C.UTF-8"
```
mysqlを削除して、postgresqlをインストールします。

```:Dockerfile
RUN apt-get update -qq && \
  apt-get remove -y --purge mysql\*  && \
  apt-get install -y build-essential libpq-dev postgresql-client && \
  rm -rf /var/lib/apt/lists/*
```
コンテナの起動順を制御するために、dockerizeをインストールします。

```:Dockerfile
ENV DOCKERIZE_VERSION v0.6.1
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
  && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
  && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz
```
コンテナ起動時に実行するコマンドを指定するためにentrykitをインストールします。

```  :Dockerfile
ENV ENTRYKIT_VERSION 0.4.0
RUN wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && mv entrykit /bin/entrykit \
  && chmod +x /bin/entrykit \
  && entrykit --symlink
```
日本語フォントのインストールをします。

```:Dockerfile
RUN cd /tmp && \
  mkdir noto && \
  curl -O -L https://noto-website-2.storage.googleapis.com/pkgs/NotoSansCJKjp-hinted.zip && \
  unzip NotoSansCJKjp-hinted.zip -d ./noto && \
  mkdir -p /usr/share/fonts/noto && \
  cp ./noto/*.otf /usr/share/fonts/noto/ && \
  chmod 644 /usr/share/fonts/noto/*.otf && \
  fc-cache -fv && \
  rm -rf NotoSansCJKjp-hinted.zip ./noto
```
yarnとnodeをnodeイメージからコピーし、シンボリックリンクの設定を行います。

```:Dockerfile
ENV YARN_VERSION 1.22.5

COPY --from=node /opt/yarn-v$YARN_VERSION /opt/yarn
COPY --from=node /usr/local/bin/node /usr/local/bin/

RUN ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \
  && ln -s /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg
```
ワークディレクトリを作成します。

```:Dockerfile
ENV APP_ROOT "/hoge_app"
RUN mkdir $APP_ROOT
WORKDIR $APP_ROOT
```
上記でインストールしたentrykitのprehookという機能を使用し、コンテナ起動前に時刻したいコマンドを設定します。

```:Dockerfile
ENTRYPOINT [ \
  "prehook", "ruby -v", "--", \
  "prehook", "dockerize -timeout 60s -wait tcp://hoge_postgresql:5432", "--", \
  "prehook", "bundle install -j4 --quiet", "--", \
  "prehook", "yarn install --ignore-optional", "--" ] 
```
# postgresql用のDockerfileの作成
Dockerfileの作成を行います。

```
$ mkdir docker
$ mkdir docker/db
$ touch docker/db/Dockerfile
```
postgresqlのイメージをベースとして使用します。さらに言語の設定をします。

```:docker/db/Dockerfile
FROM postgres:10.7
RUN localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8
ENV LANG ja_JP.utf8
```

# docker-compose.ymlの作成
docker-compose.ymlを作成します。
Docker composeとはDockerのビルドや複数のコンテナの起動を簡単なコマンドで行えるようしたものです。docker-compose.ymlにdockerイメージをビルドするために使用するDockerfileの情報や起動するコンテナの情報などを記述します。

```
$ touch docker-compose.yml
```
上記で作成したdocker-compose.ymlに以下の内容を記述していきます。
railsが動くコンテナの設定をします。ここではDockerfileのパス(build)や、実行するコマンド(command)、環境変数(environment)、永続化するディレクトリ(volumes)、ポート番号(ports)を設定します。depends_onは起動順を指定します。このコンテナはpostgresqlコンテナの後に起動します。コンテナの外から入出力できるようにするためにstdin_openとttyをtrueに設定します。
ちなみにvolumesで設定した永続化するディレクトリはローカル環境をマウントすることによって実現されています。ここでcashedというオプションを使用している部分がありますが、これはMacでdockerを動かし開発する際に、ボリュームをマウントする際に動作が非常に遅くなるという問題を解決するために使用しています。ここで注意しなければいけないのは、cashedを使用したボリュームはホストからの見え方は信頼できますが、コンテナからの見え方は遅延が発生します。

```:docker-compose.yml
version: '3'
services:
  rails: &app_base
    build: .
    container_name: hoge_rails
    command: ["rails", "s", "-p", "3000", "-b", "0.0.0.0"]
    environment:
      RAILS_ENV_NAME: development
      DATABASE_HOST: hoge_postgresql
      WEBPACKER_DEV_SERVER_HOST: webpack
      WEBPACKER_DEV_SERVER_PUBLIC: webpack:3035
    depends_on:
      - postgresql
    ports:
      - "127.0.0.1:3000:3000"
    stdin_open: true
    tty: true
    volumes:
      - .:/hoge_app:cached
      - node_modules:/hoge_app/node_modules
      - bundle:/usr/local/bundle
      - vendor
      - tmp
      - log
      - .git
```
webpackerが動くコンテナを設定します。Dockerfileはrailsコンテナと同じものを使用しています。

```:docker-compose.yml
webpack:
    <<: *app_base
    container_name: hoge_webpack
    command: "bin/webpack-dev-server"
    environment:
      NODE_ENV: development
      WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
    ports:
      - '127.0.0.1:3035:3035'
    depends_on:
      - rails
    tty: false
    stdin_open: false
```
postgresqlが動くコンテナを設定します。

```:docker-compose.yml
postgresql:
    build:
      context: docker/db
    container_name: hoge_postgresql
    ports:
      - 5432:5432
    volumes:
      - ./docker/db/volumes:/var/lib/postgresql/data:cached
    environment:
      POSTGRES_USER: root
      POSTGRES_PASSWORD: root
    hostname: postgres
    restart: always
    user: root
```
# ビルドしてコンテナを立ち上げる
作成したdocker-compose.ymlを使用してビルドを行います。 

```
$ docker-compose build
```
# rails環境を構築
Gemfileを作成し以下を記述します。

```
$ touch Gemfile
```

```:Gemfile
source 'https://rubygems.org'
gem 'rails', '~>6'
```
rails newをします。途中、Gemfileを上書きするか聞かれるのでyと入力します。

```
$ docker-compose run rails rails new . --database=postgresql
```
config/database.ymlを書き換えます。

```:config/datebase.yml
default: &default
  adapter: postgresql
  charset: UTF8
  encoding: UTF8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV.fetch('DATABASE_USERNAME', 'root') %>
  password: <%= ENV.fetch('DATABASE_PASSWORD', 'root') %>
  host: <%= ENV.fetch('DATABASE_HOST', 'hoge_postgresql') %>
  database: <%= ENV['DATABASE_NAME'] %>

development:
  <<: *default
  database: hoge_app_development

  
test:
  <<: *default
  database: hoge_app_test

production:
  <<: *default
  database: hoge_app_production
  username: hoge_app
  password: <%= ENV['HOGE_APP_DATABASE_PASSWORD'] %>
```
DBを作成します。

```
$ docker-compose run rails rails db:create
```
コンテナを立ち上げます。

```
$ docker-compose up -d
```
ブラウザでlocalhost:3000を開きます。
「Yay! You’re on Rails!」が表示されれば成功です。


# 参考にしたサイト
https://qiita.com/minamijoyo/items/711704e85b45ff5d6405
http://www.tohoho-web.com/docker/dockerfile.html
https://laptrinhx.com/rails6nodocker-compose-huan-jingwowebpackermade-hanmete-gou-zhusuru-2906065143/
https://www.pressmantech.com/tech/6522
https://hfuji.hatenablog.jp/entry/2019/06/01/102617