Dockerを使用したRails開発①(環境構築)

# はじめに
Dockerを使用して自作のRailsアプリの開発から公開までを記事にしていこうと考えています。
あくまで自分の学習したことのまとめ用に投稿していく予定ですが、同じようにこれから勉強を始める誰かの参考になればいいなと思ってます！

# Dockerとは
Dockerとは、近年注目されている仮想化ソフトの一種です。
注目されている理由は、その手軽さにあります。

Dockerは、コンテナ型仮想化と呼ばれる技術を使用しており、従来のホスト型仮想化と異なり、ハードウェアを仮想化しません。そのため、ゲストOSを起動させる必要がなくリソース使用量が少ない点がメリットとして挙げられます。

イメージ、コンテナといったワードがちょこちょこ出てきますが、以下で覚えることにしました。
イメージ・・・OSやアプリを含んだテンプレートです。言わばコンテナのひな形。
コンテナ・・・イメージから生成される仮想サーバー。マシン上で一つのプロセスとして管理されます。

Dockerは、
イメージの構成を「Dockerfile」、
コンテナの構成を「docker-compose.yml」
というファイルで管理します。

# Docker for Macのインストール〜起動
※ Macでお勉強中なので、Windowsの人はゴメンナサイ・・・

1. [公式サイト](https://store.docker.com/editions/community/docker-ce-desktop-mac)からDocker.dmgをダウンロード
2. Docker.dmgを実行
3. Docker.appをApplicationsにドラッグ&ドロップ
4. Docker.appをダブルクリックで起動

# やること
Rails用のコンテナとMySQL用のコンテナを作成して画面起動確認までを行いたいと思います。
それぞれバージョンは、
- Ruby：2.5.1
- MySQL：8.0
を使用します。

やることを整理すると以下の通り。
1. docker-compose.yml、Dockerfileの作成
2. Gemfile、Gemfile.lockの作成
3. コンテナの作成
4. データベースの設定
5. 画面起動確認

# ディレクトリ構成
ディレクトリはこんな感じで進めていきます。

```
root
├── Dockerfile
├── docker-compose.yml
├── mysql-confd
│   └── default_authentication.cnf
└── src
    ├── Gemfile
    └── Gemfile.lock
```

default_authentication.cnfは、MySQL8.0のデフォルト認証形式を変更するための設定ファイルです。
[こちら](https://qiita.com/yensaki/items/9e453b7320ca2d0461c7)の記事を参考に作成しました。
これしないで進めてたらデータベース作成時にエラーになりました。。
MySQL5.7と8.0では認証方法が異なることが原因とのこと。
認証方法を5.7に合わせるように設定しています。

# 1. docker-compose.yml、Dockerfileの作成
docker-compose.ymlの中身はこんな感じ

```yml:docker-compose.yml
# docker-compose.ymlフォーマットのバージョン指定
version: '3'
services:
  # Railsコンテナ定義
  web:
    # Dockerfileを使用してイメージをビルド
    build: .
    # コンテナ起動時のデフォルトコマンド
    # ポート番号：3000
    # バインドするIPアドレス：0.0.0.0
    # ポート3000が来たらrailsサーバーが応答
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    # ローカルのsrcをコンテナにマウント
    volumes:
      - ./src:/app
    # コンテナの外部に3000番を公開
    # 公開するポート番号：コンテナ内部の転送先ポート番号
    ports:
      - 3000:3000
    # dbコンテナが先に起動するよう設定
    depends_on:
      - db
    # pryを使用してデバッグができるよう設定
    tty: true
    stdin_open: true
  # MySQLコンテナ定義
  db:
    # mysqlを使用してコンテナ作成
    image: mysql:8.0
    volumes:
      # Mysql8.0のデフォルトの認証形式をmysql_native_passwordに設定
      - ./mysql-confd:/etc/mysql/conf.d
      # ローカルで保持しているDBをコンテナにマウント
      - db-volume:/var/lib/mysql
    # コンテナ内の環境変数を定義
    environment:
      # mysqlのルートユーザーのパスワード設定
      MYSQL_ROOT_PASSWORD: password
# DBの内容をローカルに保持
volumes:
  db-volume:
```

Dockerfileの中身はこんな感じ

```dockerfile:Dockerfile
# Railsコンテナ用Dockerfile

# イメージのベースラインにRuby2.5.1を指定
FROM ruby:2.5.1
# Railsに必要なパッケージをインストール
RUN apt-get update -qq && apt-get install -y build-essential nodejs
# ルートディレクトリを作成
RUN mkdir /app
# 作業ディレクトリを指定
WORKDIR /app
# ローカルのGemfileとGemfile.lockをコピー
COPY src/Gemfile /app/Gemfile
COPY src/Gemfile.lock /app/Gemfile.lock
# Gemのインストール実行
RUN bundle install
# ローカルのsrcをコピー
COPY src /app
```

COPYコマンドでコピー元（ローカル側）はDockerfileが置いてあるパス以下の相対パス指定しかできないみたいです。（../を使うとエラーになった。。）

# 2. Gemfile、Gemfile.lockの作成

① srcディレクトリで`bundle init`を実行してGemfile新規作成

② Gemfileの中身はこんな感じに編集。この時点でgemはrailsのみ。

```ruby:Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem 'rails'
```

③ Gemfile.lockは空で作成

```ruby:Gemfile.lock
```

# 3. コンテナの作成
① `docker-compose run web rails new . --force --database=mysql`を実行するとdocker-compose.ymlの内容でコンテナが作成されます。
※ docker-compose.ymlが存在する階層でなければdocker-composeコマンドは実行できないので注意

*コマンド解説*
`docker-compose run web`：指定したコンテナで後に続くコマンドを実行
`rails new .`：Railsプロジェクトの新規作成
`-- force`オプション：上書きコピー指定
`--database=mysql`オプション：使用データベースにMySQLを指定

コマンド実行後のディレクトリ構成がこんな感じになってればOK。
Railsプロジェクトがローカルに作成されていることがわかります。

```
root
├── Dockerfile
├── docker-compose.yml
├── mysql-confd
│   └── default_authentication.cnf
└── src
    ├── Gemfile
    ├── Gemfile.lock
    ├── README.md
    ├── Rakefile
    ├── app
    ├── bin
    ├── config
    ├── config.ru
    ├── db
    ├── lib
    ├── log
    ├── package.json
    ├── public
    ├── storage
    ├── test
    ├── tmp
    └── vendor
```

`docker-compose ps`を実行すると起動しているDBコンテナのみが表示されます。

```
    Name                 Command             State          Ports       
------------------------------------------------------------------------
touring_db_1   docker-entrypoint.sh mysqld   Up      3306/tcp, 33060/tcp
```

`docker ps -a`を実行すればrails用のコンテナも作成されていることが確認できます。

次にGemfileを開くと最初に作成した内容が①のコマンド実行により上書きされているのがわかります。

```ruby:Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.5.1'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.2.1'
# Use mysql as the database for Active Record
gem 'mysql2', '>= 0.4.4', '< 0.6.0'
# Use Puma as the app server
gem 'puma', '~> 3.11'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# See https://github.com/rails/execjs#readme for more supported runtimes
# gem 'mini_racer', platforms: :ruby

# Use CoffeeScript for .coffee assets and views
gem 'coffee-rails', '~> 4.2'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use ActiveStorage variant
# gem 'mini_magick', '~> 4.8'

# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.1.0', require: false

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '>= 3.0.5', '< 3.2'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of chromedriver to run system tests with Chrome
  gem 'chromedriver-helper'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
```

② この状態で`docker-compose build`するとDockerfileの内容が実行され、このGemfileの内容でGemのインストールや作成されたsrc配下がコンテナへとコピーされます。（最初に作ったGemfileは①で行われる初回のDockerfileのビルドを成功させるためのダミー的な扱いです。）

以降にGemを追加したい場合は、
- Gemfile修正
- `docker-compose build` の実行
を繰り返すだけです。

# 4. データベースの設定
① src/config/database.ymlに接続情報を設定
passwordとhostを以下の内容に書き換えます。
- password：docker-compose.ymlに設定したmysqlのルートユーザーのパスワード
- host：docker-compose.ymlに設定したMySQLコンテナ名

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

② `docker-compose run web rake db:create`を実行してDB作成

# 5. 画面起動確認
① `docker-compose up -d`を実行してコンテナ起動

`docker-compose ps`を実行すると起動しているRailsコンテナとDBコンテナが表示されます。StateがUpになっているので両方のコンテナが正しく起動された状態です。

```
    Name                   Command               State           Ports         
-------------------------------------------------------------------------------
touring_db_1    docker-entrypoint.sh mysqld      Up      3306/tcp, 33060/tcp   
touring_web_1   bundle exec rails s -p 300 ...   Up      0.0.0.0:3000->3000/tcp
```

② ブラウザでlocalhost:3000に接続
<img width="731" alt="スクリーンショット 2018-09-11 22.30.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/263328/f7152c94-2ede-a4cd-9272-c3b92da59311.png">

この画面が表示されればひとまず完了！