mike-neckのブログ

Java or Groovy or Swift or Golang

Gradleからdockerを起動して分散テストさせようとしたのだけれども

f:id:mike_neck:20150818084108g:plain

標記の件について、ここ2日間取り組んでいます。もちろん、ボランティアです。

元ネタはじょーたにさんに教えてもらったこのツイート。

これ、最初は簡単だろうと思ってたけど、とてつもなく難しいテーマです(未解決)。あまりに思い通りに行かないので、自殺したいレベル。


実装方針

  1. 分散してテストが実行できるように適度に振り分けたテストタスクを準備します。
  2. テストクラスまでのコンパイルを行います。
  3. project-root/build.gradleCOPYで、project-root/build/VOLUMEで載せられるようにdockerイメージを作成します。
  4. project-root/buildなどをボリューム指定して起動するdocker-compose.ymlファイルをテストタスクの数だけ作ります(註1)。
  5. ExecutorServiceでテストタスクの数+1でdocker-composeでコンテナを起動、それぞれのテストタスクを実行させます。
  6. project-root/buildは共有されているので、それぞれのテストタスクの実行結果をTestReportタスクでまとめます。

(註1) … ひとつのdocker-compose.ymlだけで起動した場合、一つのコンテナでビルドが終わると、まだビルドの終わっていないコンテナを終了してしまうので、テストタスク個別にdockerを起動しないといけません。これの回避方法を探したのですけど、どうやらない模様。

ドハマリその1

project-root/.gradle/ディレクトリーも共有してしまった結果、ここにあるロックファイルで競合が発生して、ロック取得を待ってテストを行うか、ロック取得できなくてビルドが落ちます。その結果、ビルドが不安定かつロックファイルの待ちが発生してビルドが並列で実行できないという本来の目的が達成できなくなる。

したがって、project-root/.gradle/ディレクトリーは共有の対象外にしないといけないようです。

ドハマリその2

project-root/.gradle/ディレクトリーを共有対象外にしてしまった結果、project-root/buildディレクトリーを共有していても、タスクのUP-TO-DATEの管理ができない(project-root/.gradle/ディレクトリーにクラスファイルのハッシュなどの情報が格納されており、そのハッシュと実際のクラスのハッシュを比較しているが、その比較ができない)ために、再度Javaファイルのコンパイルを実行しようとする。

その結果、各コンテナでproject-root/build/dependency-cache/ディレクトリーを作成しようとして、競合が発生して、ディレクトリーの作成に失敗、その結果ビルドが失敗する

したがって、project-root/.gradle/ディレクトリーはやはり共有する必要があるという結論になり、ドハマリその1に戻る


結論

Gradle万能派な僕ですけど、本件に関しては、おとなしく、JenkinsなどのCIでmaster/slave構成でテストした方がいいでしょう。

ちなみに、見るも無残な結果だけギッハブにあげてあります。

ここのコードでも見て、笑うなり何なりしてください(自暴自棄)

github.com