Dockerを使って開発環境を構築する

こんにちは。エンジニアの森田です。
MFクラウド会計の開発を担当しています。

今月入社したばかりなのですが、実は入社前からDockerで開発環境を作ろうと心に決めていました。
なぜなら、下記のような問題によく悩まされていたからです。

  • OSやRubyのバージョンアップの度にbundle installがこける。
  • Windowsを使っているデザイナさん向けに動作環境を作るのが大変。
  • ブランチを切り替えるとDBのスキーマが違うため動かない。
  • ライブラリを新しいものに差し替えたら既存のアプリがおかしくなる。

そこで実際にDockerを導入してみました。
今回はDocker導入に際しての問題とその対応について紹介します。
 

発覚した問題と対応

コーディングの際はMac上のエディタを使いたいので、Dockerのディレクトリ共有機能を使ってコンテナとホストでプロジェクトのディレクトリを共有していたところ、Webサーバのレスポンスがめちゃくちゃ遅くなってしまいました。(VirtualBox上のDockerだから?)

また、ファイルを更新してもRailsが認識してくれず、Railsを再起動しないと反映されない状態になりました。

共有を諦めて仮想環境上でファイルを更新すればいいのですが、それはそれでいろいろ制限があります。

そこで、Dockerの共有機能を使うのではなく、コンテナ側にsshdを立てて、ホスト側(Mac)にlsyncdをインストールして使うことにしました。
それにより、ホストとコンテナのファイルがほぼリアルタイムに同期され、Webサーバのレスポンスも快適になり、ファイルの更新も反映されるようになりました。

かなり快適に開発できるようになったので、設定を共有したいと思います。

Dockerの基本的な操作に関しては前佛 雅人様が素晴らしい記事を上げてくださっているのでそちらを参照してください。

Docker 入門ハンズオン資料
 

Docker

インストール

dockerのインストールはMacでもWindowsでもboot2dockerを使えば簡単なので割愛させていただきます。

コンテナ作成から起動まで

プロジェクトで必要なライブラリを含めたImageを作成するためにDockerfileを作成し
ます。

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

# 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/bin:$PATH
RUN echo 'export PATH=/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 ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -C '' -N ''
RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -C '' -N ''
EXPOSE 22 3000
CMD ["/usr/sbin/sshd", "-D"]

上記のDockerfileはCentOS上でrubyアプリ(Rails等)を作成するための環境用です。

Dockerfileからイメージ(ruby_app:1.0)作成

docker build -t ruby_app:1.0 .

作成したイメージ(ruby_app:1.0)を使ってコンテナ(mf_dev)を起動

docker run -d -p 80:3000 -p 2022:22 --name mf_dev -i -t ruby_app:1.0

ホストからコンテナ(mf_dev)にログイン

docker exec -it mf_dev bash

コンテナ(mf_dev)上でssh keyを登録

mkdir ~/.ssh
vi ~/.ssh/authorized_keys #ホスト(Mac)のid_rsa.pubを記述

 

lsyncd

インストール

Macのrsyncが古くてlsyncdが動かないため、rsyncを置き換えています。

brew update
brew install rsync lsyncd lua
sudo mv /usr/bin/rsync /usr/bin/rsync_orig
sudo ln -s /usr/local/bin/rsync /usr/bin/rsync

Boot2dockerのIP確認

boot2docker ip

設定ファイル作成

lcync.cnf

settings {
    logfile = "/tmp/lsyncd.log",
    statusFile = "/tmp/lsyncd-status.log",
    maxProcesses = 2,<-最大プロセス数
    delay = 3,<-変更反映をまつ時間
    nodaemon  = false,<-trueだとデーモンにしない
}

sync {
   default.rsync,
   source = "/Users/morita/sample/",<-ホスト(Mac)側のディレクトリ
   target = "192.168.59.103:/root/sample/",<-Boot2dockerのIP:コンテナ側ディレクトリ
    rsync  = {
        archive   = true,
        links     = true,
        update    = true,
        verbose   = false,
        rsh       = "/usr/bin/ssh  -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2022 -i /Users/morita/.ssh/id_rsa",<-秘密鍵の場所を指定する
    }
}

起動

#/dev/fseventにアクセスするためroot権限が必要
sudo lsyncd lsync.conf

ホスト(Mac)からコンテナへの一方通行の同期のため、コンテナ上でrails g migration等でファイルを作成、更新した場合は自分でホスト側に反映する必要があります。
 

コンテナ上でRailsアプリ実行

gem install bundler --no-ri --no-rdoc
cd /root/sample
bundle install
bundle exec rake db:migrate
bundle exec rake db:seed
bundle exec spring  rails s -b 0.0.0.0

表示確認

ブラウザで下記アドレスアクセス
http://Boot2dockerのIP

tips

コンテナ上でscreenを実行すると
‘/dev/pts/1’: No such file or directory
のエラーになりますが、screen実行前に
script /dev/null
を実行するとscreenが使えるようになります。

まとめ

Docker最高。
今流行りのDockerを活用しつつ、慣れることもできるのでオススメです。

7/12追記
docker-osx-devを使うといいよ教えてくださった方がいました。
docker-osx-devのページにBoot2Dockerのディレクトリ共有で使うVirtualBoxのvboxsfに関して下記のように記載されていました。

1.ファイルアクセスが10〜20倍遅い
2.トリガーが壊れているため、inotifyによるファイルの更新を検知できない。

そのため、docker-osx-devは、VirtualBoxのvboxsfによる共有機能ををOFFにして、rsyncを使ってホスト(Mac)側とBoot2Dockerの仮想マシンとOne wayで同期を取るようデーモンを起動する仕組みになっています。
仮想マシンとコンテナ間はdockerの-vによるディレクトリ共有機能を使います。
lsyncdによる同期とは違って、コンテナにrsync,sshdをインストールする必要がないです。

docker-osx-devを実際に使ってみましたが、簡単にインストールでき、問題なく動作しました。

ただ、docker-osx-devを再起動したところ、下記のようなワーニングが出力されました。

chown: /Users/morita/.boot2docker/boot2docker-vm.sock: Operation not supported

同期自体は動くのですが、下記のようにマシン負荷が高まりつづけてしまいます。

Command      %CPU
VBoxHeadless 162.6

仮想マシン内を見ると、同期指定していないディレクトリまでコピーされていたりしてちょっと謎です。

また記事にあるlsyncd形式にすると、下記のメリットもあるので、個人的にはlsyncd形式でいこうかと思っています。

  • 同期するディレクトリをDocker run実行後以降も選択できる
  • 同期していないディレクトリのファイルもホスト側で編集できる。(GUIのscpツールやエディタのリモート編集etc)

最後に

マネーフォワードでは、Railsエンジニアを募集しています。
お互いに改善案を出し合いながら、サービスと共に一緒に成長出来る仲間をお待ちしています!

【採用サイト】
『マネーフォワード採用サイト』 https://recruit.moneyforward.com/
『Wantedly』 https://www.wantedly.com/companies/moneyforward

【公開カレンダー】
マネーフォワード公開カレンダー

【プロダクト一覧】
家計簿アプリ・クラウド家計簿ソフト『マネーフォワード』 https://moneyforward.com/
家計簿アプリ・クラウド家計簿ソフト『マネーフォワード』 iPhone,iPad
家計簿アプリ・クラウド家計簿ソフト『マネーフォワード』 Android
クラウド型会計ソフト『MFクラウド会計』 https://biz.moneyforward.com/
クラウド型請求書管理ソフト『MFクラウド請求書』 https://invoice.moneyforward.com/
クラウド型給与計算ソフト『MFクラウド給与』 https://payroll.moneyforward.com/

Pocket