動機
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が走るのだが、これはどうしようもないのかな。けっこうな時間がかかるのでなんとかしたいのだが、なかなか難しい。。。