ちょっと仕事でJava環境をDockerで作ってたのですが、そういえばDockerfileを書かなくてもMavenで良い感じにDockerイメージを作ってくれるJibがあったな、と思いそれを試してみました。
正直、「普通にDockerfile書けば良くない?」と思ってたのでそこまで注目して無かったのですが、使ってみると意外に便利。
今回は私の主要開発環境であるMac + NetBeansでJava11な環境を作ったのでそちらのやり方を記載します。
ちょこちょこ罠もあったのでそこも記載。
出来たものは下記にあります。
https://github.com/koduki/example-jib
実行用のプログラムの作成
当たり前ですが、まずは実行対象のコードを書きます。
何でも良いのですが一旦下記の感じで。エンドポイントとしてpublic static void main
は必要っぽいです。
public class Main {
public static void main(String[] args) {
System.err.printf("%s %s, %s %s" + System.lineSeparator(),
System.getProperty("os.name"),
System.getProperty("os.version"),
System.getProperty("java.vm.name"),
System.getProperty("java.vm.version")
);
System.out.println("Hello World.");
}
}
Jibの追加
続いて何はともあれJibを追加しましょう。pom.xmlにプラグインを追加するだけの簡単なお仕事です。
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>1.0.0-rc2</version>
<configuration>
<from>
<image>openjdk:11-jdk-slim</image>
</from>
<to>
<image>example-jib</image>
</to>
<container>
<jvmFlags>
<jvmFlag>-Xmx512m</jvmFlag>
<jvmFlag>-Xdebug</jvmFlag>
</jvmFlags>
</container>
</configuration>
</plugin>
詳しくは公式ドキュメントを参照ですがいくつかポイントがあります。
まずto/image
タグですが、こちらはビルド後のイメージ名を指定します。そしてfrom/image
タグですが、こちらは元になるイメージを指定します。
from/image
は省略可能で、その場合はgcr.io/distroless/java
がデフォルトとして利用されます。
ただし、この環境はJava8なのでJava11など任意のJDKを使いたい場合は明示的に指定してやる必要があるので要注意です。
container
ではJVMオプションとか色々指定できます。今回は特に指定してませんがextraDirectoryを使うことでDockerfileのADD
相当の事もできます。
ただ、src/resoucesにあるファイルは自動でコンテナに追加されるのでそれほど出番はないかも知れないです。
何となくコンテナを複雑に弄る場合はJibでゴリゴリやらずベースのイメージを作ってそれをFROMにするのが楽そうな気もします。
Jibによるイメージのビルド
続いてイメージのビルドです。
$ mvn compile jib:dockerBuild
これだけでDockerfileの作成をすっ飛ばしてイメージを作ってくれます。以下のようにイメージがちゃんと作られてるのが確認できるはずです。
% docker images|grep example-jib
example-jib latest 8ec59091042c 49 years ago 491MB
dockerBuild
の場合はローカルのDockerデーモンにイメージを登録しますがDockerHubなどリモートに直接Pushしたい場合はjib:build
を利用します。
また、このタイミングでDocker for Macを利用していると下記のようなエラーが出るかも知れません。
Failed to execute goal com.google.cloud.tools:jib-maven-plugin:1.0.0-rc2:dockerBuild (default-cli) on project example-jib: Build to Docker daemon failed, perhaps you should make sure Docker is installed and you have correct privileges to run it -> [Help 1]
これは単純にJibがdockerデーモンを見つけれてないだけなのですがdockerコマンドを直接叩いた場合は普通に動くので???って感じでした。
ただ種明かしは簡単でdocker for Macをコマンドラインから直接呼ぶ場合はDOCKER_HOST
の指定が不要なので環境変数に入ってなかったためです。
何で、以下のコマンド等で環境変数にDOCKER_HOST
の追加が必要です。
export DOCKER_HOST=unix:///var/run/docker.sock
この辺はREADMEとかでも触れておいて欲しいのだけど、詳しい人には自明過ぎて誰も気にして無かったという事だろうか。。。
Mavenからコンテナの実行
続いてMavenからのコンテナの実行です。普通にdocker run
をしても良いのですが、それだと別にターミナル立ち上げないといけなくて不便。。。 という事でこれもMavenから実行できるようにしてみました。
Jib自体はビルドに特化しているようで実行するための仕組みは無さそうだったのでfabric8io/docker-maven-pluginという別のプラグインを入れました。
と言うわけでpom.xmlに以下を追加。
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.28.0</version>
<configuration>
<showLogs>true</showLogs>
<images>
<image>
<name>example-jib</name>
<run>
<log>
<enabled>true</enabled>
</log>
<wait>
<exit>0</exit>
</wait>
</run>
</image>
</images>
<keepContainer>false</keepContainer>
</configuration>
</plugin>
こちらも詳しくは公式ドキュメントを参照ですがimage/name
で実行するイメージを指定します。
ボリュームとかポートとかが必要な場合はrunに追記していく感じです。
ポイントはshowLogs
, image/run/log/enabled
, image/run/wait/exit
です。この辺を指定しないと実行結果が標準出力に出ません。
showLogs
とimage/run/log/enabled
は実行結果をログとして出力するためのオプションです。
ただ、これだけでは不十分でwait
タグに何かしら登録しないと実行が瞬時に終わってしまって画面に何も出ません。そのため正常完了を確認するexit 0
をチェックに入れています。
これを実行すると以下のようになります。
% mvn docker:start
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------------< cn.orz.pascal:example-jib >----------------------
[INFO] Building example-jib 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- docker-maven-plugin:0.28.0:start (default-cli) @ example-jib ---
[INFO] DOCKER> [example-jib:latest]: Start container ec75063ae514
ec7506> Linux 4.9.125-linuxkit, OpenJDK 64-Bit Server VM 11.0.1+13-Debian-2bpo91
ec7506> Hello World.
[INFO] DOCKER> [example-jib:latest]: Waited on exit code 0 964 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.142 s
[INFO] Finished at: 2019-01-13T20:09:12-08:00
[INFO] ------------------------------------------------------------------------
実行結果のOSの部分がMacでは無くLinuxなので、ちゃんとコンテナで起動している事がわかりますね。
Maven/NetBeansのビルドプロセスとの統合
全部コマンドラインから操作をするならjib:dockerBuildや
docker:start`を毎度叩けば良いですが普通はIDE使って開発しますよね? Javaだし。
NetBeansだとmavenのカスタムゴールを登録することも出来ますがIDE使ってる感がイマイチなので、もうちょっとインテグレートされた感じの設定をしていきます。
NetBeansの「Build」でjib:dockerBuildを実行
NetBeansの場合はメニューの「Build」でビルドイメージを作る事が一般的ですよね。JavaならjarがJavaEEならwarがと適切なものがこのメニューでビルドされます。
何で今回もDockerイメージが生成されうようにMavenのpackage
フェーズを上書きします。
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
...
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>dockerBuild</goal>
</goals>
</execution>
</executions>
</plugin>
先ほどのプラグイン設定の部分にexecutionタグを追加しました。これで以下のようにmvn package
でjib:dockerBuild
が走るようになります。
% mvn package
[INFO] Scanning for projects...
...
[INFO] --- jib-maven-plugin:1.0.0-rc2:dockerBuild (default) @ example-jib ---
[INFO]
[INFO] Containerizing application to Docker daemon as example-jib...
[INFO] The base image requires auth. Trying again for openjdk:11-jdk-slim...
[INFO]
[INFO] Container entrypoint set to [java, -Xmx512m, -Xdebug, -cp, /app/resources:/app/classes:/app/libs/*, cn.orz.pascal.example_jib.Main]
...
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 19.345 s
[INFO] Finished at: 2019-01-13T20:07:28-08:00
[INFO] ------------------------------------------------------------------------
NetBeansのBuild
はMavenのpackage
フェーズを呼んでいるので、NetBeans側も特にカスタムゴールの設定とかせずに普通にメニューからBuild
をするだけでOKです。
NetBeansの「Run」でdocker:startを実行
続いてコンテナの実行です。
実のところMavenの標準ライフサイクルには実行
に相当するフェーズはありません。org.codehaus.mojo:exec-maven-plugin:1.5.0:exec
を実態としては実行しているのですが、ゴールを直接指定して実行されてるだけなのでMaven側でこれをどうフックすれば良いかはわかりませんでした。
なので、Maven側はスパッと諦めてNetBeansの設定だけ変えることにしました。Run
などの標準のアクションをカスタマイズするにはプロジェクト直下のnbactions.xmlを編集します。
<?xml version="1.0" encoding="UTF-8"?>
<actions>
<action>
<actionName>run</actionName>
<goals>
<goal>docker:start</goal>
</goals>
</action>
</actions>
これでNetBeansからRunを実行すればexec:exec
の代わりにdocker:start
が実行されるようになります。
まとめ
Jibを使ってDockerとMaven及びNetBeansを良い感じにインテグレートする方法をまとめてみました。
Javaで開発してる場合はCLIよりもIDEに統合された形で色々出来た方がなんだかんだで便利です。
カスタムゴールだとイマイチ統合感が無いのでこの方法ができてよかったです。
今回はCLIベースのJavaを書いてましたがJavaEE系も問題なくJibでイメージ化できるようなので、今度はその辺を調査してみたいと思います。
これでDockerを仕事で使うための道が一歩進んだかな。
それではHappy Hacking!