Dockerで既存アプリの開発環境を作成【Ruby2.6 + Rails5.2 + Mysql5.7】

既存アプリの開発環境をDockerに。色々とハマりましたが、そこも含めて書いて行きます。

##環境
Ruby：2.6.5
Rails：5.2.4
Mysql：5.7
OS: macOS Mojave 10.14.5

##ディレクトリ構成
```
sampleApp ---- Dockerfile
           |-- docker-compose.yml
           |-- app
           |-- bin
           |-- config
                 |-- database.yml
```

既存アプリのルート直下に`Dockerfile`、`docker-compose.yml`を作成しました。

##うまくいったやり方
```Dockerfile:Dockerfile
# app_nameの部分はそのままでOK
FROM ruby:2.6.5

RUN apt-get update -qq && \
    apt-get install -y build-essential \ 
                       libpq-dev \        
                       nodejs           

RUN mkdir /app_name 
ENV APP_ROOT /app_name 
WORKDIR $APP_ROOT

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

RUN gem install bundler
RUN bundle install
ADD . $APP_ROOT
```
```docker-compose.yml
version: '3'
services:
  db:
    image: mysql:5.7
    volumes:
      - mysql_data:/var/lib/mysql
    environment:
      MYSQL_DATABASE: root
      MYSQL_ALLOW_EMPTY_PASSWORD: 1
    ports:
      - "3305:3306"
  web:
    build:
      context: .
      dockerfile: Dockerfile
    command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    tty: true
    stdin_open: true
    depends_on:
      - db
    ports:
      - "3000:3000"
    volumes:
      - .:/app_name
      - ~/.ssh:/root/.ssh
volumes:
  mysql_data:
```
```config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  charset: utf8mb4
  collation: utf8mb4_general_ci
  pool: 5
  #以下の3行が重要
  username: root
  password:
  host: db
  #
  timeout: 5000

development:
  <<: *default
  database: sample_app_development #sample_appの部分は、アプリ名に応じて修正

test:
  <<: *default
  database: sample_app_test #sample_appの部分は、アプリ名に応じて修正
```
```zsh:terminal
$ docker-compose build
$ docker-compose up -d
$ docker-compose run web rails db:create
$ docker-compose run web rails db:migrate
```
http://localhost:3000/ にアクセスして、アプリのトップページが表示さればひとまず成功。

##遭遇したエラー
###`.env`ファイル内のスペース
```rb:.env
HOGE_KEY = age053nf3g3jk0 #×
HOGE_KEY=age053nf3g3jk0 #◯
```

###`RUN gem install bundler`
```Dockerfile:Dockerfile
RUN gem install bundler #この行が無いとエラーがでたので追加した
RUN bundle install
ADD . $APP_ROOT
```

###`MYSQL_ALLOW_EMPTY_PASSWORD: 1`
```docker-compose.yml
version: '3'
services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_DATABASE: root
      MYSQL_ALLOW_EMPTY_PASSWORD: 1 #この行が無いとエラーがでたので追加した
```

###db:createが通らない
ここでめっちゃハマった。まじきつかった。
`docker-compose down --volumes`からの`docker-compose up -d --build`で解決する的な情報があって、試してみたけどうまくいかず。
`docker-compose.yml`の`environment`と、`database.yml`の値をゴニョゴニョして(今の形にして)

```zsh:terminal
$ docker-compose down --rmi all
$ docker-compose build
$ docker-compose up -d
$ docker-compose run web rails db:create
```
で解決。

###ローカルの変更が反映されない
`docker-compose.yml`の`web>volumes`の箇所が間違っていた。
`Dockerfile`内で作成したディレクトリ`app_name`を記述することで解決。

###コンテナを削除するたびにDBのデータが消える
名前付きボリュームを設定して解決。これで`down`→`up`した時、以前のデータが残ったままになりました。

```docker-compose.yml
version: '3'
services:
  db:
    image: mysql:5.7
    volumes: #ここと
      - mysql_data:/var/lib/mysql
.
.
volumes: #ここ！
  mysql_data:
```

DBのデータを永続化する方法には、
・名前つきボリューム
・匿名ボリューム
・ローカルのディレクトリのマウント、
の3つがあって、名前つきボリュームが一番良いらしい。

[Dockerのデータを永続化！Data Volume（データボリューム）の理解から始める環境構築入門](https://nishinatoshiharu.com/docker-volume-tutorial/)
[Docker、ボリューム(Volume)について真面目に調べた](https://qiita.com/gounx2/items/23b0dc8b8b95cc629f32)

###変な感じでコンテナを停止しちゃった後、再開できなくなる時がある
`rails s`の前にPIDを削除するように設定する

```docker-compose.rb
command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
```

###ローカルからデータベースに接続できない
3306のポートは、ローカルのmysqlで埋まっているので、ポートを3305に変えて接続

```docker-compose.yml
version: '3'
services:
  db:
    image: mysql:5.7
    volumes:
      - mysql_data:/var/lib/mysql
    environment:
      MYSQL_DATABASE: root
      MYSQL_ALLOW_EMPTY_PASSWORD: 1
    ports:
      #- "3306:3306"
      - "3305:3306"
```
TablePlusの設定例
<img width="500" alt=" 2020-03-11 18.08.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244893/0dc4e013-95f8-94f5-871e-df51af99fbe4.png">

###RSpecのSystem Specが動かない
`Dockerfile`にchromeをインストールする記述を追加（不要な人もいるかもなので、記事冒頭の「うまくいった方法」には載せてないです。）

```Dockerfile
RUN apt-get update && apt-get install -y unzip && \
    CHROME_DRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` && \
    wget -N http://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip -P ~/ && \
    unzip ~/chromedriver_linux64.zip -d ~/ && \
    rm ~/chromedriver_linux64.zip && \
    chown root:root ~/chromedriver && \
    chmod 755 ~/chromedriver && \
    mv ~/chromedriver /usr/bin/chromedriver && \
    sh -c 'wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -' && \
    sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' && \
    apt-get update && apt-get install -y google-chrome-stable

RUN mkdir /app_name 
.
.
```

###Better Errorsが使えない
```config/environments/development.rb
#以下を追加
if Rails.env.development?
  BetterErrors::Middleware.allow_ip! "0.0.0.0/0"
end
```

[dockerで立ち上げたrailsサーバでもBetter Errorsを使えるようにする](https://qiita.com/paranishian/items/29ce26c4324068e20aec)

###ssh接続ができなくなった
`volumes`に`~/.ssh:/root/.ssh`を追記→パーミッションを変更

```docer-compose.yml
version: '3'
#省略
  web:
    #省略
    volumes:
      - .:/app_name
      - ~/.ssh:/root/.ssh #この行を追加
volumes:
  mysql_data:
```
ローカルで設定した秘密鍵のパーミッションは引き継がれないので、手動で変更する必要があります。

```terminal
$ docker-compose exec web /bin/bash
$ cd /root/.ssh
$ chmod 600 id_rsa
```

##その他のサンプル
<details><summary>Rails6 + Postgres</summary><div>

```Dockerfile:Dockerfile
FROM ruby:2.6.5

RUN apt-get update -qq && apt-get install -y nodejs postgresql-client

#Rails6はyarnが必要！！
RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
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 && apt-get install -y yarn

RUN mkdir /app_name 
ENV APP_ROOT /app_name 
WORKDIR $APP_ROOT

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

RUN gem install bundler
RUN bundle install
ADD . $APP_ROOT
```
```docker-compose.yml
version: '3'
services:
  db:
    image: postgres
    volumes:
      - psgl_data:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: root
      POSTGRES_PASSWORD: password
    ports:
      - 5433:5432
  web:
    build:
      context: .
      dockerfile: Dockerfile
    command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    tty: true
    stdin_open: true
    depends_on:
      - db
    ports:
      - "3000:3000"
    volumes:
      - .:/app_name
volumes:
  psgl_data:
```
```config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  #以下の3行が重要
  host: db
  username: root
  password: password
```
```config/webpacker.yml
#check_yarn_integrity: true
check_yarn_integrity: false
```
</div></details>
<details><summary>DBのデータをローカルにマウントする場合</summary><div>

```docker-compose.yml
version: '3'
services:
  db:
    image: postgres
    volumes:
      - ./dbdata:/var/lib/postgresql/data #ローカルのdbdataディレクトリにデータを保存して同期
    environment:
      POSTGRES_USER: root
      POSTGRES_PASSWORD: password
    ports:
      - 5434:5432
  web:
    build:
      context: .
      dockerfile: Dockerfile
    command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    tty: true
    stdin_open: true
    depends_on:
      - db
    ports:
      - "3000:3000"
    volumes:
      - .:/app_name
#トップレベルのvolumesは不要
```
</div></details>

##参考
- [丁寧すぎ
るDocker-composeによるrails5 + MySQL on Dockerの環境構築(Docker for Mac)](https://qiita.com/azul915/items/5b7063cbc80192343fc0)
- [Inject host's SSH keys into Docker Machine with Docker Compose
](https://stackoverflow.com/questions/34932490/inject-hosts-ssh-keys-into-docker-machine-with-docker-compose)

##最後に
改善できるところがあったら指摘してもらえると嬉しいです。