Scala の Dockar Image を作る方法を考える。例として CircleCI 2.0 で Playframework 2.6 のアプリケーションのビルドを行う。
実験コード nokamoto/proposal-circileci2.0-sbt-docker
Dockerfile
Build
sbt universal:packageZipTarball
VERSION=$(cat VERSION | tr -d '\n')
docker build -t nokamoto/proposal-circileci2.0-sbt-docker:${VERSION} --build-arg VERSION=${VERSION} .
初めはコンテナ内での sbt ビルドを考えたが .ivy2 キャッシュの再利用など実行時間が問題となったので、外で作った tar をコピーする方法とした。
version := IO.readLines(file("VERSION")).head
docker build ... --build-arg VERSION=${VERSION} ...
sbt と docker でバージョン方法を共有するため VERSION ファイルを作成した。
Docker へのビルド時の変数共有は ARG によって可能となる。
COPY target/universal/${APP}-${VERSION}.tgz .
無駄なデータが入るとキャッシュが効かなくなる可能性が高まるので、sbt が作成した tar のみコピーする。
*
!./target/universal/*.tgz
上記のような .dockerignore により tgz ファイルのみをビルド時のコンテキストに取り込ませることができる。
今回のビルドでは tgz ファイルのみ利用するため単純な記述となるが、 git repo の構成次第では管理が煩雑となるかもしれない。少なくとも Scala での開発では target ディレクトリなど巨大なファイルが出来るので .dockerignore はあったほうが良さそうに思う。
実行結果
target/universal
に変更がなければ Using cache となる。
$ VERSION=$(cat VERSION | tr -d '\n')
$ docker build -t nokamoto/proposal-circileci2.0-sbt-docker:${VERSION} --build-arg VERSION=${VERSION} .
Sending build context to Docker daemon 32.66MB
Step 1/8 : FROM openjdk:8u181-jre-slim
---> c32c7f04676c
Step 2/8 : ARG VERSION
---> Using cache
---> e4376ecb0a5c
Step 3/8 : ARG APP=proposal-circileci2-0-sbt-docker
---> Using cache
---> b909a0260c5d
Step 4/8 : COPY target/universal/${APP}-${VERSION}.tgz .
---> Using cache
---> 3cd6d6285625
Step 5/8 : RUN tar -zxvf ${APP}-${VERSION}.tgz && mv ${APP}-${VERSION} ${APP}
---> Using cache
---> ed10a36c35e6
Step 6/8 : RUN rm ${APP}-${VERSION}.tgz
---> Using cache
---> d203430f0b0b
Step 7/8 : ENTRYPOINT [ "proposal-circileci2-0-sbt-docker/bin/proposal-circileci2-0-sbt-docker" ]
---> Using cache
---> dca9f0c6f3cc
Step 8/8 : CMD [ "-h" ]
---> Using cache
---> fd1139d10b8b
Successfully built fd1139d10b8b
Successfully tagged nokamoto/proposal-circileci2.0-sbt-docker:0.0.0
CircleCI 2.0
Workflow
workflows:
version: 2
ci:
jobs:
- test
- build:
requires:
- test
test
を実行して tar を作成してから build
で docker build & push を行う。
今回はデプロイまでは考えていないので上記の wrorkflow で完了する。
test
基本は Language Guide: Scala の流れと変わらない。
- run:
name: Install sbt-extras
command: |
curl -Ls https://git.io/sbt > /usr/local/bin/sbt
chmod u+x /usr/local/bin/sbt
sbt のインストールを sbt-extras に変更した。
- run:
name: Sbt test & package
command: sbt test universal:packageZipTarball
- save_cache:
key: sbt-universal-{{ .Revision }}
paths:
- "target/universal"
test
ジョブで sbt の実行環境を整備しているのでここで tar を作成し Caching で sbt-universal-{{ .Revision }}
としてキャッシュする。
build
- restore_cache:
key: sbt-universal-{{ .Revision }}
- run:
name: Docker build & push
command: |
VERSION=$(cat VERSION | tr -d '\n')
TAG=nokamoto13/proposal-circileci2.0-sbt-docker:${VERSION}
docker login -u $DOCKER_USER -p $DOCKER_PASS
docker build -t ${TAG} --build-arg VERSION=${VERSION} .
docker push ${TAG}
sbt-universal-{{ .Revision }}
をリストアしてから docker build & push を実行する。