Docker で Rails5 の開発環境を「rails new」から構築する


## 追記(2017/02/13)

以下の方法では、Gemfileを修正するたびにDockerイメージをビルドし直す必要がありますが、もっと高速に開発できるテンプレートを作成しました。

[高速に開発できる Docker + Rails開発環境のテンプレートを作った](http://qiita.com/kawasin73/items/2253523be18e5afd994f)

## はじめに

Dockerを使ってRails5の開発環境を構築します。
また、railsコマンドを`spring`を通して実行できるようにします。
`rails new` から `rails db:create`まで、すべてをDockerの上で行うことを目指して構築します。
また、実際のコードは[こちら](https://github.com/kawasin73/docker-rails5-sample)にアップロードしています。

## 環境

私の手元の環境は、OS X で行いましたが、基本的にDockerが使えればできます。

## 手順

### 1. `rails new` する環境を作成する

以下のファイルを作ります。

- `Dockerfile.development`
- `docker-compose.yml`
- `Gemfile`
- `Gemfile.lock`

#### `Dockerfile.development`

```Dockerfile.development
FROM ruby:2.3.1

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

RUN mkdir /app
WORKDIR /app
ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock
RUN bundle install -j4
ADD . /app
```

#### `docker-compose.yml`

```docker-compose.yml
version: '2'
services:
  rails: &app_base
    build:
      context: .
      dockerfile: "Dockerfile.development"
    command: "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:
      - ".:/app"
    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"
```

#### `Gemfile`

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

#### `Gemfile.lock`

`Gemfile.lock` は、`Dockerfile.development`の`ADD Gemfile.lock /app/Gemfile.lock` を実行させるために空のファイルを作ります。

```bash
touch Gemfile.lock
```

### 2. `rails new` する

`rails new` します。今回は、APIモードで`rails new` してみました。オプションはそれぞれのケースに合わせて選ぶと良いと思います。

```bash
docker-compose run --rm rails rails new . --force --database=postgresql --skip-bundle --skip-test --api
```

### 3. `Gemfile` を修正し、`Gemfile.lock`を更新する

Gemfileをお好みで修正し、`Gemfile.lock`を生成します。

`Gemfile.lock`は以下のコマンドで更新されます。

```bash
docker-compose run --rm rails bundle install
```

また、`Gemfile.lock`を更新した後は、Dockerのイメージを更新する必要があります。

```bash
docker-compose build
```

今回は、`pry-rails`などを追加してみました。

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


# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '5.0.0'
# Use postgresql as the database for Active Record
gem 'pg', '~> 0.18'
# Use Puma as the app server
gem 'puma', '~> 3.0'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 3.0'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development

# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
# gem 'rack-cors'

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug'
  gem 'bullet'
  gem 'factory_girl_rails'
  gem 'pry-rails'
  gem 'pry-byebug'
end

group :development do
  gem 'listen', '~> 3.0.5'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
  gem 'annotate'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
```

### 4. database の設定を行う

databaseの設定を行います。
`config/database.yml` を修正した後に、以下のコマンドでデータベースの初期化を行います。

```bash
docker-compose run --rm rails rails db:create
```

#### `config/database.yml`

Dockerで環境構築をする場合は、設定事項は、環境変数で設定した方が簡単です。

```config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  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: app_development

test:
  <<: *default
  database: app_test

production:
  <<: *default
  database: app_production

```

### 5. `spring` の`binstub`を作成する

以下のコマンドを実行します。

```bash
docker-compose run --rm rails bundle exec spring binstub --all
```

### 6. 完成

以上で完成です。実際に環境を立ち上げてうまく表示されることを確認しましょう。

```bash
docker-compose up
open http://$(docker-machine ip):3000
```

### 7. 本番環境の`Dockerfile`を作る

本番環境ではよりスリムなイメージを作りたいと思います。開発環境では、`Dockerfile.development`を利用しているので、本番環境では、`Dockerfile`を作成してビルドするレシピを書けば、開発環境と本番環境のイメージを別にすることができます。

## 開発を進めるには

### spring でコマンドを高速実行

`spring` コンテナーを作成しているので、`rails` コマンドを`spring`を利用して高速に実行できます。
例えば、`rails db:migrate`を実行するには

```bash
docker-compose exec spring rails db:migrate
```

を実行するとdockerコンテナー内で実行されます。

### `binding.pry` を使う

`docker-compose.yml` で

```docker-compose.yml
    tty: true
    stdin_open: true
```

を設定しています。これによって、pumaのサーバーの処理に挿入した`binding.pry` を使えるようになります。

```bash
docker ps
```

コマンドで、railsコンテナのコンテナ名を調べてください。

```bash
docker attach xxxx_rails_1 # xxxx_rails_1 は調べたコンテナ名
```

とすると、binding.pryをDockerの中で使えるようになります。

## まとめ

Dockerの公式サイトでのチュートリアルでは、`spring`を使った開発などが考慮されていないので使いにくいと思いました。
この手順で環境を構築するのもいいのではないかと思います。
アドバイスなどあれば是非お願いします！

## 参考URL

- [Quickstart: Docker Compose and Rails](https://docs.docker.com/compose/rails/)
- [Railsアプリケーション開発を完全にDocker化する](http://tech.degica.com/ja/2016/06/14/dockerized-rails-development/)
- [docker-compose patterns 201603](https://speakerdeck.com/pottava/docker-compose-patterns-201603)
- [Docker を使ったサービス構築速習会](https://speakerdeck.com/dtan4/docker-woshi-tutasabisugou-zhu-su-xi-hui)
- [dtan4/docker-service-rails](https://github.com/dtan4/docker-service-rails)
- [How to debug a rails app in docker with pry?](http://stackoverflow.com/questions/35211638/how-to-debug-a-rails-app-in-docker-with-pry)