DockerでRails + Vue + MySQLの環境構築をする方法【2020/09最新版】

# はじめに
フロントにVue、APIサーバにRailsという組み合わせは日本ではメジャーな構成の一つです。今回はこの構成をDockerを使って構築していきます。

この記事を読むことで、以下の構成をDocker作成出来るようになります！
API： **Rails ６系**
フロント：**Vue(TypeScript) 2系**
DB： **MySQL 5.7**

## 環境構築の流れ
1. Rails、VueのDockerfile作成
2. docker-compose.yaml作成
3. Rails、Vueのプロジェクト作成


最終的なディレクトリ構成は以下のようになります。

```
[project_name]
├── api // Rails
│   ├── Dockerfile
│   ├── ...
├── docker-compose.yaml
└── front // Vue
    ├── Dockerfile
    ├── ...
```

以下レポジトリです。
https://github.com/kyoppii13/vue-rails-mysql-docker-sample

では環境構築に入っていきましょう！

# 1. ディレクトリ作成
プロジェクトディレクトリとその配下にAPI、フロントのディレクトリを作成します

```bash
$ mkdir project_name
$ cd project_name
$ mkdir api front
```

以降のコマンドはカレントディレクトリがプロジェクトディレクトリ想定で記載しています。

# 2. Dockerfile作成
RailsとVueそれぞれのDockerfileを作成します。
## 2-1. Rails
指定しているバージョン
- Ruby 2.7.1
- Rails 6.0.x

```dockerfile:api/Dockerfile
FROM ruby:2.7.1

RUN apt-get update -qq && \
    apt-get install -y build-essential \ 
                       libpq-dev \        
                       nodejs \
   && rm -rf /var/lib/apt/lists/* 

RUN mkdir /app
ENV APP_ROOT /app
WORKDIR $APP_ROOT

ADD ./Gemfile $APP_ROOT/Gemfile
ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock

RUN bundle install
``` 
`rm -rf /var/lib/apt/lists/* `はaptのキャッシュを削除しています。これはDockerのイメージファイルサイズを軽量化するためです。

このDockerfileではRailsを含むGemfileを必要とするので、以下のファイルを`api/`に作成します。

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

空のGemfile.lockも作成します。

```bash
$ touch api/Gemfile.lock
```

この時点でのディレクトリ構成

```
[project_name]
├── api 
│   ├── Dockerfile   <- New!
│   ├── Gemfile      <- New!
│   └── Gemfile.lock <- New!
└── front
```

## 2-2. Vue
指定しているバージョン
- node 12.18.3
- Vue 2系 <- これは後述のコンテナ内でVueプロジェクトを作成する際に指定します

```Dockerfile:front/Dockerfile
FROM node:12.18.3-alpine

ENV APP_HOME /app
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME

RUN apk update && npm install -g @vue/cli
```

この時点でのディレクトリ構成

```
[project_name]
├── api 
│   ├── Dockerfile
│   ├── Gemfile
│   └── Gemfile.lock
└── front 
    └── Dockerfile <- New!
```

# 3. docker-compose.yaml作成
以下のdocker-compose.yamlをプロジェクトディレクトリに作成します

```yaml:docker-compose.yaml
version: '3'

services:
  web:
    build: ./api
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    ports:
      - '3000:3000'
    depends_on:
      - db
    volumes:
      - ./api:/app
      - bundle:/usr/local/bundle
    tty: true
    stdin_open: true
  db:
    image: mysql:5.7
    volumes:
      - mysql_data:/var/lib/mysql/
    environment:
      MYSQL_ROOT_PASSWORD: password
    ports:
      - '3306:3306'
  front:
    build: ./front
    volumes:
      - ./front:/app
    ports:
      - '8080:8080'
    tty: true
    stdin_open: true
    command: npm run serve

volumes:
  mysql_data:
  bundle:
```
Railsの開発サーバ起動時に`-b`でホストを指定します。省略するとホストからコンテナにアクセスできません。`0.0.0.0`を指定することで、コンテナが持つ全てのインターフェースでlistenできるようになるため、ホスト側からコンテナにアクセスできるようになります。

MySQLとbundleのデータはボリュームにマウントし永続化しています。これによってコンテナを削除しても、データは消えません。
bundleはマウントしなくても、Gemを追加するたびにイメージのビルドすれば良いのですが時間がかかります。bundleをマウントすることでGemの追加が`docker-compose run api bundle install`で済むようにです。

この時点でのディレクトリ構成

```
[project_name]
├── api 
│   ├── Dockerfile
│   ├── Gemfile
│   └── Gemfile.lock
├── docker-compose.yaml <- New!
└── front 
    └── Dockerfile
```

# 4. プロジェクトの作成
## 4-1. Rails
**Railsプロジェクトの作成**
`rails new`でRailsプロジェクトを作成します

```bash
$ docker-compose run web rails new . --force --database=mysql --api
```
`rails new`の引数について
`--force`：Gemfileを強制的に上書き更新する
`--database`:使用するデータベースを指定する
`--api`：APIモードでプロジェクトを作成。APIモードではUIに関係するファイルが省略されます。

**CORS設定**
フロントとバックエンドでオリジンが異なるためCORSの設定が必要となります。

Gemfileの以下のコメントアウト部分を解除

```diff:api/Gemfile
# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
- # gem 'rack-cors'
+ # gem 'rack-cors'
```

その後、bundle install

```
$ docker-compose run web bundle install
```

CORS設定ファイルが生成されるので、許可するoriginsを指定。今回はフロントのポートを`8080`にしているので以下のように記述します。

```api/config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'http://localhost:8080'

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end
```


**dockerイメージ更新**
Gemfileが更新されたので、buildしてdocker imageを更新します。

```bash
$ docker-compose build
```

**DBの設定ファイルを修正**

RailsのDB設定ファイル`api/config/database.yml`を修正します。

```diff:api/config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
- password:
+ password: password
- host: localhost
+ host: db
```
- `password`はdocker-compose.yamlの環境変数`MYSQL_ROOT_PASSWORD`で指定したもの
- `host`はDBのサービス名
に対応しています。

**DBの作成**

```bash
$ docker-compose run web rails db:create 
```

これでRailsの環境構築は完了です！

## 4-2. Vue
**vue-cliでVueプロジェクトを作成**
コンテナ内に入り、vue-cliを使ってVueプロジェクトの作成を対話的にします。

以下の設定項目はvue-cli v4.5.6のものです。設定内容は一例なのでお好みでどうぞ。

Vueコンテナでシェルを実行

```bash
$ docker-compose run front sh
```

以下フロントコンテナ内で対話的に設定していきます。

```
$ vue create .
# 現在のディレクトリ(/app)に作成するかの確認
? Generate project in current directory? (Y/n) Yes

# プリセットを使用するかどうか
? Please pick a preset: (Use arrow keys)
  Default ([Vue 2] babel, eslint)
  Default (Vue 3 Preview) ([Vue 3] babel, eslint)
❯ Manually select features # TypeScriptをインストールするためこちらを選択

# プロジェクトにインストールするライブラリの選択
? Check the features needed for your project:
 ◉ Choose Vue version #
 ◉ Babel
❯◉ TypeScript # TypeScriptをインストールする場合はこれを選択
 ◯ Progressive Web App (PWA) Support
 ◯ Router
 ◯ Vuex
 ◯ CSS Pre-processors
 ◉ Linter / Formatter
 ◯ Unit Testing
 ◯ E2E Testing

# Vueバージョンの選択
? Choose a version of Vue.js that you want to start the project with (Use arrow keys)
❯ 2.x
  3.x (Preview)

# Class styleを使用するかどうか。私はObject styleを使うため No
? Use class-style component syntax? (Y/n) No

# TypeScriptと一緒にbabelを使うか
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? (Y/n) Yes

# LintとFormatterの設定に何を使うか
? Pick a linter / formatter config:
  ESLint with error prevention only
  ESLint + Airbnb config
  ESLint + Standard config
❯ ESLint + Prettier
  TSLint (deprecated)

# Lintの実行タイミング
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◉ Lint on save # 保存時にLintを実行
 ◯ Lint and fix on commit (requires Git)

# Babel, ESLintなどの設定をどこに記述するか
? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
❯ In dedicated config files # 各設定ファイルにする
  In package.json

# 今回設定した内容をプリセットで保存するか。基本的にはプロジェクトを以降作成することはないため No
? Save this as a preset for future projects? No

# パッケージマネージャーに何を使うか
? Pick the package manager to use when installing dependencies: (Use arrow keys)
  Use Yarn
❯ Use NPM
```

インストール完了後、`Ctrl+D`でコンテナを停止します。

また`front/Dockerfile`を以下のように書き換えます

```diff
FROM node:12.18.3-alpine

ENV APP_HOME /app
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME

RUN apk update 
+ COPY package.json .
+ RUN npm install
```

# 5. 動作確認

コンテナを立ち上げる

```
$ docker-compose up -d
```
`-d`: バックグラウンドでプロセスを実行する

## 5-1. Rails
`localhost:3000`にアクセスし以下のページが表示されることを確認
![スクリーンショット 2020-09-24 22.37.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133979/78bdcb35-96b5-b961-94e1-3bd2bf42e091.png)


## 5-2. Vue
`localhost:8080`にアクセスし以下のページが表示されることを確認
![スクリーンショット 2020-09-24 23.30.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133979/8cc87045-7422-3ac2-99d0-ba5067467d9d.png)


# おわりに
お疲れさまでした。
今回はDockerでRuby on Rails + Vue + MySQLの環境構築する方法について書きました。

VueとRailsはそこまで学習コストが高くないため、初心者にもオススメ出来る構成です。ぜひお試しください！

# その他
githubなどから初めてローカルに持ってきた場合は以下の手順でプロジェクトを開始します。

```
$ docker-compose run front npm install
$ docker-compose up
```