動機
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
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オプションを指定して、ボリュームのマウントを高速化するように改良しました。代わりにコンテナ・ホスト間でのファイルの同一性は保証されなくなりますが、開発用途で問題が出ることはあまりないと思われます。
コマンド
上の環境が作成できたら、
cd app
docker-compose build
docker-compose up
でサービスが立ち上がる。
(doryを使う場合でまだ立ち上がっていないときは、dory up
も)
課題
Gemfileを変更したときにdocker-compose build
すると、毎回始めからbundle install
が走るのだが、これはどうしようもないのかな。けっこうな時間がかかるのでなんとかしたいのだが、なかなか難しい。。。