Railsで開発した個人アプリにDockerを導入する手順と最低限の知識

Railsで開発した個人アプリに、後からDockerを導入しました。
その時知識不足なせいでハマってしまった場面があったので、自分の中で整理してアウトプットすることを目的にこの記事を書き残します。

ちなみに、以下の記事を主に参考にさせていただきました。
[DockerをMacにインストールする](https://qiita.com/kurkuru/items/127fa99ef5b2f0288b81)
[Docker + Rails + Puma + Nginx + MySQL](https://qiita.com/eighty8/items/0288ab9c127ddb683315)


# RailsアプリにDockerを導入する手順＋事前知識
まずは事前知識として、これは知っておいたほうがいいというものを簡単に書いていきます。


## 事前知識 - Docker関係

### Docker(ドッカー)

※1.軽量な仮想化環境を実現するためのツール。

OS やアプリケーションを設定したものを丸ごと実行イメージとして保存できるので、Docker が導入されている別のマシンにそのまま持って行くことができる。

実行環境をテキストファイルとして共有できるのでとても便利。


※1.仮想化環境とは、コンピュータ上にソフトウェアによって仮想的に構築されたコンピュータ（仮想マシン）が備える仕様や機能の総体のこと。

### Dockerイメージ
Dockerイメージは、コンテナを起動させるためのベースとなるもの(オブジェクト指向でいうと「クラス」にあたる)

テキストファイル(Dockerfile)からビルドされる。(後に記述)

DockerHub(Docker向けのコンテナ共有サービス)では、既に多くのイメージが公開されている。


### Dockerコンテナ
Dockerのコンテナは、 Dockerイメージを元に作成される仮想環境の実行部分(オブジェクト指向でいうと「インスタンス」にあたる)

原則1コンテナ1アプリ。

### Dockerfile(ドッカーファイル)
指定したベースのDockerイメージに加える変更を記述するファイル。

Dockerfileを使うことでオリジナルのDockerイメージを作成することができる。

### docker-compose(ドッカーコンポーズ)

複数のコンテナから構成されるアプリケーションで、Dockerイメージのビルドや各コンテナの起動・停止などをより簡単に行えるようにするツール。

docker-composeを使用する際は「docker-compose.yml」が必要になる。

## 事前知識 - サーバ関係

### ミドルウェア
OSとアプリケーションの間に入り、中間的な処理を行うソフトウェアのこと。

**ー 例 ー**

- Webサーバ
 - Apache、NginXなど
- APサーバ
 - Puma
 - Unicornなど
- DBサーバ
 - MySQL
 - PostgreSQLなど


### Nginx(エンジンエックス)
webサーバの一つ。

Apacheよりも処理能力が高い。

### puma(プーマ)
※1.Rackという機能を提供するためのアプリケーションサーバ。

webサーバの1つでもある。

※1.RackとはWeb サーバと Rubyやフレームワークをつなぐ最小のインタフェースを提供するもの。


## Docker導入手順

### ① Docker for Macを公式サイトからインストール、そして起動
**[公式サイト](https://hub.docker.com/editions/community/docker-ce-desktop-mac)**で会員登録を済ませた後、Docker for Macをダウンロードしインストール。

インストールが終わったら、Dockerを起動しておく。
(MACの画面上部にDockerのマークが出れば起動できてる証拠)


### ②作成済みのアプリケーションフォルダの直下に「Dockerfile」、「docker-compose.yml」ファイルを新しく作成する。
フォルダ構成

 - 既存のRailsアプリフォルダ
    - app
    - bin
    - config
    - db
    - ・・・
    -  **Dockerfile**
    -  **docker-compose.yml**

### ③Dockerfileに記述する(アプリケーションフォルダ直下)

```ruby:Dockerfile
FROM ruby:2.5.1

RUN apt-get update && \
    apt-get install -y mysql-client nodejs vim --no-install-recommends && \
    rm -rf /var/lib/apt/lists/*

RUN mkdir /myproject

WORKDIR /myproject

ADD Gemfile /myproject/Gemfile
ADD Gemfile.lock /myproject/Gemfile.lock

RUN gem install bundler
RUN bundle install

ADD . /myproject

RUN mkdir -p tmp/sockets

```

### ④docker-compose.ymlに記述する(アプリケーションフォルダ直下)

```docker-compose.yml
version: '2'
services:
  db:
    image: mysql:5.6
    environment:
      - ./environments/db.env
    volumes:
      - mysql-data:/var/lib/mysql
    ports:
      - "4306:3306"

  app:
    build: .
    command: bundle exec puma -C config/puma.rb
    volumes:
      - .:/myproject
      - public-data:/myproject/public
      - tmp-data:/myproject/tmp
      - log-data:/myproject/log

  web:
    build:
      context: containers/nginx
    volumes:
      - public-data:/myproject/public
      - tmp-data:/myproject/tmp
    ports:
      - 80:80
volumes:
  mysql-data:
  public-data:
  tmp-data:
  log-data:

```

### ⑤アプリケーションフォルダ直下に「environments」フォルダを作成、さらにenvironmentsフォルダ直下に「db.env」ファイルを作成する。

- 既存のRailsアプリフォルダ
    - app
    - bin
    - config
    - db
    - **environments**
        - **db.env**
    - ・・・
    -  Dockerfile
    -  docker-compose.yml

### ⑥db.envを編集
```environments/db.env
MYSQL_ROOT_PASSWORD=password
MYSQL_USER=user
MYSQL_PASSWORD=password
```

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


### ⑧アプリケーションフォルダ直下に「containers」フォルダを作成、さらにcontainersフォルダ直下に「nginx」フォルダを作成する。
フォルダ構成

 - 既存のRailsアプリフォルダ
    - app
    - bin
    - config
    - **containers**
        - **nginx**
    - db
    - environments
        - db.env
    - ・・・
    -  Dockerfile
    -  docker-compose.yml


### ⑨作成したnginxフォルダ直下に「Dockerfile」、「nginx.conf」ファイルを作成する。

 - 既存のRailsアプリフォルダ
    - app
    - bin
    - config
    - containers
        - nginx
            - **Dockerfile**
            - **nginx.conf**
    - db
    - environments
      - db.env
    - ・・・
    -  Dockerfile
    -  docker-compose.yml

### ⑩Dockerfileに記述する(containers/nginxフォルダ直下)

```ruby:containers/nginx/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に記述する(containers/nginxフォルダ直下)

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

server {
  listen 80;
  server_name 13.112.60.229;

  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;
  }
}
```

### ⑫puma.rbを編集

```puma.rb
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
```

### ⑬ターミナルでコマンドを実行
ファイルの準備が整ったら、最後にターミナル上でコマンドを実行。
(railsアプリケーションフォルダ内で実行する)

#### イメージを構築する

```terminal:ターミナル
$ docker-compose build
```

#### railsのコンテナを作成し、データベースの作成処理を行う

```terminal:ターミナル
$ docker-compose run --rm app rake db:create db:migrate
```

#### 全てのコンテナを構築・起動する

```terminal:ターミナル
$ docker-compose up
```
上記の流れが終わったあとlocalhostにアクセスすると、しっかり確認することができました。

ちなみに今回書きまとめたものは、DBもDockerで管理することになりますが、DBはローカルのものを参照したいという場合、以下の記事が参考になるかと思います。
**[既存のRailsアプリをDocker化し、ローカルのDBに接続する方法](https://nishinatoshiharu.com/existing-rails-dockernize/)**

### (おまけ)

#### Dockerfileに記述できるコマンド一覧

|コマンド|意味|
|:-:|:-:|
|FROM|ベースとなるイメージ|
|RUN|docker build 時に実行するコマンド|
|CMD|docker run 時に実行するコマンド|
|ENTRYPOINT|docker run 時に実行するコマンド|
|MAINTAINER|作者情報|
|LABEL|ラベル情報(メタデータ)|
|EXPOSE|公開ポート番号|
|ENV|環境変数|
|ARG|一時変数|
|COPY|ホストからコンテナへのファイルコピー|
|ADD|ファイル/ディレクトリの追加|
|VOLUME|ボリュームのマウント|
|USER|実行ユーザ|
|SHELL|シェル指定|
|WORKDIR|ワークディレクトリ|
|ONBUILD|ビルド時に実行するコマンド|
|STOPSIGNAL|コンテナ終了時に送信されるシグナル|
|HEALTHCHECK|ヘルスチェック|


#### ターミナル上で実行できるdocker-composeコマンド一覧

|コマンド|意味|
|:-:|:-:|
|build|サービスの構築または再構築|
|config|compose ファイルの確認と表示|
|create|サービスの作成|
|down|コンテナ・ネットワーク・イメージ・ボリュームの停止と削除|
|events|コンテナからリアルタイムにイベントを受信|
|help|コマンド上でヘルプを表示|
|kill|コンテナを kill (強制停止)|
|logs|コンテナの出力を表示|
|pause|サービスを一時停止|
|port|ポートに割り当てる公開用ポートを表示|
|ps|コンテナ一覧|
|pull|サービス用イメージの取得|
|restart|サービスの再起動|
|rm|停止中のコンテナを削除|
|run|１度だけコマンドを実行|
|scale|サービス用コンテナの数を指定ド|
|start|サービスの開始|
|stop|サービスの停止|
|up|コンテナの作成と開始|
|version|Docker Compose のバージョン情報を表示|