10ステップでMac OS XにRails5のDocker開発環境を構築する

## 動機
私はRubyOn Railsのエンジニアです。
わたしのPCでは、`rvm`, `macports`をつかって開発していて、rubyのバージョン管理やその他周辺ライブラリのインストールではまったりすることが多かったことから、Dockerによる開発環境の仮想化を検討しました。


## Dockerについての理解
Dockerはアプリケーション（プロセス）単位で実行環境を仮想化します（アプリケーションの実行に必要なファイルやディレクトリをまとめた「コンテナ」という単位での管理）。

ウェブアプリケーションであれば、webサーバ、DBをコンテナをそれぞれ作成し、それらを連携させることで機能させることができます。

ウェブアプリでは必須の「データ」の永続化に関しては、記憶領域をコンテナにして、DBと連携させることでホストから切り離すことができます。（postgresのインストールでハマりまくったので、非常に感動しました）
[Docker でデータのポータビリティをあげ永続化しよう](http://qiita.com/mopemope/items/b05ff7f603a5ad74bf55)

これを使うことで、ローカルでの開発環境の手間を省けるだけでなく、環境をイメージとして、公開し、再配布できるというメリットもあります。


## ホスト環境
- Mac OS X Yosemite (10.10.5)
- メモリ　4G

## 構築する環境
 - Ruby On Rails (5.0.0)
 - Ruby (2.3.1)
 - PostgreSQL (9.5)


## Docker for Macのインストール
 - https://docs.docker.com/engine/installation/mac/
 こちらに従ってインストールします。`macOS 10.10.3 Yosemite or newer`では**Docker for Mac** を、それ以外では**Docker Toolbox**をインストールします。今回は**Docker for Mac**がインストールされたものとして進めます。

## コマンド
細かいコマンドの説明は省きます。以下のステップで必要なものだけ説明します
- [こちらなどを参照し、学習してください](http://docs.docker.jp/v1.11/mac/toc.html)

## 参考ページ
以下のページを参考にさせていただきました。ありがとうございました。
-[Docker で Rails5 の開発環境を「rails new」から構築する](http://qiita.com/kawasin73/items/d5836007c715b7f7885f)
-[Railsアプリケーション開発を完全にDocker化する](http://tech.degica.com/ja/2016/06/14/dockerized-rails-development/)
- [こちらも](https://github.com/vovimayhem/docker-compose-rails-dev-example/blob/master/docker-compose.yml)


## 手順1:プロジェクトルートディレクトリの作成
まずはプロジェクトルートを作成します。今回のプロジェクト名は`thinklog`です。
作成したらプロジェクトルートに移動しておきます

```shell-session
mkdir thinklog
cd thinklog
```

## 手順2:railsをインストールするためのファイル作成
railsをinstallするためにGemfile, Gemfile.lockを作成します

```shell-session
touch Gemfile
touch Gemfile.lock
```
Gemfileは以下の内容を書きます

```ruby:Gemfile
source 'https://rubygems.org'
gem 'rails', '5.0.0'
```


## 手順3:rails(webサーバコンテナ)作成用のDockerfileを作成
railsのコンテナは、docker-compose.ymlからDockerfileを参照しイメージを作成することにします。

```shell-session
touch Dockerfile.development
```

```:Dockerfile.development
FROM ruby:2.3.1

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

# ARG命令でdocker-compose.ymlから渡されたAPP_HOMEという引数を参照できるようにします
ARG APP_HOME

# dockerの環境側へプロジェクトルートを作成
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME

# ADD命令でホストにあるファイルをdockerの環境内にコピーします。
ADD Gemfile $APP_HOME/
ADD Gemfile.lock $APP_HOME/
RUN bundle install -j4
ADD . $APP_HOME

```

## 手順4:railsアプリケーションを構成するすべてのコンテナの定義と連携をdocker-compese.ymlにかく


ポイントは以下になります
 - webコンテナを作成するときにargsとしてカレントディレクトリを渡しており、プロジェクト名に依存しない作り
 - dataコンテナを作り、データ永続化用のコンテナをつくり、ホストの依存を断ち切っていること
 - *Ctrl C* でkillすると  tmp/pids/server.pid が削除されず、サーバを再起動できない問題があったので以下*command*の箇所を修正しました


```yaml:docker-compose.yml
version: '2'
services:
  web: &app_base
    build:
      context: .
      dockerfile: "Dockerfile.development"
      args:
        - APP_HOME=${PWD}
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"

    environment:
      - "DATABASE_HOST=db"
      - "DATABASE_PORT=5432"
      - "DATABASE_USER=postgres"
      - "DATABASE_PASSWORD=mysecretpassword1234"
    volumes:
      - .:${PWD}
    ports:
      - "3000:3000"
    links:
      - db
    tty: true
    stdin_open: true
  spring:
    <<: *app_base
    command: "bundle exec spring server"
    ports: []
    tty: false
    stdin_open: false
  db:
    image: "postgres:9.5"
    environment:
      - "POSTGRES_USER=postgres"
      - "POSTGRES_PASSWORD=mysecretpassword1234"
    volumes_from:
      - data
  data:
    image: "busybox"
    volumes:
      - "/var/lib/postgresql/data"


```
## 手順5:rails newを実行して、イメージを作成する
以下のコマンドを実行すると、プロジェクトが新規作成され、それをもとにイメージが作成されます。
この段階ではまだコンテナの雛形である「イメージ」の段階です
```shell-session
docker-compose run --rm web rails new . --force --database=postgresql --skip-bundle
```

## 手順6:bunldle installし、Gemfile.lockを更新
rails newを実行するとGemfileがRails5のベースのGemfileに差し代わっています。
このタイミングで**bundle install**を実行し、Gemfile.lockを更新します.
ここではイメージ側のGemfile.lockが書き換わっただけです。

```shell-session
docker-compose run --rm web bundle install
```

## 手順7: 再buildし、Gemfileの更新結果をイメージに反映
これをして初めて、イメージ側にgemが反映されます。
ホスト側とイメージ側で２回bunlde installが走るのがいけてないです。
(しかもフルで走る)

```shell-session
docker-compose build
```

## 手順８: データベース設定
**docker-compose.yml**で環境変数が渡されているのでそれを読み取るようにします。

```yaml:config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  # For details on connection pooling, see rails configuration guide
  # http://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV.fetch('DATABASE_USER') { 'root' } %>
  password: <%= ENV.fetch('DATABASE_PASSWORD') { 'password' } %>
  host: <%= ENV.fetch('DATABASE_HOST') { 'localhost' } %>
  port: <%= ENV.fetch('DATABASE_PORT') { 5432 } %>

development:
  <<: *default
  database: thinklog_development

test:
  <<: *default
  database: thinklog_test

production:
  <<: *default
  database: thinklog_production
  

```

## 手順９: データベース作成
```shell-session
docker-compose run --rm web rails db:create
```

## 手順１０: 起動

```shell-session
docker-compose up
```

## 確認
```shell-session
docker ps
```
```shell-session:結果
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
e87c36fb1fa8        postgres:9.5        "/docker-entrypoint.s"   30 seconds ago      Up 12 seconds       5432/tcp                 thinklog_db_1
0e7b50b979b8        thinklog_web        "bundle exec rails s "   3 minutes ago       Up 11 seconds       0.0.0.0:3000->3000/tcp   thinklog_web_1
2ab3e48b66d2        thinklog_spring     "bundle exec spring s"   3 minutes ago       Up 11 seconds                                thinklog_spring_1

```
コンテナが起動しました。しかし、データボリュームコンテナは常駐していない模様...(必要な時しか起動しないのか)

でけた！！
![スクリーンショット 2017-01-11 6.24.05 PM.jpg](https://qiita-image-store.s3.amazonaws.com/0/105028/4d3a92fb-f21b-6bb6-4bdb-d836871beb67.jpeg)

## ちなみに
```shell-session:コンソール起動はこれ
docker-compose run --rm web rails s
```
## まとめ
 - ホストに依存しない環境は構築できました。しかし、docker-composeコマンドをたたかないといけなかったり bundle installを何回もしないといけないなど、デメリットもあります。
 - あと気になったのが、データボリュームコンテナは、アクセスされたタイミングで起動と終了していて、これはいいのかどうか.....
 - しかしながら一回作った環境をファイルだけで使いまわせる（ほかのメンバとシェアできる）のは大きな魅力です！今後はワーカージョブを起動したりする設定を追加していきたいと思います！！！！！