docker-compose で Rails の開発環境を作る
docker-compose (fig) を利用して Rails の開発環境を作ってみます。個人的には Docker を本番環境で運用するのはまだちょっと怖いなーという感じなのですが、開発環境やテスト環境でならもう全然使えるレベルかなと思います。例として Rails 4.2 + PostgreSQL 9.4 の環境セットアップ。Docker の基本的な操作はひととおり抑えているものとします。
こちらの記事を参考にしてます。
この記事で作ったプロジェクトはこっち。
コンテナ戦略
原則 1 Container 1 Service
に従います。従って超基本構成なら
- Rails コンテナ
- DB コンテナ
となります。後から Redis を追加したいとなった場合は、
- Rails コンテナ
- DB コンテナ
- Redis コンテナ
となります。1コンテナで複数のサービスを動作させるのは基本的にはやらない。
Rails の Dockerfile を準備する
Ruby が初めから入ってるイメージを選ぶと無駄がないです。以前は CentOS 等のイメージを持ってきてあれこれセットアップして…みたいなのでしたが今は色んなオフィシャルコンテナが用意されてるのでとても簡単ですね。こんな感じです。
FROM ruby:2.2.2
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev postgresql-client
RUN mkdir /app
WORKDIR /tmp
COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lock
RUN bundle install
ADD . /app
WORKDIR /app
/tmp
に Gemfile をコピーしてるのは bundle install を高速化するというやつですね。Docker 1.1 から ADD キャッシュが入ったので、Gemfile が変更されなければ再ビルドをしても、高速にセットアップすることが出来ます。Dockerfile はリポジトリのルートにおいておくのが良いかと。
docker-compose でオーケストレーション
docker-compose.yml
を同じくリポジトリのルートに準備。復数のコンテナを同時に利用できるよう設定を行います。
db:
image: postgres
expose:
- "5432"
web:
build: .
command: bundle exec rails s -p 3000 -b '0.0.0.0'
volumes:
- .:/app
ports:
- "3000:3000"
links:
- db
DB コンテナの Dockerfile は用意しませんでしたが、ここではオフィシャルの postgres イメージをそのまんま使ってます。ポート版は 5432 なので、これはそのまま expose
(コンテナ外へポート開放) してます。
Rails コンテナの Dockerfile は先ほど準備したものを build: .
で指定、links
によって DB コンテナを同時に起動するようにしています。Rails コンテナは Docker 外から接続させたいので expose
ではなく posts
を利用してます。expose と ports はこんな感じ。
- expose: Docker 内の他のコンテナ向けにポートを開放
- ports: Docker 外にポートを開放、Docker 内の他のコンテナからもアクセス可能
-
ports
はDockerホストポート:Dockerコンテナポート
で対応してます
オーケストレーションはこれだけでOKですが、DB は別コンテナになるので Rails の database.yml
を書き換えておきます。
development: &default
username: postgres
host: db
db
となってるのはコンテナ名ですね。
コンテナ起動
後は docker-compose
コマンドを利用してコンテナを起動するだけです。
docker-compose build
まずコンテナをビルドします。その後、DB の作成処理などが必要になるので、
docker-compose run web bin/rake db:create db:migrate db:seed
などとして Migration を掛けます。 web を対象にコマンドを実行していますが、DB コンテナにリンクしているため同時にDBコンテナも起動します。終わったら
docker-compose up
基本的にはこれだけで2つのコンテナが上がってくると思います。Rails コンテナのコマンドに rails server
を指定してるので docker-compose up
するとサーバーのログがそのまま出ると思います。この状態で localhost:3000 にアクセスすると Rails が起動していることが確認できるはず。(boot2docker 使ってる場合は boot2docker ip
で出てくるIPに変える)
この状態ができれば、リポジトリのファイルが Rails コンテナとリンクしているため、ローカルのファイルを編集すれば、コンテナ上のアプリケーションに反映されます。つまりいつも通り開発ができるということですね。
コンテナ起動後の操作
Migration を行いたいなどの場合、起動中のコンテナに対する操作は行わず、 docker-compose run
を利用して別途コンテナを起動する形になります。
- 各種操作の対応表
ローカル開発時 | Docker Compose |
---|---|
rails g model Member | docker-compose run web rails g model Member |
rake db:migrate | docker-compose run web rake db:migrate |
rails dbconsole | docker-compose run web rails dbconsole |
RAILS_ENV=test bundle exec rake db:setup | docker-compose run -e RAILS_ENV=test web bundle exec rake db:setup |
従来のローカル開発に比べてちょっとテンポ悪くなるところもあるんですが、依存するミドルウェアが多い等のコンテナが沢山あるような環境であるほど恩恵を受けられると思いますよ。
他の開発メンバーに環境を配る
Dockerfile
と docker-compose.yml
をリポジトリに含めておけば、リポジトリを clone してもらって、docker 動くようにしてから
$ docker-compose build
$ docker-compose run web bundle exec rake db:setup
$ docker-compose up
を叩いてもらうだけです。ruby や mysql をインストールみたいなのも必要ないですしね。ただ、メンバーが Docker 動く環境を持ってないとつらいですね…
その他
雑感とかTipsとか。
ローカル開発環境を完全に捨てることはまだ難しい
rails new
する時とか、ちょっとこの gem 試したいなー、の時とか Docker 外の環境を利用することはゼロではないので、完全にコンテナベースとするのはまだ難しい印象です。やり方が悪いのかもしれませんが。
ただ、メイン開発メンバーでない限り例えば、デザイナーさんに環境を渡して開発してもらう、等の場合にはかなりの威力を発揮すると思います。ちょっとコマンド叩いてもらうだけでアプリケーション起動しますからね。
インフラ構築にストレスが無い
docker-compose.yml
にちょろっと掛けば Redis やら Memcached やらすぐに利用できるようになるのメッチャ快適です。しかもアプリケーション固有のバージョンを指定して利用できますからバグの再現性なども高く快適です。
Gemfile の更新がちょっとストレス
仕方ないとはいえ、Gemfile を更新するとイメージを再ビルドすることになってしまいます。コンテナ再構築時には、1から bundle install
することになるので結構な時間がかかります。こういうテクニックも考えられますが、まあそこまでやるかなあ…という気はしますね。
# Rails 等基本的な Gemfile をまとめて install することでちょっと時間を短縮
RUN gem install rails pg rspec guard factory_girl ....
# アプリケーション本体の bundle install
COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lock
RUN bundle install
docker-compose
コマンド長い!
元は fig
というツールだったので私の場合はエイリアスを割り当てててます。
alias fig='docker-compose'
fig build
fig up
fig run web rails console
とかですね。