sbtが取得するjarをキャッシュさせる部分で頭を捻らないといけなくて、ベストプラクティスが無い状態だけど、やってみたら意外と簡単だった。
以下の内容はCIサーバーで動かす用途を想定していて、手元のマシンで動かすことは考えてない。
Dockerfileをプロジェクトのルートに置く。
FROM java:8-jdk
COPY . /myapp
WORKDIR /myapp
Dockerfileではファイルのコピーだけやって、ビルドスクリプトでビルドする。
ビルドスクリプトはこんな感じ。
#!/bin/bash -eux
CONTAINER=myapp-build
IMAGE=myapp-build
docker build -t $CONTAINER .
# キャッシュするディレクトリをあらかじめ作っておく
mkdir -p $PWD/.ivy2 $PWD/.sbt
# 既に同名のイメージがあるとエラーになるので消しておく
docker rm $IMAGE || true
# キャッシュを使いつつビルド
docker run --name=$IMAGE -v $PWD/.ivy2:/root/.ivy2 -v $PWD/.sbt:/root/.sbt $CONTAINER ./activator dist
# イメージからzipを抜き出す
docker cp $IMAGE:/myapp/target/universal/myapp-1.0-SNAPSHOT.zip ./
# イメージを削除
docker rm $IMAGE || true
これだけで myapp-1.0-SNAPSHOT.zip が手に入る。
$PWD/.ivy2
と $PWD/.sbt
はDockerfileで COPY . /myapp
したときにコピーされると困るので .dockerignore に追加すると良い。
ついでにテストも走らせる
テストにMySQLとmemcachedを使いたかったのでDockerfileでインストール。
FROM java:8-jdk
# MySQLのインストールにrootパスワード入力を求めない
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && apt-get install -y mysql-server memcached --no-install-recommends && rm -rf /var/lib/apt/lists/*
COPY . /myapp
WORKDIR /myapp
ビルドスクリプトの ./activator dist
のところを
bash -c 'service memcached start && service mysql start && mysql -uroot < ./init.sql && ./activator test dist'
に変更。init.sqlにはデータベース作成とかユーザー作成が書いてある。
ついでにデプロイ用Dockerコンテナも作る
ビルドにはJDKが必要だけどデプロイ用にはJREだけでいい。なので上のとは別にDockerfileを作った。
Dockerfile-release
FROM java:8-jre
EXPOSE 9000
# tgzをADDすると展開されてディレクトリになる
ADD ./myapp-1.0-SNAPSHOT.tgz /
WORKDIR /myapp-1.0-SNAPSHOT
RUN ["chown", "-R", "daemon", "."]
USER daemon
ENTRYPOINT ["bin/myapp"]
zipじゃなくてtgzで出力するために、ビルドスクリプトの activator dist
のところを activator universal:packageZipTarball
に変更しなければいけない。
1.0-SNAPSHOTって付くのがキモい場合はbuild.sbtに name in Universal := moduleName.value
と書けば myapp.tgz
という名前になってくれて嬉しい。
最終的なビルドスクリプト
上に書いた要素をすべて盛り込むとこういう感じになる。
#!/bin/bash -eux
CONTAINER=myapp-build
IMAGE=myapp-build
docker build -t $CONTAINER .
# キャッシュするディレクトリをあらかじめ作っておく
mkdir -p $PWD/.ivy2 $PWD/.sbt
# 既に同名のイメージがあるとエラーになるので消しておく
docker rm $IMAGE || true
# キャッシュを使いつつテストしてからビルド
docker run --name=$IMAGE -v $PWD/.ivy2:/root/.ivy2 -v $PWD/.sbt:/root/.sbt $CONTAINER \
bash -c 'service memcached start && service mysql start && mysql -uroot < ./init.sql && ./activator test universal:packageZipTarball'
# イメージからtgzを抜き出す
docker cp $IMAGE:/myapp/target/universal/myapp.tgz ./
# イメージを削除
docker rm $IMAGE || true
# デプロイ用コンテナを作成
docker build -t quay.io/example/myapp -f Dockerfile-release .
# quay.ioにpushする
docker push quay.io/example/myapp