高速に開発できる Docker + Rails開発環境のテンプレートを作った

追記

続編書きました。
よりスリムになって、webpackerにも対応しました。こっちの方を読んでください。

DockerでRails+Webpackerの開発環境を構築するテンプレート

はじめに

docker-composeを使い、railsを簡単に開発できるテンプレートを作成しました。
特徴は以下の通りです。

  • bundle installをイメージビルドのときではなく、ENTRYPOINTで行う
  • springコンテナを用意して高速にrailsコマンドを実行できる

テンプレートは、github にアップロードしています

使い方

テンプレートからプロジェクトを立ち上げる場合は、以下のコマンドです。RubyバージョンやRailsのバージョンを調整できます。

git clone https://github.com/kawasin73/rails_docker_template.git
cd rails_docker_template
script/init && script/bootstrap

雛形から始めたい場合は、以下のコマンドで即座に開発を開始できます。

git clone https://github.com/kawasin73/rails_docker_template.git
cd rails_docker_template
git checkout -b ruby-2.4.0-rails-5.0.1 origin/ruby-2.4.0-rails-5.0.1 && script/bootstrap

テンプレートの内容解説

テンプレートの中身は

  • Dockerfile.dev
  • docker-compose.yml
  • Gemfile
  • .env.dev.sample
  • template/database.yml
  • .gitignore
  • script/init
  • script/bootstrap

です。

Dockerfile.dev

Dockerfile.dev
FROM ruby:2.4.0

ENV LANG C.UTF-8

RUN apt-get update -qq && \
 apt-get install -y --no-install-recommends \
  build-essential \
  libpq-dev \
  libfontconfig1 && \
  rm -rf /var/lib/apt/lists/*

ENV ENTRYKIT_VERSION 0.4.0

RUN wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && mv entrykit /bin/entrykit \
  && chmod +x /bin/entrykit \
  && entrykit --symlink

RUN mkdir /app

WORKDIR /app

RUN bundle config build.nokogiri --use-system-libraries

ENTRYPOINT [ \
  "prehook", "ruby -v", "--", \
  "prehook", "bundle install -j3 --quiet", "--"]

ファイル名はあえて「Dockerfile.dev」にしています。開発環境と本番環境ではDockerfileの中身は変わってくると思うので、Dockerfileは別途作って本番環境用に使うことができます。

ENV LANG C.UTF-8 によって rails consoleの中で日本語の入力をできるようにしています。

最大の特徴は、ENTRYKITだと思います。開発しやすいRails on Docker環境の作り方を参考にしました。
ENTRYPOINT の中で bundle install を行うことでインストールした gem をDocker Volumeの中にキャッシュすることができます。
Dockerfileの中でbundle installをしないので、新しくgemを追加した時に、Dockerイメージを再度ビルドする必要がなく、高速に開発を進めることができます。

開発環境では、ローカルのrailsディレクトリを、Dockerコンテナにマウントして開発します。
そのため、Dockerfile.devでは、COPY . /appをしません。RUN mkdir /app/appディレクトリを作成するだけにとどめます。

docker-compose.yml

docker-compose.yml
version: '2'
services:
  rails: &app_base
    build:
      context: .
      dockerfile: "Dockerfile.dev"
    command: ["bundle", "exec", "rails", "s", "-p", "3000", "-b", "0.0.0.0"]
    env_file:
      - "./.env.dev"
    volumes:
      - ".:/app"
    volumes_from:
      - data
    ports:
      - "3000:3000"
    depends_on:
      - db
    tty: true
    stdin_open: true
  spring:
    <<: *app_base
    command: ["bundle", "exec", "spring", "server"]
    ports: []
    tty: false
    stdin_open: false
  db:
    image: "postgres:9.6.2"
    environment:
      - "POSTGRES_USER=postgres"
      - "POSTGRES_PASSWORD=password"
    volumes_from:
      - data
    ports:
      - "5432:5432"
  data:
    image: "busybox"
    volumes:
      - "db:/var/lib/postgresql/data"
      - "bundle:/usr/local/bundle"

volumes:
  db:
    driver: local
  bundle:
    driver: local

ほとんどそのままですが、特徴と言えるのは、springコンテナだと思います。
Railsアプリケーション開発を完全にDocker化するを参考にしました。

docker-compose up -d

した上で、

docker-compose exec spring rails db:migrate

を実行するなどすることで、springを利用することができ各種コマンドを高速に実行することができます。

Gemfile

Gemfile
# frozen_string_literal: true
# A sample Gemfile
source 'https://rubygems.org'

gem 'rails', '5.0.1'

最初の状態は、gem 'rails', '5.0.1'のみを書きます。後述のscript/initで、rails newすることでGemfileの中身を増やしてくれます。

.env.dev.sample

.env.dev.sample
DATABASE_HOST=db
DATABASE_PORT=5432
DATABASE_USER=postgres
DATABASE_PASSWORD=password

データベースの情報は環境変数で railsアプリケーションに渡します。

cp .env.dev.sample .env.dev

で利用することができます。また、パスワードなどの秘密情報を管理したくなると思うので、.env.devはgitignoreに追加します。

template/database.yml

template/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

データベースの情報は環境変数で railsアプリケーションに渡します。

cp -f template/database.yml config/database.yml

で、自動生成されるdatabase.ymlを上書きします。

.gitignore

.gitignore
/log
/tmp
!/log/.keep
!/tmp/.keep
/public/system

.env
.env.dev

/.bundle
/vendor/bundle

/.idea

.DS_Store

ここは各自ご自由に。

script/init

script/init
#!/usr/bin/env bash

cp .env.dev.sample .env.dev

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

cp -f template/database.yml config/database.yml

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

ここまでのテンプレートを用意した上で、このスクリプトを流すと、Railsプロジェクトが作成されます。
APIモードで作っていますが、rails new のオプションは各自ご自由に

script/bootstrap

script/bootstrap
#!/usr/bin/env bash

cp .env.dev.sample .env.dev
docker-compose run --rm spring rails db:create
docker-compose run --rm spring rails db:migrate
docker-compose up -d

script/initを実行して作成済みのプロジェクトを、git cloneしてきた後などに実行します。データベースの作成ができます。

注意点としては、git cloneしてきた直後に docker-compose up -dしてはいけない ということです。
ENTRYPOINTの中でbundle installが行われるのですが、初回はrailsコンテナとspringコンテナの両方で、同時にbundle installが実行されます。
この2つのコンテナのgemを保存するディレクトリ /usr/local/bundle は、Docker Volume でつながっているのでエラーが起こることがあります。一度キャッシュを作ってしまえば、2回目以降は同時に立ち上げても大丈夫です。

開発を進めるには

spring でコマンドを高速実行

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

docker-compose exec spring rails db:migrate

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

binding.pry を使う

docker-compose.yml

docker-compose.yml
    tty: true
    stdin_open: true

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

docker ps

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

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

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

最後に

以上です。

参考URL

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.