Dockerを使用して環境構築してみた(初心者)

#概要
Dockerを使用してRuby on Railsの開発環境を構築してみました。
Dockerを使用すると、ローカル環境に左右されず開発環境を構築でき、開発環境から本番環境への移行も設定を変更せず移行できるため、多くの開発現場で使用されている。
独学でDockerを勉強した私が作成した最終的な設定になります。

#バージョン
Ruby 2.6.3
Ruby on Rails 5.2.0
Docker 19.03.13
docker compose 1.27.4
MySQL 5.7
Nginx 1.15.8

#前提
* docker、docker-composeはインストール済みである。

#ディレクトリ構成

```terminal:ディレクトリ構成
./myapp
├── containers
│   └── nginx
│       ├── Dockerfile
│       └── nginx.conf
├── .env
├── docker-compose.yml
├── Dockerfile
├── Gemfile
└── Gemfile.lock
```

#各ディレクトリ、ファイルの詳細
**Rails用Dockerfile**

```dockerfile:Dockerfile
FROM ruby:2.6.3

RUN apt-get update\
  && apt-get install -y --no-install-recommends\
  libpq-dev\
  nodejs\
  vim\
  mariadb-client\
  build-essential\
  && apt-get clean\
  && rm -rf /var/lib/apt/list/*

RUN mkdir /myproject
WORKDIR /myproject

COPY Gemfile /myproject/Gemfile
COPY Gemfile.lock /myproject/Gemfile.lock

RUN gem install bundler
RUN bundle install

COPY . /myproject

RUN mkdir -p tmp/sockets
RUN mkdir -p tmp/pids
```

**Gemfile**

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

gem 'rails', '5.2.0'
```

**Gemfile.lock**
中身は空でOK

**Nginx用Dockerfile**

```dockerfile:Dockerfile
FROM nginx:1.15.8

RUN rm -f /etc/nginx/conf.d/*

ADD nginx.conf /etc/nginx/conf.d/myproject.conf

CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
```

**Nginx.confファイル**

```nginx:nginx.conf
upstream myproject {
  server unix:///myproject/tmp/sockets/puma.sock;
}

server {
  listen 80;
  server_name IPアドレス [or localhost];

  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;
  
  root /myproject/public;

  client_max_body_size 100m;
  error_page 404             /404.html;
  error_page 505 502 503 504 /500.html;
  try_files  $uri/index.html $uri @myproject;
  keepalive_timeout 5;

  location @myproject {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://myproject;
  }
}
```

**.envファイル**

```:.env
MYSQL_ROOT_PASSWORD=password
MYSQL_USER=username
MYSQL_PASSWORD=password
MYSQL_DATABASE=docker_database
```

**docker-compose.ymlファイル**

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

services:
  db:
    image: mysql:5.7
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    env_file:
      - ./.env
    volumes:
      - mysql-data:/var/lib/mysql
    ports:
      - "4306:3306"

  app:
    build: .
    env_file:
      - ./.env
    command: bundle exec puma -C config/puma.rb
    init: true
    volumes:
      - .:/myproject
      - public-data:/myproject/public
      - tmp-data:/myproject/tmp
      - log-data:/myproject/log
    depends_on:
      - db

  web:
    build:
      context: containers/nginx
    init: true
    volumes:
      - public-data:/myproject/public
      - tmp-data:/myproject/tmp
    ports:
      - 80:80
    depends_on:
      - app

volumes:
  mysql-data:
  public-data:
  tmp-data:
  log-data:
```

#Railsアプリケーションの設定
まずrails newにてRailsアプリケーションを作成します。

```terminal:terminal
$ docker-compose run --rm app bundle exec rails new . --force --database=mysql --skip-bundle
```

続いて、configディレクトリ内のpumaファイルを編集します。

```ruby:puma.rb
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
threads threads_count, threads_count
port        ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { "development" }
plugin :tmp_restart

app_root = File.expand_path("../..", __FILE__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"

stdout_redirect "#{app_root}/log/puma.stdout.log", "#{app_root}/log/puma.stderr.log", true
```

database.ymlを編集します。

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

development:
  <<: *default
  database: docker_database

test:
  <<: *default
  database: myproject_test

production:
  <<: *default
  database: myproject_production
  username: myproject
  password: <%= ENV['MYPROJECT_DATABASE_PASSWORD'] %>
```

#コンテナの起動
ここまで準備ができましたら、コンテナを起動します。

```terminal:terminal
$ docker-compose build
ビルドが完了したら
$ docker-compose up
```

ここでlocalhostにアクセスしてRailsのページが立ち上がれば完成です。

#既存のRailsアプリをdockerで環境構築する場合
アプリケーションのフォルダの中に以下のディレクトリ、ファイル類を追加します。
（ファイルの設定は一から作る場合と一緒です。）

```terminal:ディレクトリ構成
./myapp
├── containers
│   └── nginx
│       ├── Dockerfile
│       └── nginx.conf
│
│　その他のファイル
│
├── .env
├── docker-compose.yml
└── Dockerfile
```

その後、ビルドを行います。アプリケーションはすでにあるので、rails newコマンドをする必要はありません。
データベースは一度migrateする必要があるので、ビルド後にmigrateを行います。

```terminal:terminal
$ docker-compose build
ビルドが完了したら
$ docker-compose up -d
$ docker-compose exec app rails db:migrate
```

以上でlocalhostにアクセスすれば既存のアプリケーションにdockerを導入することができると思います。

#補足
私もまだまだ勉強不足でして、途中でエラーになったりとかなり苦戦しました。
その都度、ネットで調べたりして解決できました。

#ハマったエラー
* rails newでrailsファイルが作成できない。

参考ページでは

```terminal:
$ docker-compose run --rm app rails new . --force --database=mysql --skip-bundle
```

となっていましたが、

```terminal:
$ docker-compose run --rm app bundle exec rails new . --force --database=mysql --skip-bundle
```

でRailsファイル群をインストールすることができました。

* docker-compose buildが通らない。

```terminal
$ docker-compose build
...
Bundler could not find compatible versions for gem "sprockets":
  In snapshot (Gemfile.lock):
    sprockets (= 4.0.2)

  In Gemfile:
    sass-rails (~> 5.1) x86_64-linux was resolved to 5.1.0, which depends on
      sprockets (>= 2.8, < 4.0)

    rails (~> 5.2.0) x86_64-linux was resolved to 5.2.0, which depends on
sprockets-rails (>= 2.0.0) x86_64-linux was resolved to 3.2.2, which
depends on
        sprockets (>= 3.0.0)

Running `bundle update` will rebuild your snapshot from scratch, using only
the gems in your Gemfile, which may resolve the conflict.
ERROR: Service 'app' failed to build : The command '/bin/sh -c bundle install' returned a non-zero code: 6
```

bundle updateをするよう言われているので、bundle updateを行います。

```terminal:
$ docker-compose run app bundle update
その後
$ docker-compose build
```

#まとめ
dockerでの環境構築はなんとかできましたが、まだまだ勉強不足で苦戦しました。
私が初めてdockerを使い始めたときは、既存のアプリケーションをdocker化しました。その後、新規環境構築からdockerを導入する方法を行いましたが、何度もつまずいてしまいました。
細かな設定ももう一度勉強し、ひとつひとつの解説もいつかやっていきたいです。

#参考文献
[Docker + Rails + Puma + Nginx + MySQL](https://qiita.com/eighty8/items/0288ab9c127ddb683315)
[#エラー解消 rails newできない Running bundle update will rebuild your snapshot from scratch, using only the gems in your Gemfile, which may resolve the conflict.](https://qiita.com/s56258/items/39a2e6f8cfd7ae2fa0b7)