Help us understand the problem. What is going on with this article?

Jibを使ってDockerとMaven/NetBeansを良い感じにインテグレートしてみた

ちょっと仕事でJava環境をDockerで作ってたのですが、そういえばDockerfileを書かなくてもMavenで良い感じにDockerイメージを作ってくれるJibがあったな、と思いそれを試してみました。
mojikyo45_640-2.gif

正直、「普通に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です。この辺を指定しないと実行結果が標準出力に出ません。
showLogsimage/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:dockerBuilddocker: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 packagejib: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!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away