docker-composeによるRails6(APIモード)+MySQLのDocker環境作成(for Mac)

# はじめに
バイト先で全面改修を進めていく上で、環境をdocker化しようという話が出ました。
フロントエンドNuxt.js、バックエンドRails6の構成で作ることになり、バックエンド部分を自分が担当することになったのでDockerfileをごりごり書いていきましょー！ということで勉強も兼ねて実装し、出来上がった構成を紹介します。
Railsコンテナの作り方は、<a href="https://docs.docker.com/compose/rails/">Dockerの公式ページ</a>に簡単に書いてありますが、今回はRails6に考慮したDockerfileの書き方と、MySQLコンテナの作り方も合わせて紹介していこうと思います。

# 自身のスキル
個人でrailsを触り始めたのは1年半ほど前、webエンジニアとしての実務での開発経験は1年強と期間的には長くはありません。
しかし、大学でつけた幅広い前提知識や、どんどん色々なことに挑戦する姿勢を利用して素早く成長しています。
前のプロジェクトでもdocker周りには触らせていただいていて、データベースのコンテナの変更(MarinaDB->MySQL)や、初期設定用のシェルスクリプトの作成などを行っていました。
自分で1からdockerを作る機会がなかったので今回挑戦した形です。

# docker環境作成
ここから先は実際にdockerによる環境を作っていきます。
## ファイル構成
まずは簡単にファイル構成を紹介します。
作成するアプリケーションの名前を`sample`とすると、今回関係するファイルは以下のようなファイル構成になっています。

```
sample 
|- backend --- app
|           |- bin
|           |- config --- ...
|           |          |- database.yml
|           |          |- ...
|           |- ...
|           |- Dockerfile
|           |- Gemfile
|           |- Gemfile.lock
|           |- ...
|- db --- data --- ...
|      |- my.cnf
|- docker-compose.yml
```

実際はもっと多くのファイルが存在する(作成される)ことになりますが、今回操作するファイルはこれらのファイルのみになります。

## 必要ファイルの作成
続いてdockerに関するファイルを記述していきます。
### backend/Dockerfile
これはRailsのコンテナをビルドするための手順を記したファイルになります。内容を下に載せ、それぞれどのような処理を行っているのかをコメントで説明しています。

```dockerfile:backend/Dockerfile
# 使用するイメージとバージョン
FROM ruby:2.7.1

# 必要なライブラリをインストール
RUN apt-get update -qq && \
    apt-get install -y build-essential \ 
                       libpq-dev \        
                       nodejs

# 以下はrails6以降(APIモード除く)で必要
RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
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 && apt-get install -y yarn

# コンテナ内にappというフォルダを作成
# ※ローカルPCにフォルダが作成されるわけではない
RUN mkdir /app
# ルートをappディレクトリに変更
ENV APP_ROOT /app
WORKDIR $APP_ROOT

# ローカルPC内のGemfile(.lock)をコンテナ内にコピー
ADD ./Gemfile $APP_ROOT/Gemfile
ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock

# bundle installを実行しローカルPCのファイルたちをコンテナ内にコピー
RUN bundle install
ADD . $APP_ROOT

# コンテナがlistenするポート番号
EXPOSE 3000

# 実行されるコマンド
CMD ["rails", "server", "-b", "0.0.0.0"]
```

rails6からはWebpackerがデフォルトとなったので、`yarn`がインストールされていないと`rails new`をするときにエラーとなってしまいます。
ただし、後述のAPIモードで作成する場合には`yarn`のインストールは不要です。

### backend/Gemfile
Railsをインストーするためにあらかじめ`Gemfile`を記述しておきます。
このファイルは後ほど`rails new`をするときに書き換えられます。
ここではrails6以降のバージョンを指定しています。

```ruby:backend/Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 6'
```

### backend/Gemfile.lock
`bundle install`した際に依存関係を元にライブラリの設計図のようなものを作ってくれます。
空のものを作成しておきます。ファイルが存在しないと後でエラーになりました。

```ruby:backend/Gemfile.lock
```

### db/data/
ディレクトリだけ用意しておいてあげましょう

### db/my.cnf
MySQLの設定に関するファイルです。

```my.cnf
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
sql_mode=''

[client]
default-character-set=utf8mb4
```

文字コードの設定や`sql_mode`の設定を行っています。
特に`sql_mode`の設定はしておいた方が良いと思います。
というのも、MySQL5.7からデフォルトの`sql_mode`が変更となっており、特に`only_full_group_by`などのモードで度々エラーを吐いてしまうことがあるので、どの`sql_mode`にするかは明示的に書いておきましょう。今回は設定なし(MySQL5.6.5以前のデフォルト)を明示的に指定しています。
(この辺りでも追々記事が書きたい)

### docker-compose.yml
いよいよdocker-composeを書きます。
簡単な説明をコメントで記述してあります。

```yml:docker-compose.yml
version: "3"
services:
  # MySQL
  db:
    #ビルドするイメージ
    image: mysql:5.7
    # 環境変数の指定
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: spportunity
      MYSQL_USER: root
      MYSQL_PASSWORD: root
      TZ: 'Asia/Tokyo'
    # 文字コードをutf8mb4に設定
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:
      # db/dataディレクトリをコンテナのmysqlディレクトリにマウント
      - ./db/data/:/var/lib/mysql
      # /db/my.cnfファイルをコンテナ内のmy.cnf設定ファイルにマウント
      - ./db/my.cnf:/etc/mysql/conf.d/my.cnf
    ports:
      - 3306:3306

  #Rails  
  backend:
    # ビルド元の指定
    build:
      context: ./backend
      dockerfile: Dockerfile
    # コンテナ名の指定
    container_name: "sample-backend"
    # 起動時のコマンド指定 前のプロセスを終了してからrails sをしている
    command: bash -c "rm -f tmp/pids/server.pid && rails s -p 3000 -b '0.0.0.0'"
    # backendフォルダ内のファイルをコンテナ内のappディレクトリにマウント
    volumes:
      - ./backend/:/app
    ports:
      - "3000:3000"
    # dbコンテナよりも後に起動
    depends_on:
      - db
```

これでファイルの準備はできたのでrailsアプリケーションを作成していきます。

## railsアプリケーションの作成
`docker-compose`コマンドを利用してrailsアプリケーションを作成します。
現在のディレクトリが`sample`であることを確認してください。

### 通常モードでrailsを作成(dockerfileでyarnのinstallが必要)

```
% docker-compose run backend rails new . --force --no-deps --database=mysql --skip-bundle
```

- `--force`...`Gemfile`を書き換えます
- `--database=mysql`...データベースにMySQLを指定します
- `--skip-bundle`...`bundle`をスキップします(まだ`Gemfile.lock`は変更されません)

### APIモードでrailsを作成

```
% docker-compose run backend rails new . --force --no-deps --database=mysql --skip-bundle --api
```

- `--api`...APIモードでの作成オプション

完了すると、`backend/`フォルダ内にrails関連のフォルダやファイルが大量に出来上がります。
APIモードと通常モードの違いやファイルの差分はまた記事にします。

## コンテナのビルド

```
% docker-compose build
```

このコマンドを実行することで、MySQLのコンテナの作成と、railsコンテナでの`bundle install`が行われます。
コンソールを見ているとDockerfileの内容を順番に実行している様子が分かるかと思います。

## railsのdbへの接続設定

```yml:backend/config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: root
  host: db

development:
  <<: *default
  database: sample


test:
  <<: *default
  database: sample_test

production:
  <<: *default
  database: sample_production
  username: <%= ENV['APP_DATABASE_USER'] %>
  password: <%= ENV['APP_DATABASE_PASSWORD'] %>

```
接続情報を、hostを先ほど作成したMySQLコンテナのイメージ、ユーザー名とパスワードを先ほど作成したコンテナの環境変数のものに変更します。

## (APIモードでなければ)Webpackerのインストール

```
% docker-compose run backend rails webpacker:install
```

## データベースの作成

```
% docker-compose run backend rails db:create
```

## dockerの起動
ここまで来れば設定は完了です。
あとはdockerを起動するのみです！

```
% docker-compose up
```

## ブラウザでRailsが立ち上がっていること確認
ブラウザで http://localhost:3000/ へアクセスすると、無事Railsが立ち上がっています。
<img width="841" alt="スクリーンショット 2020-07-15 21.24.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674831/c125550b-e663-e7df-3f04-d97e29780373.png">

## 拡張性
今回は言及しませんでしたが、frontendコンテナなどを追加することによってフロントエンドアプリケーションとAPIサーバを兼ね備えた環境を一発で作ることができるようになります。
その際は、同様の手順でDockerfileの作成やdocker-composeへのコンテナの追加などを行っていくことになりまね。
僕はまだフロントエンド初心者なので、ゆくゆくはフロントのコンテナも作っていきたいです。

# 感想
やっぱりdockerは便利ですね。
自分個人の開発ではdockerは使っていませんでしたが、これからは積極的に使っていこうかなと思っています。
またその際には記事にしてあげていければと思います。

qiitaの記事を書くのが初めてで、どれくらいのものを書けばいいのか悩みながら書いていたら、だいぶ長くなってしまいました。
ただ、とても内容が濃い記事を何本も書いている人がたくさんいて、自分はまだまだだなぁと日々感じています。
僕はまだ内容があるものは頻繁には書けないかもしれませんが、少しずづ知識を記事化していければと思います。

# 参考にしたもの
以下のqiitaの記事を参考にさせていただきました。
書き方も真似させていただき、かなり参考になりました。
ありがとうございます。

https://qiita.com/azul915/items/5b7063cbc80192343fc0
https://qiita.com/kodai_0122/items/795438d738386c2c1966

上記の記事に加えて、APIモードやMySQLコンテナのマウントや設定なども盛り込んだ内容となっているので、書き方が似ている点はご容赦いただければと思います。