sbt-native-packagerのDocker pluginでentrypoint.sh
を追加してイメージの実行を制御したかったがつまづいたので 解決方法をメモ。
sbt-native-packager
sbt-native-packagerとは、Scalaプロジェクトを様々なプラットフォームのネイティブな方式にパッケージしてくれるsbtプラグインで、例えばDockerやWindowsのMSI形式などにsbtアプリケーションをパッケージングしてくれる。
sbt-native-packager.readthedocs.io
自分が開発している解説動画生成ツールであるZMMでもこのsbt-native-packagerを使っていて、Docker pluginを使って色々な依存関係をまとめた形でDockerイメージとしてアプリケーションをリリースしている。
https://hub.docker.com/r/windymelt/zmmhub.docker.com
さて、今回はDockerでZMMを実行すると生成物のパーミッションがrootになってしまい困ってしまうというトラブルを解消するために、デフォルトのentrypoint.sh
ではなく独自のentrypoint.sh
を使うことにした。このため、標準でsbt-native-packagerが追加するファイルに加えてentrypoint.sh
を追加し、これをDockerfile
から呼び出す必要が出てきた。
雑に独自ファイルをイメージに取り込もうとしても失敗する
sbt-native-packagerではDockerイメージを構築する過程をbuild.sbtでカスタマイズすることが可能で、以下のようにDSLを記述することで独自のDockerfileを書き、独自のentrypoint.shを指定することができる:
lazy val root = (project in file(".")) .enablePlugins(JavaAppPackaging) // for DockerPlugin .enablePlugins(DockerPlugin) /* snip */ .settings( dockerBaseImage := "amazoncorretto:17", dockerCommands ++= Seq( // coretto image does not have useradd utils ExecCmd("RUN", "yum", "install", "-y", "shadow-utils") ), dockerEntrypoint := Seq("/foo/bar/entrypoint.sh") )
が、Docker pluginは自由にファイルを追加できない仕組みになっているようで、ExecCmd("COPY", "entrypoint.sh", "/entrypoint.sh")
などと書いてもうまく見付けてもらえない。独自のContextでビルドしているようで、基本的に直接ファイル追加に手を入れることは想定していないようだ。
mappings機能を使ってentrypoint.sh
を取り込む
もともとsbt-native-packagerはDocker専用のツールではなく、MSIやdeb形式などにアプリケーションをパッケージする汎用プラグインを指向している。そのため、Dockerイメージにファイルを追加したい場合はmappingsという機能を使う。
Universal / mappings += file("entrypoint.sh") -> "entrypoint.sh"
という記述をbuild.sbt
の設定に書き込むと、そのファイルはDockerfileに取り込まれるようになる(今回の例では、プロジェクトルートにあるentrypoint.sh
が/opt/docker/entrypoint.sh
として取り込まれる)。Dockerの場合、ファイルは/opt/docker/
に配置される(sbtのDocker/defaultLinuxInstallLocation
の設定でカスタマイズ可能)。
本当はDocker / mappings += ...
と書いても動くはずなのだが、うまく動作してくれなかったのでUniversal / mappings
と書いている。
entrypointを指定する
sbt-native-packagerのDocker pluginでは、エントリポイントはDockerfileではなくbuild.sbt上で設定する。
dockerEntrypoint := Seq("/opt/docker/entrypoint.sh")
前述した通り、mappingしたファイルはデフォルトで/opt/docker/
に配置されるので、そのパスを指定してやればよい。
まとめ
sbt-native-packagerのDocker pluginで、任意のファイルをDockerイメージに取り込む方法を紹介した。ドキュメントにもちょっと書かれているが、プラグイン自体が汎用的で大きなプラグインなのでなかなかやり方が分からなかった。