Docker上でテストを回す

最近RailsPerlで書いたWebアプリケーションのテストをDocker上で回すようにした。

Docker周りエコシステムは沢山あるけど、まだスタンダードと呼べるものがなく、環境を整えたり、変更点をキャッチアップするコストも馬鹿にならないので、とりあえずdockerだけインストールされていれば実行出来る、一番シンプルな形に今は落ち着いている。

例えばMySQLを使うRailsアプリの場合、プロジェクト内にdocker/test/Dockerfileを作っておいて、

FROM nkwhr/ruby-ubuntu

RUN apt-get -y install libmysqlclient-dev

ADD deploy.key /root/.ssh/id_rsa
RUN chmod 400 /root/.ssh/id_rsa

RUN echo "Host private.gitserver.com\n\tStrictHostKeyChecking no\n" > /root/.ssh/config

RUN git clone -b docker git@private.gitserver.com:your/project-a.git

RUN cd project-a && bundle install --without development:production -j $(getconf _NPROCESSORS_ONLN)

WORKDIR /project-a

ENV RAILS_ENV test

CMD git pull && \
    bundle install -j $(getconf _NPROCESSORS_ONLN) && \
    bin/rake db:create db:migrate && \
    bin/rspec

以下の様なシェルスクリプトをJenkinsなどで実行すればテストを行える。(MySQLサーバはmysqlというホスト名でアプリケーションコンテナにlinkしているので、Railsの場合は予めconfig/database.ymlでhostをmysqlに変更しておく必要がある。)

#!/bin/sh

PROJECT="project-a"
APP="$PROJECT/test"
MYSQL="dockerfile/mysql"

echo "o Preparing docker images..."

docker history $MYSQL >/dev/null 2>&1

if [ $? -ne 0 ] ; then
  echo "--> Downloading image $MYSQL"
  docker pull $MYSQL
fi

docker history $APP >/dev/null 2>&1

if [ $? -ne 0 ] ; then
  echo "--> Building image $APP"
  docker build -t $APP docker/test/.
fi

echo "  Done"

echo -n "o Starting [mysql] --> "
docker run -d --name mysql $MYSQL
echo "  Done"

echo -n "o Running [app] --> "
docker run --rm --name app --link mysql:mysql $APP
result=$?
echo "  Done"

echo -n "o Stopping [mysql] --> "
docker stop mysql >/dev/null
docker rm mysql
echo "  Done"

exit $result

初回実行時はアプリケーションコンテナのイメージを生成するので時間がかかってしまうが、二度目からは差分のみの更新となるので通常の実行方法と変わらないくらいの時間で実行出来る。

  • pros

    • dockerさえあればどこでも動く
    • プロジェクト毎に言語やミドルウェアのバージョンが違っても気にしなくていい
    • 関連しているアプリケーションが必要な場合も、追加でそのコンテナイメージをrunするだけ
    • テスト後のゴミデータを気にしなくていい
    • 少し書き換えればtestだけじゃなくてdev/prod環境としてもデプロイできる
  • cons

    • 複雑なアプリケーションだと、ちゃんと動くイメージと実行スクリプトを作るが結構大変かもしれない
    • アプリケーション側の設定が複雑になりがち
      • /etc/hostsで解決出来るものならいいが、固定値でないものは何かしら受け渡しする仕組みを作る必要がある
        • Consulやetcdを使えば良さそうだけど、今はそういうケースではconf生成スクリプトをCMDで実行している…
    • 変更点が増えてくると、テストの実行までに時間がかかる可能性がある
      • 定期的にイメージを作り直すことで回避出来る

まだ々試行錯誤している段階ではあるけど、常にクリーンな環境でテストを実行出来るのは精神衛生的によい。