はじめに
Dockerでの開発環境構築については色々と情報が転がっていましたが、情報が古かったり構成が違ったりで試行錯誤ありましたのでひとまずまとめました。ちなみに本番環境は今のところDocker全く使っておらず、開発環境を手軽に整えるという目的のみで使っています。
開発中のサービスは現在下記のコンポーネントで構成されています。
- Ruby on Rails 4
- MySQL
- memcached
- redis
- nginx
Docker導入前と後での変化
弊社では各個人のPCに環境を構築して開発しています。
mysqldやmemcached、ImageMagickなど依存しているモジュールを入れて環境をセットアップするのですが、まぁビルドは時間かかるしバージョンが違ったりでなんだかんだ丸一日費やすことが多かったです。
Dockerで開発環境をセットアップできるようにした結果、PCがまっさらな状態であっても30分もあればセットアップが完了して開発がスタートできるようになりました。
まだ導入したばかりなのですが直近で実感した効果としては、インターン生が入ってきた時に最初のモチベーションのまますぐ開発に入ってもらえるのがだいぶ大きいなというのがありました。
やったこと
1. 必要なツール群の導入
VirtualBoxなども全て入った Docker Toolbox がリリースされているので、それだけでおkでした。
https://www.docker.com/docker-toolbox
brew cask入れてる人は
$ brew cask install dockertoolbox
で入ります。
2. 元となるイメージの構築
Ruby(rails)が動くイメージが必要です。もとからRubyが入ってるイメージファイルは公式であるのですが、今回はAmazon Linuxと同等の環境を作るためにCentOSをベースに作ろうと思い、自前でイメージを作成することにしました。Ruby以外にもImageMagickなど現在のサービスに必要なものも yum で突っ込んでます。
ちなみに最初は Amazon Linux のAMIからDockerイメージを作成して利用しようと思ったのですが、 yum がAWS環境でしかアクセス出来ないという初歩的な問題に引っかかり諦めました。
ということでイメージの作成ですが、まずはDockerfileを用意します。
FROM centos:centos6
RUN yum -y update
RUN yum -y install gcc git rsync tar openssl openssl-devel readline-devel zlib-devel libffi-devel gdbm-devel tk tk-devel tcl tcl-devel patch gcc-c++ which sqlite-devel wget openssh-server file
RUN yum -y install http://dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm
RUN yum -y install mysql mysql-devel ImageMagick
# rbenvのインストール
RUN git clone https://github.com/sstephenson/rbenv.git /root/.rbenv
RUN git clone https://github.com/sstephenson/ruby-build.git /root/.rbenv/plugins/ruby-build
RUN ./root/.rbenv/plugins/ruby-build/install.sh
ENV PATH /root/.rbenv/shims:/root/.rbenv/bin:$PATH
RUN echo 'export PATH=/root/.rbenv/shims:/root/.rbenv/bin:$PATH' >> /root/.bashrc
RUN echo 'eval "$(rbenv init -)"' >> /root/.bashrc
# rubyのインストール
ENV CONFIGURE_OPTS --disable-install-doc
RUN rbenv install 2.2.2
RUN rbenv global 2.2.2
RUN rbenv rehash
RUN rbenv exec gem install bundler
ENV APP_HOME /app
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
ADD Gemfile* $APP_HOME/
RUN bundle install
最後に bundle install
してますが、各個人の環境で docker-compose build
を行うときにやれば良いかなと思ったのですが、諸事情もありinstallしたGemを永続化できる領域にうまく置くことができませんでした。その場合Dockerの性質上、Gemfile更新のたびに docker-compose build
をする必要があるわけですが、その時に毎回最初から各gemの導入から始まるのは時間がかかりすぎてつらたんだったので、ある時点でのGemが入った状態にしておくことでBuildを短時間で終わらせられるようにしています。
上記ファイルを用意したら docker build
します。(その前に docker-machine create
だけやる必要があります。)
docker-machine create --virtualbox-disk-size 3000 -d virtualbox dev
eval "$(docker-machine env dev)"
docker build -t my-dockerhub-repository/centos6-ruby2.2.3 .
また、作られたイメージを各個人のPCから使えるできるようにするためにDockerhubにアップしました。プライベートリポジトリにする必要もないので無料プランでOKです(たぶん)。
docker push my-dockerhub-repository/centos6-ruby2.2.3
3. 設定ファイルの準備
解説は後で、まずは設定ファイルをドーンと載せちゃいます。下記はRailsのルート直下に配置しています。
#公開するとあれなところはいくつか情報書き換えてるのでtypoなどによりそのままだと動かないかも
FROM my-dockerhub-repository/centos6-ruby2.2.3
ENV APP_HOME /app
WORKDIR $APP_HOME
ADD Gemfile* $APP_HOME/
RUN bundle install
ADD . $APP_HOME
EXPOSE 3000
CMD ["bundle", "exec", "rails", "s"]
mysql:
image: mysql:5.6.23
environment:
MYSQL_ROOT_PASSWORD: password
ports:
- '3306:3306'
volumes:
- ./docker/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
volumes_from:
- datastore
redis:
image: redis:2.8.19
ports:
- '6379:6379'
volumes_from:
- datastore
memcached:
image: memcached:1.4.24
ports:
- '11211:11211'
volumes_from:
- datastore
datastore:
build: docker/datastore
web:
build: .
ports:
- '3000:3000'
environment:
RAILS_ENV: development
MYSQL_ROOT_PASSWORD: 'password'
DATABASE_HOST: mysql
REDIS_HOST: redis
MEMCACHED_HOST: memcached
dns:
- 8.8.8.8
volumes:
- .:/app
volumes_from:
- datastore
links:
- mysql
- redis
- memcached
FROM busybox:latest
VOLUME /var/lib/mysql
VOLUME /data
CMD /bin/sh
簡単に解説
memcached、mysql、redisなどの依存関係は docker-compose.yml で簡単に定義できます。
mysqlは utf8md4 に関する設定が必要だったので、 ローカルに配置した my.cnf を利用するように定義しています。
DBの情報は永続化しないといけないので、busyboxを使って /data
、 /var/lib/mysql
を保存するようにしています。docker/datastore/Dockerfile
を用意して docker-compose.yml の中で datastore として定義しています。
メインのDockerfileは見ての通り簡単な構成でGemfileをコピーしてbundle installしているだけです。runした時に rails s が実行されて、3000番ポートがコンテナのポートにマッピングされます(この表現が正しいのか不明)。
rails 側の database.yml などの設定ファイルの書き換え
Dockerで開発環境を構築できるものの、今までローカルに環境を構築していた人はそのままで開発したいケースもありますし、導入後しばらくは問題があった場合に以前の開発スタイルに戻せるように、Dockerがなくても問題なく開発できるようにしました。
具体的には、環境変数が定義されていればそれを使いそうでなければ localhost を見るという設定にしています。
development:
<<: *default
database: db
encoding: utf8mb4
charset: utf8mb4
collation: utf8mb4_general_ci
host: <%= ENV["DATABASE_HOST"] || "localhost" %>
port: 3306
password: password
DATABASE_HOSTという環境変数は docker-compose.yml で定義していますのでご確認ください。
memcachedやredisのホストも同様に設定しています。
4. 各個人PCでのセットアップ
初期状態のMacでも git と dockertoolbox (とエディタ)だけ入れて開発がスタートできました。
セットアップ
$ docker-machine create --driver virtualbox dev
$ eval "$(docker-machine env dev)"
$ docker-compose build
マイグレーション
$ docker-compose run web bundle exec rake db:create db:migrate db:seed RAILS_ENV=development
$ docker-compose run web bundle exec rake db:create db:migrate RAILS_ENV=test
**立ち上げ**
$ docker-compose up
アクセスするURL(デフォルト)
http://192.168.99.100:3000
テスト実行
$ docker-compose run web bundle exec rake spec
注意:Gemfileを更新したら
$ docker-compose build
を忘れずにやる。
FAQ
ビルド終わってて再起動したあとはどのコマンドを実行すればよいの?
下記3コマンドを実行してください
$ docker-machine start
$ eval "$(docker-machine env dev)"
$ docker-compose up
pry使いたい
docker-compose run --service-ports app
で立ち上げるとできる。
ただこの立ち上げ方だと終了時にプロセスがうまく Kill されずに残って次の起動時にポートが取られてる場合があるのでめんどくさいこともあった。
なんか docker-compose up で立ち上がらなくなった
たまにあるけど原因がよくわからないことも。
$ docker-compose stop
$ docker-compose kill
あたりを試してみる。だめなら
$ docker-machine restart
$ eval "$(docker-machine env dev)"
とか。
それでもだめなら
$ docker-machine rm dev
をして最初からセットアップする。
mysqlとかが起動しない
ローカルでmysqld や memcache が動いていないか確認する。ポートがかぶっていると立ちあげられないので。
vendor/bundle が共有されてしまう問題
.dockerignore を設定してもうまくいかなかったので解決してません。 ローカルで bundle install --path vendor/bundle と言った形でパスを指定してbundle installを行っていると、その .bundle/config がDocker側でも利用されてしまいうまく rails が起動しないことがあった。.bundle/configを削除したら動いた。
おわりに
Docker側にsshでログインしてそこで直接ファイルを編集したりするスタイルのが良いかなとも思ったのですが、色々試してこの形に落ち着きました。他にも docker-osx-dev を使う構成にしてみたりとか何が最適なのかは難しいですね。
まだ運用始めて間もないので問題も出てくるかなと思ってます。
何かあればコメントいただけると幸いです。