【Rails6/MySQL5】Docker環境とローカル環境両方でデータベースを使えるようにする方法


※2022年から技術系の記事は[個人ブログ](https://yamaday0u.net/category/programmer/)に投稿しております。ぜひこちらもご覧ください→[yamaday0u Blog](https://yamaday0u.net/category/programmer/)

以前書いた以下の記事の関連で、Docker環境とローカル環境両方でデータベースを使えるようにする設定について紹介します。
[【Rails6/MySQL5】既存アプリにDocker環境を構築した方法（Capistrano・Amazon S3にも対応）](https://qiita.com/yamaday0u/items/587809996336f6bd993d)

## 解決したいこと
`rails s`コマンドを使いローカル環境でサクッとデータベースの動作を確認したい。

基本はdockerで立ち上げたコンテナ環境で動作確認しているけど、ちょこっとデータベースの動きを確認をするためだけにdockerを立ち上げるのが面倒と感じている。

## 開発環境・前提

- Ruby 2.6.5
- Rails 6.0.3.6
- Docker 20.10.6
- docker-compose 1.29.1
- mysql 5.6.50

## docker-compose.ymlの記述

```docker-compose.yml
#docker-composeのバージョン
version: '3'
services:
  db:
    # 既存アプリとあわせる/ターミナルで[$ mysql --version]で確認
    image: mysql:5.6.50
    # 環境変数の設定
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: root
    # ポート番号を指定/[ローカル:コンテナ]
    ports:
      - "4306:3306"
    # 名前付きボリュームでDBのデータを永続化
    # [データボリューム名:コンテナ内のパス]
    volumes:
      - mysql_data:/etc/mysql/conf.d
  web:
    # Dockerfileのパスをカレントディレクトリに指定
    build: .
    # server.pidファイルを削除してからrailsサーバー起動
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    # 環境変数の設定
    environment:
      - MYSQL_HOST=db
      - MYSQL_PASSWORD=password
    # ディレクトリのマウント
    volumes:
      - .:/group_calendar
    ports:
      # ポート番号を指定/[ローカル:コンテナ]
      - 3000:3000
    # サービス間の依存関係を指定
    # この場合、db → webの順でサービスを起動
    depends_on:
      - db
    # コンテナを起動させ続けるための設定
    # make container stay launched
    tty: true
    # stdinはstandard input（標準入力）の略
    # コンテナの外から命令を送るための設定
    stdin_open: true
volumes:
  # db:volumes:で作成したボリューム名を記述
  mysql_data:
```

特に特別な記述はしていないのですが、注目ポイントは`services:db:environment:`以下の環境変数です。
後述の`database.yml`での記述に活かされます。

```docker-compose.yml
services:
  db:
    # 環境変数の設定
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: root
```

## database.ymlの記述

```config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: <%= ENV['MYSQL_PASSWORD'] || '' %> # 記述箇所
  socket: /tmp/mysql.sock
  host: <%= ENV['MYSQL_HOST'] || 'localhost' %> # 記述箇所
```

### 解説
#### 概要
`password`と`host`のところの記述が肝です。

`||`は比較演算子で、左側の値が真であればその値を返し、偽であれば右側の値を評価してその値を返します。
[Ruby 3.0.0 リファレンスマニュアル  演算子式 or](https://docs.ruby-lang.org/ja/latest/doc/spec=2foperator.html#or)

左側の環境変数`MYSQL_PASSWORD`と`MYSQL_HOST`は`docker-compose.yml`で定義した環境変数です。

右側は`rails s`コマンドでローカル環境を立ち上げるときに使用されるデータベースの`host`と`password`の値です。

#### 補足：順番も大事
左側にDocker環境用の変数を置き、右側にローカル環境用の値を置いていることにも意味があります。

もし、以下のように左側にローカル環境用の値を記述してしまうと、たとえ`docker-compose up`でDocker環境を立ち上げたとしても、ローカル環境用のデーターベースに接続しようとしてしまい、接続エラーを起こしてしまいます。

```config/database.yml
default: &default
  password: <%= '' || ENV['MYSQL_PASSWORD'] %> # 左右の順番を入れ替え
  host: <%= 'localhost' || ENV['MYSQL_HOST'] %> # 左右の順番を入れ替え
```
[![Image from Gyazo](https://i.gyazo.com/3f0c5889d2213b1429b08eaf1c885925.png)](https://gyazo.com/3f0c5889d2213b1429b08eaf1c885925)

その理由はDocker環境の外にあるローカル環境のデータベースに接続することができないからです。

これを回避するために、Docker環境用のデータベースに接続するための記述を比較演算子の左側に記述する必要があります。

`rails s`でローカル環境を立ち上げる場合は、ローカル環境に環境変数`MYSQL_PASSWORD`と`MYSQL_HOST`の値がなく「偽」となるため、右側の値が返されるという仕組みになっています。

## 参考資料

- [docker導入後のMysql2::Error::ConnectionError (Unknown MySQL server host 'db' (0))}（teratail）](https://teratail.com/questions/321052)
- [【database.yml】DBをDockerとrails server両方で使えるように設定（Qiita）](https://qiita.com/waniwaninowani/items/21d677a6c455e5b76d45)
- [Ruby 3.0.0 リファレンスマニュアル  演算子式 or](https://docs.ruby-lang.org/ja/latest/doc/spec=2foperator.html#or)