LoginSignup
2
6

More than 5 years have passed since last update.

Wercker CLI で Docker 上に Scala 開発環境を簡単構築

Last updated at Posted at 2017-06-12

Wercker はプライベートリポジトリが無料で使える CI サービスとして有名ですが、Wercker CLI を使ったローカル開発もかなり便利です。

今回は Scala 開発環境を Docker で構築するために Wercker CLI を使ってみます1

準備

Docker engine と Wercker CLI をインストールしてください。Wercker CLI は Github のダウンロードページ から適切なバイナリをダウンロードしてきて chmod +x して PATH に置けば OK です。

※Wercker CLI 最新版の 1.0.882 ではこの記事のサンプルは動作しません。Docker container exits (exit status 130) · Issue #312 · wercker/wercker と同様のエラーが発生してしまいます。1つ前のバージョン 1.0.643 での動作は問題ありません。

開発サンプル

今回は Akka HTTP による Web アプリケーションをサンプルとします。

Server.scala
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.ContentTypes
import akka.http.scaladsl.model.HttpEntity
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import akka.stream.ActorMaterializer

object Server {

  implicit val system = ActorSystem("simple-rest-system")
  implicit val materializer = ActorMaterializer()
  implicit val executionContext = system.dispatcher

  val route: Route = {
    path("") {
      get {
        complete(HttpEntity(ContentTypes.`text/plain(UTF-8)`, "Hello!"))
      }
    }
  }

  def main(args: Array[String]): Unit = {
    Http().bindAndHandle(route, "0.0.0.0", 9000)
  }

}
build.sbt
scalaVersion := "2.12.2"
libraryDependencies += "com.typesafe.akka" %% "akka-http" % "10.0.7"
project/build.properties
sbt.version=0.13.15

以下のように配置します :

$ tree
.
├── build.sbt
├── project/
│   └── build.properties
└── Server.scala

第 1 版

プロジェクトルートに wercker.yml を作って配置します :

wercker.yml
dev:
   box:
      id: java:8
      ports:
       - '9000'
   steps:
    - script:
         code: |
            echo 'deb http://dl.bintray.com/sbt/debian /' > /etc/apt/sources.list.d/sbt.list
            apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823
            apt-get update
            apt-get install -y sbt
    - script:
         code: |
            sbt run

実行します :

$ wercker dev --expose-ports
...
...
[info] Running Server

別ターミナルから試します :

$ curl http://localhost:9000/
Hello!

終了するには wercker dev を Ctrl-C で中止します。

一応実行できますが、いろいろ問題点があります :

  • ソースコード Server.scala を書き換えるたびにいちいち Ctrl-C で止めてから wercker dev して再起動する必要がある。
  • 再起動するたびに apt-get update して遅い。
  • 再起動するたびに sbt が依存ライブラリをダウンロードしてめちゃ遅い。

第 2 版

まず apt-get updatesbt の依存ライブラリのロードが遅い問題に対処しましょう。どちらも Wercker CLI がもつキャッシュ機能を利用します。

apt-get update の内容をキャッシュするためには、公式 step の install-packages を使います。今回のサンプルで注意する点としては、sbt をインストールするために install-packages の前に必要な設定を済ませておく必要がある点です。

sbt でダウンロードした内容をキャッシュするのは自前でやる必要があります。wercker が使うキャッシュ用のディレクトリが、コンテナー内から環境変数 $WERCKER_CACHE_DIR で参照できるのでそれを使います。キャッシュされた内容はホストのプロジェクトルート下の ./.wercker/cache ディレクトリに保存され明示的に消去されるまで無くなりません。

新しい wercker.yml は以下のようになります2 :

wercker.yml
dev:
   box:
      id: java:8
      ports:
       - '9000'
   steps:
    - script:
         code: |
            [[ ! -e "$WERCKER_CACHE_DIR/ivy2-cache" ]] && mkdir "$WERCKER_CACHE_DIR/ivy2-cache"
            mkdir ~/.ivy2
            ln -s "$WERCKER_CACHE_DIR/ivy2-cache" ~/.ivy2/cache
    - script:
         code: |
            echo 'deb http://dl.bintray.com/sbt/debian /' > /etc/apt/sources.list.d/sbt.list
            apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823
    - install-packages:
         packages: sbt
    - script:
         code: |
            sbt run

これで wercker dev を実行してから [info] Running Server と表示されるまでの時間はかなり短縮されました。とはいえ、ソースコード Server.scala を修正するたびに wercker dev を再起動しなくてはならないので、まだまだ快適とはいえません。次はこの点を改善してみます。

第 3 版

wercker dev はプロジェクトディレクトリのファイル変更を監視する機能を持っています。internal/watch がそれです。wercker.yml の最後の部分 :

wercker.yml
    - script:
         code: |
            sbt run

を以下のようにします :

wercker.yml
    - internal/watch:
         reload: true
         code: |
            sbt run

これでうまくいく... はずなのですが、sbt がプロジェクトルート内にビルド結果を書き込むせいなのか、2 回目以降の実行ではうまくいきません。そこで次のようなハックで逃げます。

wercker.yml
    - script:
         code: |
            rm -rf target project/target
    - internal/watch:
         reload: true
         code: |
            sbt run

この状態で wercer dev --expose-ports を実行し、Server.scala を編集して変更すると :

[info] Running Server
--> Reloading
[info] Loading project definition from /pipeline/source/project
[info] Set current project to source (in build file:/pipeline/source/)
[info] Compiling 1 Scala source to /pipeline/source/target/scala-2.12/classes...
[info] Running Server

のようになって、リロード(再コンパイル)されます。

前記のハックにより wercker を再起動した時に必ず全コンパイルになるのが悲しいですが、一旦起動した後のファイル変更に伴う再コンパイルでは必要ファイルのみが再コンパイルされるのでまあ良しとしましょう。

もう一つ気になるのは、コンテナー内では sbt が root 権限で動作するために、ビルドの成果物が root の所有のままホスト側のプロジェクトディレクトリに生成されてしまう点です。今回は、前記ハックのために起動時に毎回消去するので、終了時にも消去してしまいましょう。

最終的な wercker.yml は以下のようになります。

wercker.yml
dev:
   box:
      id: java:8
      ports:
       - '9000'
   steps:
    - script:
         code: |
            [[ ! -e "$WERCKER_CACHE_DIR/ivy2-cache" ]] && mkdir "$WERCKER_CACHE_DIR/ivy2-cache"
            mkdir ~/.ivy2
            ln -s "$WERCKER_CACHE_DIR/ivy2-cache" ~/.ivy2/cache
    - script:
         code: |
            echo 'deb http://dl.bintray.com/sbt/debian /' > /etc/apt/sources.list.d/sbt.list
            apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823
    - install-packages:
         packages: sbt
    - script:
         code: |
            rm -rf target project/target
    - internal/watch:
         reload: true
         code: |
            sbt run
    - script:
         code: |
            rm -rf target project/target

最終版

wercker.yml ファイル1個だけのシンプル版としてはこのままで十分ですが、実践的には初期化が終了したコンテナーを Docker イメージとして Docker Hub 等にアップロードしておいてそれを使ったほうが更に快適になります。

Docker イメージは通常 Dockerfile で作成しますが Wercker CLI で作るのも簡単です。以下では開発用の Docker イメージの作成をこれまでの wercker.yml に同居させたものです3 (以下のファイル中 yourname は各自の Docker Hub ID に変更してください) :

wercker.yml
dev:
   box:
      id: yourname/sbt
      ports:
       - '9000'
   steps:
    - script:
         code: |
            [[ ! -e "$WERCKER_CACHE_DIR/ivy2-cache" ]] && mkdir "$WERCKER_CACHE_DIR/ivy2-cache"
            mkdir ~/.ivy2
            ln -s "$WERCKER_CACHE_DIR/ivy2-cache" ~/.ivy2/cache
    - script:
         code: |
            rm -rf target project/target
    - internal/watch:
         reload: true
         code: |
            sbt run
    - script:
         code: |
            rm -rf target project/target
build:
   box: java:8
   steps:
    - script:
         code: touch "$WERCKER_OUTPUT_DIR/dummy"
deploy:
   box: java:8
   steps:
    - script:
         code: |
            echo 'deb http://dl.bintray.com/sbt/debian /' > /etc/apt/sources.list.d/sbt.list
            apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823
    - install-packages:
         packages: sbt
    - internal/docker-push:
         username: $USERNAME
         password: $PASSWORD
         repository: yourname/sbt

dev: セクションの内容も微妙に変わっているので注意してください。

イメージの Docker Hub へのアップロードは以下のようにします :

$ wercker build --artifacts
$ X_USERNAME='yourname' X_PASSWORD='your_long_password' wercker deploy

そして dev を実行します :

$ wercker dev --expose-ports

脚注


  1. 筆者の環境(ホスト)は Ubuntu 16.04, Docker 17.03.1-ce です。Mac では挙動が変わる可能性があります(ホスト側に root のファイルが生成されないなど)。 

  2. ここでは簡単のため $WERCKER_CACHE_DIR/ivy2-cache という名前のディレクトリを使っていますが、本来は衝突回避のため $WERCKER_CACHE_DIR/yourname/ivy2-cache (yourname は wercker CI サービスのユーザー名) などとすべきでしょう。 

  3. 実際にはイメージの生成は別プロジェクトにすべきでしょう。そしてせっかく Wercker を使っているのですから Wercker CI に生成させましょう。 

2
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
6