[Docker] Ruby2.7.2 / Rails6.0.3 / MySQL8.0の開発環境構築できたメモ

#はじめに
`Ruby2.7.2` / `Rails6.0.3` / `MySQL8.0` のWeb開発環境の構築手順を記しておきます。

- `web`と`db`の 2つのコンテナをたてていきます

 - `web`：**ruby2.7**をベースとし、**rails6**に必要なパッケージをインストールするよう記述した**Dockerfile**からimageを構築しコンテナを作る
 - `db`：**mysql:8.0**のimageからコンテナを作る

※ **Docker**の基礎は、 [**かめさん**](https://twitter.com/usdatascientist?s=21) の [Udemy講座](https://www.udemy.com/course/aidocker/) で学習しました。
　**コマンド動作**や**記述ファイル**の意図などは講座にて学習するのがいいと思います。


#環境
- macOS Catalina
- Docker version 20.10.2
- docker-compose version 1.27.4

`docker --version` `docker-compose --version` コマンドで確認できます


#Docker / docker-compose インストール
インストール等も動画教材に沿って行いました。

- Udemy　[米国AI開発者がゼロから教えるDocker講座](https://www.udemy.com/course/aidocker/)
- ブログ　[かめさんブログ - Docker超入門①](https://datawokagaku.com/whatisdocker/)
- Twitter　[かめさんツイッター](https://twitter.com/usdatascientist?s=21)

#ファイル準備
① Dockerfile
② Gemfile
③ Gemfile.lock
④ entrypoint.sh
⑤ docker-compose.yml
⑥ .env

※ 今回は`アプリケーション名`を`myapp`とし、別アプリ名とする場合は適宜置き換える
※ Desktop上に`myapp`フォルダを作りその中にファイル作成しました


##① Dockerfileの用意
`web`コンテナの元となる **docker image** を構築するための設計ファイル

```Dockerfile
FROM ruby:2.7

# nodejsとmysql-client (default-mysql-client) のインストール
RUN apt-get update -qq && apt-get install -y curl apt-transport-https wget nodejs default-mysql-client

# Yarn のインストール
RUN 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 -qq && apt-get install -y yarn

RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
```
※ Ruby2.7をベースとして、Rails6から標準となった **webpacker** の導入に必要なパッケージマネージャー **yarn** をインストールする

##② Gemfileの用意
必要な **gem** を一覧にして管理するためのファイル。まずは **Rails** をインストールする

```ruby:
source 'https://rubygems.org'
gem 'rails', '~>6.0.0'
```

##③ Gemfile.lockの用意
空のファイルでOK

##④ entrypoint.shの用意
コンテナの初回起動時に実行したいことを記述する


```ruby:
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
```

`set -e`　エラーが発生するとスクリプトを終了する
`rm`　railsのpidファイルを削除
`exec`　**"$@"** でCMDで渡されたコマンドを実行（本記事ではrails s）



>- [dockerで初回起動時のみ特定の処理を行うヘルパースクリプト（docker-entrypoint.sh）](https://qiita.com/bashaway/items/e6279ba432d18c2d4299)
- [Docker コンテナの動作に必要な設定を起動時に渡す](https://blog.amedama.jp/entry/2018/01/30/230221)

##⑤ docker-compose.ymlの用意
```docker-compose.yml
version: '3'
services:
  db:
    platform: linux/x86_64  # M1チップの場合は追記
    image: mysql:8.0
    environment:
      - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
      - MYSQL_USER=${DB_USER}
      - MYSQL_PASSWORD=${DB_PASSWORD}
    volumes:
      - db-data:/var/lib/mysql
    command: --default-authentication-plugin=mysql_native_password
    ports:
      - ${DB_PORT}:3306

  web:
    build: .
    env_file: .env
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
      - bundle-data:/usr/local/bundle
    ports:
      - "3000:3000"
    tty: true
    stdin_open: true
    depends_on:
      - db
    links:
      - db

volumes:
  db-data:
  bundle-data:

```

`platform: linux/x86_64` 　M1チップの場合はこれを追記しないと `docker-compose run` コマンドを実行した際にエラーが出ます。
> エラー文：no matching manifest for linux/arm64/v8 in the manifest list entries


##⑥ .envの用意
```
DB_ROOT_PASSWORD=mysql_root_password
DB_USER=myapp
DB_PASSWORD=myapp_password
DB_PORT=3306
```

#構築実行
- Railsアプリ作成
- imageを構築する
- DBの設定をする
- コンテナを起動させる
- DBユーザーに権限を付与する
- DBを作成する


##rails new
「Webpacker successfully installed 🎉🍰」まで5分程かかりました。諦めないでください。

```ruby:terminal
docker-compose run web rails new . --force --no-deps --database=mysql --webpacker --skip-test
```
**コマンドの意味**
`--force`　すでに **Gemfile** があるため上書きをする
`--no-deps`　リンクしたサービスを起動しない
`--database=mysql`　DBに**MySQL**を指定する
`--webpacker`　Rails6から標準となった**webpackerをインストール**する
`--skip-test`　テストは**RSpec**を使いたいのでrailsデフォルトの**Minitest**をskipしておく


##database.yml の設定
`rails new`で作られたファイルにDB情報を追記する

```ruby:config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV['DB_USER'] %>
  password: <%= ENV['DB_PASSWORD'] %>
  host: db

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test

production:
  <<: *default
  database: myapp_production
  username: myapp
  password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>
```

##imageをbuildする
```ruby:terminal
docker-compose build
```

```ruby:terminal（表示結果）
db uses an image, skipping    #「db」はmysql:8.0のimageがあるのでskip
Building web                  #Dockerfileを元に「web」のimgaeを構築
Step 1/14 : FROM ruby:2.7     #Dockerfile 一行目の記述から順にimage layerを作る
.....
```


##コンテナ起動、DBユーザーに権限の付与
```ruby:terminal
docker-compose up -d    #コンテナをバックグラウンド起動する
docker-compose ps -a    #コンテナが Up になっていることを確認
docker-compose exec db mysql -uroot -p -e"GRANT ALL PRIVILEGES ON *.* TO 'myapp'@'%';FLUSH PRIVILEGES;"

#passwordを聞かれるので、「.env」ファイルの「DB_ROOT_PASSWORD」に指定したpasswordを入力してEnter
```

>（注）権限付与するuser名（上の場合 `myapp`）は、`.env`で記述した`DB_USER`名に適宜置き換える。

##DB作成
```ruby:terminal
docker-compose run web rails db:create
```
##構築完了
**[http://localhost:3000](http://localhost:3000)** にアクセス！
ブラウザでいつもの画面が確認できたら環境構築は完了です！
本番はここから・・・！
開発がんばりましょーっ！
![railsルートページ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646452/3b17a53b-158d-3588-8c57-74d72d435f79.jpeg)



----------------

#コンテナ作成後の `gem` の追加方法
１. コンテナを起動する

```ruby
docker-compose up
```

２. gemを追加する

```ruby:Gemfile
# ログイン機能
gem 'devise'
```

３. `bundle install`を実行する
 
```ruby
docker-compose exec web bundle install
```

４. 一旦コンテナを削除する

```ruby
docker-compose down     
# コンテナとネットワークのみ削除（ボリュームとイメージは削除されない
```

５. コンテナを起動する

```ruby
docker-compose up
```

※ `docker-compose.yml`ファイルに記載している通り、gemのインストール先のディレクトリ`/usr/local/bundle`を **データvolume** として保存している（ **gemの永続化** ）
　→ インストール済みのgemを再インストールしなくて済む
　（gemの永続化をしていなかったら、毎回イメージのビルドをする必要がある=時間がかかる）

> [【Docker】Rails開発で知っておきたい！gemの永続化による作業効率アップの話](https://nishinatoshiharu.com/datavolume-for-gem/#gem)



#参考
- Docker公式ドキュメント（英語）
 - [Quickstart: Compose and Rails](https://docs.docker.com/compose/rails/)
 - 英語かつ Rails5 / PostgreSQL環境ですが「エンジニアは第一ソースを見ろ」とのことなので参考にしました
 - [用語集](https://docs.docker.com/glossary/) 

- Dockerドキュメント（日本語訳をしてくれているサイトです）
 - [用語集](http://docs.docker.jp/glossary.html)
 - [Dockerfile のベストプラクティス
](http://docs.docker.jp/engine/articles/dockerfile_best-practice.html)

- とってもお世話になりました記事
 - [DockerでRuby2.7.x＋Rails6.0.x＋MySQL8.0.xな環境を構築する](https://qiita.com/madai0517/items/417063f924c0dfc27f3a)
 - [【Rails】Rails 6.0 x Docker x MySQLで環境構築](https://qiita.com/nsy_13/items/9fbc929f173984c30b5d)
 

#さいごに
使用言語やフレームワークのバージョンによってインストールすべきパッケージが異なるので、公式ドキュメント等で適切な方法を検索して**初めて**、環境構築ができました。

文言や手順などに、間違いがあればコメントをいただけると幸いです。