docker-compose + railsで開発環境構築

## 動機
DockerをOSX上で動かすのに、今まではdinghy+docker machine+virtualboxを使ってきましたが、先日満を持してDocker for Macへ移行しました。これを機に開発環境を本格的にDockerへ移行しようと思い立ちました。

## 構成
ウェブアプリケーションからAPIサーバーへアクセスする構成を想定しています。APIサーバーはrailsアプリケーションとして構築されており、railsからpostgreSQL・Fluentdを利用するイメージです。

### ディレクトリ構成
```
app/
 | 
 |------- docker-compose.yml
 |
 |
 |------- rails/ (railsのルートディレクトリ)
 |           |------- Dockerfile
 |           |------- Gemfile
 |           |------- Gemfile.lock
 |           |------- init.sh (DockerfileのCMDディレクティブで実行される起動用スクリプト)
 |           |------- railsのソースコード
 |
 |
 |------- web/ (ウェブアプリケーションのルートディレクトリ)
 |         |------- Dockerfile
 |         |------- package.json
 |         |------- ウェブアプリケーションのソースコード
 |
 |
 |------- data/pg/ (dbサービスのデータ永続化のためのマウント用ディレクトリ)
```


### docker-compose.yml
```yaml
version: '2'
services:
  fluentd:
    image: fluent/fluentd
    ports:
      - "24225:24224"
  db:
    image: postgres
    volumes:
      - ./data/pg:/var/lib/postgresql/data:delegated
  api:
    build: ./rails
    ports:
      - "3001:3000"
    links:
      - fluentd
      - db
    tty: true
    stdin_open: true
    environment:
      RAILS_ENV: development
      VIRTUAL_HOST: api.docker
      FLUENTD_U: fluentd
    volumes:
      - ./rails:/app:cached
  front:
    build: ./web
    ports:
      - "9001:9000"
    volumes:
      - ./web:/app:cached
    environment:
      - VIRTUAL_HOST=front.docker
```

### app/rails/Dockerfile
```
FROM ruby:2.3.3
ENV LANG C.UTF-8
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev apt-utils
RUN mkdir /app
WORKDIR /app
ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock
RUN bundle install
ADD . /app
CMD ["bash", "./init.sh"]
```

### app/rails/init.bash (例)
```
cd /myapp
bundle exec ./init/delayed_job start # deloayed jobを起動するスクリプト
bundle exec puma -C ./init/puma.rb # pumaを起動するスクリプト
```

### app/web/Dockerfile
```
FROM node:6.9.1
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev libkrb5-dev
RUN mkdir /app
WORKDIR /app
ADD package.json /app/package.json
RUN npm install
ADD . /app
CMD npm run dev 
```

## ポイント
- コンテナが削除されてもDBのデータが消えないように、dbサービスにマウントを設定しておく。公式のイメージを使うと、postgreSQLの場合は`/var/lib/postgresql/data`、mySQLの場合は`/var/lib/mysql`がコンテナ内のデータディレクトリになる。

- ドメイン名を使ってコンテナ内で立ち上がっているサーバーへアクセスするとき、ポート番号だけでサービスを区別していると、ローカルで様々な開発環境が入り混じってきたときに不便。VIRTUAL_HOSTを設定しておけば、自分でわかりやすいドメイン名が付けられる。
VIRTUAL_HOSTを有効にするためにはローカルDNS・リバースプロキシが必要になるが、たとえばdory(https://github.com/FreedomBen/dory )が使える。`gem install dory`からの`dory up`で、DNS・リバースプロキシがDockerコンテナとして立ち上がる。

- エンコードの指定(`ENV LANG C.UTF-8`)をDockerfileに入れる。これがないと、コンテナ内で日本語の入力ができない。  
  
- railsアプリケーション内にinit.shという名前のファイルを置いておき、docker-composeのCMDディレクティブで実行するようにする。柔軟に起動コマンドを変えられて便利。

- `ADD . /app`をする前に`RUN bundle install`・`RUN npm install`を走らせないと、ソースコードに変更がある度に毎回`RUN bundle install`・`RUN npm install`が走ってしまうので注意。

- apiコンテナに`tty: true`と`stdin_open: true`を指定しておくと、railsのbyebug（デバッグツール）が使えるようになる。byebugを使うには、立ち上げたapiコンテナに`docker attach`でアタッチする。

### 2017年8月追記

Docker for Macでボリュームをマウントすると、コンテナ・ホスト間でのファイルの同期が遅い問題がありました。Dockerのバージョン17.04で、この問題の改善が行われ、ボリュームのマウントにconsisistent・cached・delegatedのオプションを指定できるようになりました（https://docs.docker.com/docker-for-mac/osxfs-caching/ ）。
そこで、db・api・frontの各ボリュームにcached・delegatedオプションを指定して、ボリュームのマウントを高速化するように改良しました。代わりにコンテナ・ホスト間でのファイルの同一性は保証されなくなりますが、開発用途で問題が出ることはあまりないと思われます。

## コマンド
上の環境が作成できたら、

```bash
cd app
docker-compose build
docker-compose up
```
でサービスが立ち上がる。
（doryを使う場合でまだ立ち上がっていないときは、`dory up`も）

## 課題
Gemfileを変更したときに`docker-compose build`すると、毎回始めから`bundle install`が走るのだが、これはどうしようもないのかな。けっこうな時間がかかるのでなんとかしたいのだが、なかなか難しい。。。