はじめてのDockerでRails開発環境構築 [NGINX + Rails 5 (puma) + PostgreSQL] 

#はじめてのDockerでRails開発環境構築 [NGINX + Rails 5 (puma) + PostgreSQL] 

**それなりに時間を書けて取り組んだので改善点等コメントいただけると嬉しいです
GitHubにコードも上げてありますので、必要があれば公開いたします同様にコメントください**

## Rails6版できました
- コマンドひとつ、5分でRails環境構築
- Rails６で躓きがちな、webpackによるCSS, JavaScript管理について対策済み
- Bootstrap自動インストール
- より新しいComposeの使用を反映

[コマンドひとつ、5分でRails6の開発環境構築 on Docker - Rails6 + Nginx + PostgreSQL + Webpack (Bootstrap install済) - Qiita](https://qiita.com/naokit-dev/items/96cb41361ebc4b7716c0)

# なぜやるか

- 今後AWSのEC2上にNGINX + puma (Rails) + PostgreSQLの環境でアプリをデプロイしたい 
- 開発環境も同じ構成でやりたい
- 実務での開発を意識して**Dockerを使ってローカル開発環境を構築したい**
- Railsチュートリアルでお世話になったAWS Cloud9ではなく、VS Codeを使いたい

# 何を得たか

- Dockerの基礎知識、概念
- 快適なローカル開発環境 (VSCode素敵)
- **初学者が開発環境構築までやると時間対効果よくない**（丸一日かかりました...）
- ただし、一度構築してしまえば**環境の再現は恐ろしく簡単、環境の共有という視点も納得**（シェル書けばあっという間？）
- AWSデプロイもこのまま行けそう、という謎の自信
- **一次情報大事**という気づき
- **Railsチュートリアルの製作者たちが様々な工夫で環境構築のハードルを劇的に下げてくれていることへの感謝と、それ以上の柔軟性と実用性を提供してくれるDockerの素晴らしさ**

# 結論
手順通りに進むと、表題のRails開発環境が手に入る、はずです

初学者がポートフォリオ作成のロードマップの中でDockerを導入するのであれば、
先人のお知恵を借りて、既存の開発環境を自分のローカル環境に取り入れるくらいの感覚のほうが良いかもしれない

時間をかけてRailsチュートリアルに取り組める、かつ私のような物好きは
Dockerで環境構築から初めて、環境の差による不具合を敢えて乗り越えて行くのも粋かもしれない

# 環境・スキル

- Mac OS (Mojave) 
- Docker for Mac, Docker composeインストール済み
- Ruby 2.5
- Rails 5.2.4.3
- Nginx 1.15.8
- PostgreSQL 
- Rails チュートリアル1周目、Docker初心者

# 参考

Docker公式docs: 基本的にこの流れを踏襲しています
[Quickstart: Compose and Rails | Docker Documentation](https://docs.docker.com/compose/rails/)

nginx公式docs: 設定例
[Full Example Configuration | NGINX](https://www.nginx.com/resources/wiki/start/topics/examples/full/)

nginxほか設定例
[nginxについてまとめ(設定編) - Qiita](https://qiita.com/morrr/items/7c97f0d2e46f7a8ec967)

pumaの設定はこちらを参考
[既存のRailsアプリのローカル開発環境をDockerの仮想環境に切り替える - りょ〜Blog](https://ryo10leo.hatenablog.com/entry/2020/01/31/210520#Dockerfile-1)

ボリュームマウントの構成はこちら
[Docker + Rails + Puma + Nginx + MySQL - Qiita](https://qiita.com/eighty8/items/0288ab9c127ddb683315#db%E6%8E%A5%E7%B6%9A%E7%94%A8%E3%81%AE%E6%83%85%E5%A0%B1%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB)



# 備忘録

## ディレクトリ構成

(予め用意するもののみ記載)

```
sample_app ---- Dockerfile
            |-- Gemfile
            |-- Gemfile.lock
            |-- nginx ---- Dokerfile
            |          |-- nginx.conf
            |
            |-- config ---- database.yml (後で自動生成)
            |           |-- puma.rb (後で自動生成)
            |
            |-- environments --db.env (今回うまく使えてません)
            |
            |-- docker-compose.yml
            |-- entrypoint.sh
            

```



## Dockerfile (Ruby)作成



まずはruby用のDockerfileを用意

```dockerfile:Dockerfile
FROM ruby:2.5

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

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

# puma.sockを配置するディレクトリを作成
RUN mkdir -p tmp/sockets
```



`Gemfile`（これはあとの`rails new`コマンドで上書きされる）

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



`Gemfile.lock`
空のファイルで良いので`touch`コマンドのみ

```bash:Gemfile.lock
touch Gemfile.lock
```



`entrypoint.sh`

```shell:entrypoint.sh
#!/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 "$@"
```



## Dockerfile (NGINX)作成

次にnginx用のDockerファイルを用意

```dockerfile:/nginx/Dockerfile
FROM nginx:1.15.8

# インクルード用のディレクトリ内を削除
RUN rm -f /etc/nginx/conf.d/*

# Nginxの設定ファイルをコンテナにコピー
ADD nginx.conf /etc/nginx/myapp.conf

# ビルド完了後にNginxを起動
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/myapp.conf
```



上で呼び出される
`nginx.conf`ファイル
(ログの部分はコメントアウトを外すと、ディレクトリが見つからないとエラーが出ます。どうしても解決できずコメントアウトのまま)

```properties:/nginx/nginx.conf
user  root;
worker_processes  1;

events{
    worker_connections  512;
}

# ソケット接続
http {
  upstream myapp{
    server unix:///myapp/tmp/sockets/puma.sock;
  }
  server { # simple load balancing
    listen 80;
    server_name localhost;
    
    #ログを記録しようとするとエラーが生じます
    #root /myapp/public;
    #access_log logs/access.log;
    #error_log logs/error.log;

    location / {
      proxy_pass http://myapp;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
    }
  }
}
```



## Docker-composeを作成

これらのコンテナをうまくゴニョゴニョしてくれるという`docker-compose`
`docker-compose.yml`を以下のように記述
（`env_file:`は書いているものの、うまく使えていないと思います）

```yml:docker-compose.yml
version: '3'
services:
  app:
    build: .
    env_file:
      - ./environments/db.env
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec puma -C config/puma.rb"
    volumes:
      - .:/myapp
      - public-data:/myapp/public
      - tmp-data:/myapp/tmp
      - log-data:/myapp/log
    depends_on:
      - db
  db:
    image: postgres
    env_file:
      - ./environments/db.env
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password
  web:
    build: nginx
    volumes:
      - public-data:/myapp/public
      - tmp-data:/myapp/tmp
    ports:
      - "80:80"
    depends_on:
      - app
volumes:
    public-data:
    tmp-data:
    log-data:
    db-data:
```



## Gemインストール

カレントディレクトリをDocker fileが配置されている場所に移動し
以下の`docker-compose run`コマンドでRailsを展開する
（この時Gemファイルが上書きされるはず）

```bash
docker-compose run app rails new . --force --no-deps --database=postgresql
```

Gemをインストールするために`docker-compose build`コマンドを実施
（Gemファイルおよび、Dockerファイルに変更がある場合のみ再度このコマンドが必要)

> Now that you’ve got a new Gemfile, you need to build the image again. (This, and changes to the Gemfile or the Dockerfile, should be the only times you’ll need to rebuild.)
> （Docker 公式 docs)

```ruby
docker-compose build
```





## 設定ファイルの修正

`config/puma.rb`の中身を以下で置き換える

```ruby:/sample_app/config/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
```



`config/database.yml`の中身を以下で置き換える

```yml:/sample_app/config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test
```



## コンテナを起動

`docker-compose up`コマンドでアプリたちをを起動する

```
docker-compose up
```

処理が止まっているように見えてもエラーが出ていなければサーバーが起動しているはずです、
`docker-compose up -d`でターミナルの入力を継続できます

`doker ps`で起動しているコンテナを確認すると
nginx(sample_app_web), puma(sample_app_app), DB(postgres)の3つが起動しているはず

```bash
$ docker ps
CONTAINER ID        IMAGE             COMMAND   							 ...   PORTS                NAMES
7188a5bb9d3c        sample_app_web    "/bin/sh -c '/usr/sb…"   ...   0.0.0.0:80->80/tcp   sample_app_web_1
41759b7d078e        sample_app_app    "entrypoint.sh bash …"   ...                        sample_app_app_1
1c81a75b5c22        postgres          "docker-entrypoint.s…"   ...   5432/tcp             sample_app_db_1
```

この時点で`localhost:80`にアクセスすると、
Railsのエラー画面、あともう少し
データベースがないよと言われるので



## DBを作成する

```ruby
docker-compose run app rake db:create
```



## 完成

再び`localhost:80`で...
<img width="1440" alt="スクリーンショット 2020-06-27 22.14.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649386/bbb709a0-0a55-20dd-64ed-9470cb8c3ff4.png">



## 最後に

これRailsのコマンドはどうやって打つの？という残念なレベル...
`docker-compose run app`に続けて記述するそうです
appはRailsが動いているコンテナですね

```
$ docker-compose run app rails -v
Starting sample_app_db_1 ... done
Rails 5.2.4.3
```



動いているコンテナを終了するのは
（これをやらないと次回起動時エラーが出る）

```
$ docker-compose down
```

再度起動するには

```
$ docker-compose up
```