Rails API + MySQL + docker-composeの環境構築

DBを`MySQL`にした`Rails API`環境を`docker-compose`を使って構築しました。

<br>

### 前提
Docker & docker-composeをインストール済み

### 動作環境
- macOS Catalina 10.15.7
- Ruby 2.7.1
- Ruby on Rails 6.0.3
- MySQL 8.0.23
- Docker 20.10.2
- docker-compose 1.27.4

### ディレクトリ構成
最終的なディレクトリ構成は以下です。MySQLのデータを格納する`mysql`と、`rails`内の各ディレクトリ以下の内容は省略しています。

```
rails_api_mysql
  ├── docker-compose.yml
  ├── db
  └── app
      ├── Dockerfile
      ├── Gemfile
      ├── Gemfile.lock
      ├── README.md
      ├── Rakefile
      ├── app
      ├── bin
      ├── config
      ├── config.ru
      ├── db
      ├── lib
      ├── log
      ├── public
      ├── storage
      ├── test
      ├── tmp
      └── vendor
```

<br>

### 手順
プロジェクトを作成します。

```
mkdir rails_api_mysql
cd rails_api_mysql
```

<br>

`docker-compose.yml`を作成します。

```yml:docker-compose.yml
version: '3'
services: 
  db:
    image: mysql:8.0.23
    environment: 
      MYSQL_ROOT_PASSWORD: password
    volumes: 
      - ./mysql:/var/lib/mysql
    command: ["--default-authentication-plugin=mysql_native_password"]
    ports: 
      - 3306:3306
  app:
    build: rails
    volumes: 
      - ./rails:/usr/src/app
    command: ["rails", "server", "-b", "0.0.0.0"]
    ports: 
      - 3000:3000
    depends_on:
      - db
```
MySQLのデータをホスト側で管理するために、`services` の `db`に`volume`を指定しています。

<br>

MySQLのデータを格納するための`db`ディレクトリを作成します。中身は空で大丈夫です。

```
mkdir db
```

<br>

Railsアプリケーションを格納するための`app`ディレクトリを作成し、その中に`Gemfile` `Gemfile.lock`  `Dockerfile`を作成します。

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

gem 'rails', '6.0.3'
```

```text:/app/Gemfile.lock
空で大丈夫です
```

```dockerfile:/app/Dockerfile
FROM ruby:2.7.1-alpine3.11

ENV BUNDLER_VERSION=2.1.4

WORKDIR /usr/src/app

COPY Gemfile .
COPY Gemfile.lock .

RUN apk update && \
    apk add --no-cache \
    shared-mime-info \
    linux-headers \
    libxml2-dev \
    curl-dev \
    make \
    gcc \
    libc-dev \
    g++ \
    build-base \
    mariadb-dev \
    tzdata && \
    gem install bundler && \
    bundle install

COPY . .

EXPOSE 3000
```

<br>

`rails new`を実行し、アプリケーションを作成します。これで`rails`ディレクトリに各ファイルが生成されます。

```
docker-compose run --rm --no-deps app rails new . --force --database=mysql --skip-keeps -M -C -S -J -B
```

<br>

`/app/config/database.yml`を編集します。

```yml:database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password # docker-compose.ymlのMYSQL_ROOT_PASSWORDと同じにします
  host: db # docker-compose.ymlのservicesのdbのことです

development:
  <<: *default
  database: app_development

test:
  <<: *default
  database: app_test

production:
  <<: *default
  database: app_production
  username: app
  password: <%= ENV['APP_DATABASE_PASSWORD'] %>
```

<br>

dockerイメージをもう1度ビルドします。上記の`rails new`を実行したことにより`Gemfile` `Gemfile.lock`の内容が変わっているためです。

```
docker-compose build
```

<br>

コンテナを起動します。

```
docker-compose up
```

<br>

コンテナにアクセスし、DBを構築します。

```
docker-compose run app rails db:create
```

<br>

`localhost:3000`にアクセスし、起動画面が表示されれば成功です。

![スクリーンショット 2021-03-25 1.39.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1016415/b178bbbf-28b6-d115-f285-7e1d3bd5b09e.png)


<br>

### 動作確認
MySQLが正常に動作しているか確認します。

コンテナにログインします。

```
docker-compose exec app sh
```

<br>

`controller` と `model`を作成します。

```
rails g controller Users index --no-template-engine --no-test-framework --no-assets
```

```
rails g model User name:string --no-test-framework
```

<br>

`/db/seeds.rb`に以下を追記してテストデータを定義します。

```ruby:seeds.rb
User.create name: "Pythagoras"
```

<br>

DBを初期化し、テストデータを反映させます。

```
rails db:migrate
rails db:seed
```

<br>

`/app/controllers/users_controller.rb`のindexアクションを以下に変更します。

```ruby:users_controller.rb
def index
  @users = User.all
  render json: @users
end
```

<br>

`localhost:3000/users/index`にアクセスすると以下の様に表示されるはずです。

![スクリーンショット 2021-03-26 12.45.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1016415/37e54f3c-ee73-4302-216b-de87244f4c97.png)

<br>

コンテナを1度破棄し、再び起動します。

```
docker-compose down
```

```
docker-compose up
```

<br>

もう1度`localhost:3000/users/index`にアクセスして以下の様に表示されれば成功です。ちゃんとホスト側のMySQLデータが読み込まれていますね。

![スクリーンショット 2021-03-26 12.45.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1016415/37e54f3c-ee73-4302-216b-de87244f4c97.png)

<br>

読んでいただきありがとうございました！ご指摘やご意見などありましたらコメントしていただけると嬉しいです🐳