CLOVER🍀

That was when it all began.

Jib Coreで、Dockerコンテナイメージを作ってみる

これは、なにをしたくて書いたもの?

そういえば、Jibというものがあったけど、全然使ったことがないなと思い、1度試しておこうかなと。

Javaアプリケーションを自動的にコンテナイメージにビルドするツール「Jib」がバージョン1.0に到達 - Publickey

Jib、Google提供のJavaコンテナイメージビルダ

名前や情報自体は、時々見ていたんですけどね。

Jib?

Jibとは、Googleの開発したコンテナイメージをビルドするツールです。Javaアプリケーション向けに、最適化されているようです。

Build container images for your Java applications.

GitHub - GoogleContainerTools/jib: 🏗 Build container images for your Java applications.

Jibとはなにか、その説明を見てみます。

What is Jib?

以下を特徴にしています。

  • Dockerデーモンなしでコンテナイメージを作成できる
  • Dockerイメージを作成するための、ベストプラクティスを熟知する必要がない
  • Javaアプリケーション向けに最適化されたコンテナイメージを作成できる
  • コンテナイメージのフォーマットは、DockerまたはOCIが選択可能
  • Maven、Gradleのプラグイン、CLI、Javaライブラリの4形態で利用可能

Google Cloud Platform Blog: Introducing Jib — build Java Docker images better

Build containers faster with Jib, a Google image build tool for Java applications - Speaker Deck

また、高速であること、再現性、デーモンレスを目標として掲げています。

Goals

Dockerレジストリへのpushなどもできるようです。

FAQは、こちら。

jib/faq.md at master · GoogleContainerTools/jib · GitHub

Jib Core

今回は、Javaライブラリとして提供される、Jib Coreを使用してみます。MavenプラグインやGradleプラグインを使うと便利だとは
思うのですが、まずはこちらを1度使ってみた方が理解が進むかな、と。

https://github.com/GoogleContainerTools/jib/tree/v0.13.1-core/jib-core

利用するバージョンは、0.13.1です。

APIリファレンスは、こちら。

com.google.cloud.tools.jib.api (jib-core 0.13.1 API)

Jib Core自体は、汎用的なコンテナイメージのビルドツールのようです。Javaアプリケーション向けのAPIも備えてはいますが、
README.mdに沿ってAPIを使っていくと、割と汎用的なイメージを作っているような感覚を覚えるでしょう。

実際、そうみたいですし。

ちなみに、APIはまだベータ段階なので、今後大幅に変更される可能性があることに注意してください。

The API is currently in beta and may change substantially.

もちろん、MavenプラグインやGradleプラグインのベースにもなっています。

まあ、説明はこのあたりにして使っていってみましょう。

環境

今回の環境は、こちらです。

$ java --version
openjdk 11.0.6 2020-01-14
OpenJDK Runtime Environment (build 11.0.6+10-post-Ubuntu-1ubuntu118.04.1)
OpenJDK 64-Bit Server VM (build 11.0.6+10-post-Ubuntu-1ubuntu118.04.1, mixed mode, sharing)


$ mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 11.0.6, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "4.18.0-25-generic", arch: "amd64", family: "unix"

サンプルアプリケーション

まずは、JibでDockerイメージを作るためのお題となるアプリケーションを作りましょう。

簡単なJAX-RSアプリケーションを作ります。

pom.xmlの抜粋。

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jdk-http</artifactId>
            <version>4.5.2.Final</version>
        </dependency>
    </dependencies>

ソースコード。
src/main/java/org/littlewings/jaxrs/Server.java

package org.littlewings.jaxrs;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

import com.sun.net.httpserver.HttpServer;
import org.jboss.logging.Logger;
import org.jboss.resteasy.plugins.server.sun.http.HttpContextBuilder;

public class Server {
    public static void main(String... args) throws IOException {
        Logger logger = Logger.getLogger(Server.class);

        HttpServer server = HttpServer.create(new InetSocketAddress(8080), 10);

        try {
            HttpContextBuilder builder = new HttpContextBuilder();
            builder.getDeployment().getActualResourceClasses().add(HelloResource.class);

            builder.bind(server);

            server.start();

            logger.info("server start.");

            while (true) {
                try {
                    TimeUnit.SECONDS.sleep(1L);
                } catch (InterruptedException e) {
                    // ignore
                }
            }
        } finally {
            server.stop(0);
            logger.info("server stop.");
        }
    }

    @Path("hello")
    public static class HelloResource {
        @GET
        @Produces(MediaType.TEXT_PLAIN)
        public String message(@QueryParam("value") String value) {
            return "Hello " + Optional.ofNullable(value).orElse("World") + "!!";
        }
    }
}

QueryStringで受け取ったパラメーターを使って、メッセージを返すだけのJAX-RSリソースクラスが動作します。

簡単に動作確認してみましょう。ビルドしてサーバーを起動。

$ mvn compile exec:java -Dexec.mainClass=org.littlewings.jaxrs.Server

アクセス。

$ curl localhost:8080/hello
Hello World!!


$ curl localhost:8080/hello?value=JAX-RS
Hello JAX-RS!!

OKですね。では、こちらをJibを使ったDockerイメージにすることを目標に、進めていきましょう。

Jib Coreを使う

それでは、Jib Coreを使ってDockerイメージを作成していきます。

先ほど作ったJAX-RSアプリケーションとは、別のMavenプロジェクトを用意します。依存関係は、こちら。

        <dependency>
            <groupId>com.google.cloud.tools</groupId>
            <artifactId>jib-core</artifactId>
            <version>0.13.1</version>
        </dependency>

まずは、雛形を用意しましょう。
src/main/java/org/littlewings/jib/Builder.java

package org.littlewings.jib;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

import com.google.cloud.tools.jib.api.AbsoluteUnixPath;
import com.google.cloud.tools.jib.api.CacheDirectoryCreationException;
import com.google.cloud.tools.jib.api.Containerizer;
import com.google.cloud.tools.jib.api.DockerDaemonImage;
import com.google.cloud.tools.jib.api.InvalidImageReferenceException;
import com.google.cloud.tools.jib.api.Jib;
import com.google.cloud.tools.jib.api.Port;
import com.google.cloud.tools.jib.api.RegistryException;

public class Builder {
    public static void main(String... args) throws InvalidImageReferenceException, InterruptedException, ExecutionException, RegistryException, CacheDirectoryCreationException, IOException {

        // ここに、Jibを使ったコードを書く

    }
}

こちらのmainメソッドの中身を、README.mdのExamplesを参考に埋めていきます。ちなみに、Dockerレジストリへのpushは行いません。

Examples

ベースイメージを決めて、Dockerイメージを作る

最初に行うのは、ベースイメージの決定と、作成するコンテナイメージの名前付けですね。Jibクラスをエントリポイントとして行います。

        Jib
                .from("adoptopenjdk:11-jre-hotspot")
                .containerize(
                        Containerizer
                                .to(
                                        DockerDaemonImage.named("kazuhira/simple-jaxrs-server")
                                )
                );

これで、AdoptOpenJDK 11 HotSpot(JREのみ)をベースイメージとして、Dockerイメージ名「kazuhira/simple-jaxrs-server」で
コンテナイメージが作成されます。なお、バージョンタグは「latest」となります。

実行してみましょう(

$ mvn compile exec:java -Dexec.mainClass=org.littlewings.jib.Builder

できました。作成時間は、50年前になっていますけど。

$ docker image ls | grep simple-jaxrs-server
kazuhira/simple-jaxrs-server       latest              f50fab05cd8b        50 years ago        225MB

ベースがAdoptOpenJDKのコンテナイメージなので、実行するとシェルが起動します。

$ docker container run -it --rm kazuhira/simple-jaxrs-server:latest 
root@1439d07d732b:/# 

中身はさておき、Jib Coreを使ってDockerコンテナイメージが作れたことになります。

ここから、ちょっとずつ中身を変えていってみましょう。

今回作成したDockerイメージは、削除しておきます。

$ docker image rm kazuhira/simple-jaxrs-server:latest 
タグを指定する

タグが「latest」になるのはイマイチですね。「0.0.1」にしてみます。

        Jib
                .from("adoptopenjdk:11-jre-hotspot")
                .containerize(
                        Containerizer
                                .to(
                                        DockerDaemonImage.named("kazuhira/simple-jaxrs-server:0.0.1")
                                )
                );

コンテナを再作成すると、タグが変わったことが確認できますね。

$ docker image ls | grep simple-jaxrs-server
kazuhira/simple-jaxrs-server       0.0.1               f50fab05cd8b        50 years ago        225MB

ここで指定する文字列のフォーマットは、内部的に呼び出されるImageReference#parseの説明を見ることで確認できます。

https://www.javadoc.io/static/com.google.cloud.tools/jib-core/0.13.1/com/google/cloud/tools/jib/api/ImageReference.html#parse-java.lang.String-

ちなみに、DockerDaemonImage#namedなどに、直接ImageReference#parseの結果を渡すことも可能です。

作成したイメージは、どの環境向けか?

ところで、先ほどからDockerデーモンレスと言いながらdockerコマンドで作成したイメージの情報を見ていますが、
これはContainerizer#toで指定しているものがポイントになっています。

                        Containerizer
                                .to(
                                        DockerDaemonImage.named("kazuhira/simple-jaxrs-server:0.0.1")
                                )

Containerizer (jib-core 0.13.1 API)

今回はDockerDaemonImageを選択しましたが、他にRegistryImage、TarImageがあり、どのような環境向けにイメージを作成するかを
指定することができます。

DockerDaemonImage (jib-core 0.13.1 API)

RegistryImage (jib-core 0.13.1 API)

TarImage (jib-core 0.13.1 API)

たとえば、レジストリにpushしたかったら、RegistryImageを使います。Jib CoreのExamplesでは、RegistryImageを使うように
なっていますね。

ENTRYPOINTを指定する

次に、ENTRYPOINTを指定してみましょう。

たとえば、「java --version」を実行するようにEntrypointを設定してみましょう。

        Jib
                .from("adoptopenjdk:11-jre-hotspot")
                .setEntrypoint(Arrays.asList("java", "--version"))
                .containerize(
                        Containerizer
                                .to(
                                        DockerDaemonImage.named("kazuhira/simple-jaxrs-server:0.0.1")
                                )
                );

確認。

$ docker container run -it --rm kazuhira/simple-jaxrs-server:0.0.1
openjdk 11.0.6 2020-01-14
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.6+10)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.6+10, mixed mode)

反映されましたね。

ちなみに、ProgramArgumentsを使うことで、ENTRYPOINTに対する引数を設定することも可能です。

        Jib
                .from("adoptopenjdk:11-jre-hotspot")
                .setEntrypoint(Arrays.asList("java"))
                .setProgramArguments(Arrays.asList("--version"))
                .containerize(
                        Containerizer
                                .to(
                                        DockerDaemonImage.named("kazuhira/simple-jaxrs-server:0.0.1")
                                )
                );
イメージの作成時刻を変更する

最初にイメージを作った時に確認しましたが、作成時刻が50年前になっています。

これを変更する場合は、CreationTimeを指定します。実行時の時刻を使うようにしてみましょう。

        Jib
                .from("adoptopenjdk:11-jre-hotspot")
                .setCreationTime(Instant.now())
                .setEntrypoint(Arrays.asList("java", "--version"))
                .containerize(
                        Containerizer
                                .to(
                                        DockerDaemonImage.named("kazuhira/simple-jaxrs-server:0.0.1")
                                )
                );

ビルド後にイメージを確認すると、イメージの作成時刻が変更されました。

Jibで作成したイメージの時刻がやたら古い理由は、FAQに記載があります。

Why is my image created 48+ years ago?

同じ時刻で作成することで、再現性を担保することを目的にしています。作成時刻を変更するということは、再現性が犠牲になる
ということが注意事項になります、だそうで。

$ docker image ls | grep simple-jaxrs-server
kazuhira/simple-jaxrs-server       0.0.1               ff8b454d2541        12 seconds ago      225MB
レイヤーを追加(ADD)して、ポートをEXPOSEする

そろそろ、最初に作ったアプリケーションを動かしたいところですね。アプリケーションのJARや、依存するライブラリなどを
イメージに追加していってみましょう。

とりあえず、Jib Coreを動かしているプロジェクトに、JARファイルを置く用のディレクトリを作成します。

$ mkdir -p container-target/libs

一方でアプリケーション側のプロジェクトでJARファイルを作成、依存ライブラリを取得して、Jib Coreを先ほど作成したディレクトリに
コピーします。

$ mvn package && mvn dependency:copy-dependencies -DincludeScope=compile
$ cp target/simple-jaxrs-server-0.0.1-SNAPSHOT.jar ../jib-core-example/container-target
$ cp target/dependency/* ../jib-core-example/container-target/libs

アプリケーション本体は作成したディレクトリの直下に、ライブラリは「libs」ディレクトリに置きました。

これらのJARファイルをイメージに追加するために、addLayerでJARファイルを追加していきます。

        Jib
                .from("adoptopenjdk:11-jre-hotspot")
                .addLayer(Arrays.asList(Paths.get("container-target/simple-jaxrs-server-0.0.1-SNAPSHOT.jar")), AbsoluteUnixPath.fromPath(Paths.get("/")))
                .addLayer(
                        Files.find(Paths.get("container-target/libs"),
                                1,
                                (path, attr) -> Files.isRegularFile(path) && path.getFileName().toString().endsWith(".jar")).collect(Collectors.toList()),
                        "/libs"
                )
                .setCreationTime(Instant.now())
                .setEntrypoint(Arrays.asList("java", "--version"))
                .containerize(
                        Containerizer
                                .to(
                                        DockerDaemonImage.named("kazuhira/simple-jaxrs-server:0.0.1")
                                )
                );

ここですね。

                .addLayer(Arrays.asList(Paths.get("container-target/simple-jaxrs-server-0.0.1-SNAPSHOT.jar")), AbsoluteUnixPath.fromPath(Paths.get("/")))
                .addLayer(
                        Files.find(Paths.get("container-target/libs"),
                                1,
                                (path, attr) -> Files.isRegularFile(path) && path.getFileName().toString().endsWith(".jar")).collect(Collectors.toList()),
                        "/libs"
                )

addLayerの第2引数には、ファイルをADDする先のディレクトリを指定します。

ENTRYPOINTも変えてしまいましょう。

        Jib
                .from("adoptopenjdk:11-jre-hotspot")
                .addLayer(Arrays.asList(Paths.get("container-target/simple-jaxrs-server-0.0.1-SNAPSHOT.jar")), AbsoluteUnixPath.fromPath(Paths.get("/")))
                .addLayer(
                        Files.find(Paths.get("container-target/libs"),
                                1,
                                (path, attr) -> Files.isRegularFile(path) && path.getFileName().toString().endsWith(".jar")).collect(Collectors.toList()),
                        "/libs"
                )
                .setCreationTime(Instant.now())
                .setEntrypoint("java", "-cp", "/libs/*:/simple-jaxrs-server-0.0.1-SNAPSHOT.jar", "org.littlewings.jaxrs.Server")
                .containerize(
                        Containerizer
                                .to(
                                        DockerDaemonImage.named("kazuhira/simple-jaxrs-server:0.0.1")
                                )
                );

8080ポートでリッスンしますし、ポートもEXPOSEしておきましょう。

        Jib
                .from("adoptopenjdk:11-jre-hotspot")
                .addLayer(Arrays.asList(Paths.get("container-target/simple-jaxrs-server-0.0.1-SNAPSHOT.jar")), AbsoluteUnixPath.fromPath(Paths.get("/")))
                .addLayer(
                        Files.find(Paths.get("container-target/libs"),
                                1,
                                (path, attr) -> Files.isRegularFile(path) && path.getFileName().toString().endsWith(".jar")).collect(Collectors.toList()),
                        "/libs"
                )
                .setExposedPorts(Port.tcp(8080))
                .setCreationTime(Instant.now())
                .setEntrypoint("java", "-cp", "/libs/*:/simple-jaxrs-server-0.0.1-SNAPSHOT.jar", "org.littlewings.jaxrs.Server")
                .containerize(
                        Containerizer
                                .to(
                                        DockerDaemonImage.named("kazuhira/simple-jaxrs-server:0.0.1")
                                )
                );

ここですね。

                .setExposedPorts(Port.tcp(8080))

では、イメージを作成して、起動。

$ docker container run -it --rm -p 8080:8080 kazuhira/simple-jaxrs-server:0.0.1
Mar 21, 2020 5:44:09 PM org.littlewings.jaxrs.Server main
INFO: server start.

サーバーが起動しました。

確認。

$ curl localhost:8080/hello
Hello World!!


$ curl localhost:8080/hello?value=JAX-RS
Hello JAX-RS!!

OKですね!

JibContainerBuilder

ここまで説明しませんでしたが、Jib#fromの戻り値は、JibContainerBuilderのインスタンスです。

JibContainerBuilder (jib-core 0.13.1 API)

JibContainerBuilderを使って、作成するコンテナの設定を行っていきます。JibContainerBuilderに定義されているメソッドを見ると、
どのような設定が可能なのかを確認することができます。

たとえば、USERやWORKDIRなどの指定はできそうですが、DockerfileにおけるRUNに相当することはできなさそうですね、など。

JibContainerBuilder#containerizeだけは戻り値がJibContainerBuilderにはならず、JibContainerとなりコンテナイメージの作成が
実行されます。

https://www.javadoc.io/static/com.google.cloud.tools/jib-core/0.13.1/com/google/cloud/tools/jib/api/JibContainerBuilder.html#containerize-com.google.cloud.tools.jib.api.Containerizer-

実際に利用する時は、JibContainerBuilderのAPIを眺めることになるでしょう。

ベースイメージのダウンロード先は?

ところで、今回はAdoptOpenJDKをベースイメージに選びましたが、「docker image ls」コマンドにAdoptOpenJDKは現れません。

$ docker image ls | grep adopt

どこにあるかというと、「$HOME/.cache/google-cloud-tools-java/jib」配下にあります。

$ find ~/.cache/google-cloud-tools-java/jib -type f
$HOME/.cache/google-cloud-tools-java/jib/images/adoptopenjdk!11-jre-hotspot/config.json
$HOME/.cache/google-cloud-tools-java/jib/images/adoptopenjdk!11-jre-hotspot/lock
$HOME/.cache/google-cloud-tools-java/jib/images/adoptopenjdk!11-jre-hotspot/manifest.json
$HOME/.cache/google-cloud-tools-java/jib/layers/f11b29a9c7306674a9479158c1b4259938af11b97359d9ac02030cc1095e9ed1/977183d4e9995d9cd5ffdfc0f29e911ec9de777bcb0f507895daa1068477f76f
$HOME/.cache/google-cloud-tools-java/jib/layers/bf7abfeb06800729d4f24d971e8017c537c4fa86368ae0bca66a7b36ca2b4189/85fa6e80789c791dff92d9427ce7cc6ffdb5164aa7296c986cb0c1adcd6711f7
$HOME/.cache/google-cloud-tools-java/jib/layers/78bf9a5ad49e4ae42a83f4995ade4efc096f78fd38299cf05bc041e8cdda2a36/16542a8fc3be1bfaff6ed1daa7922e7c3b47b6c3a8d98b7fca58b9517bb99b75
$HOME/.cache/google-cloud-tools-java/jib/layers/930bda195c84cf132344bf38edcad255317382f910503fef234a9ce3bff0f4dd/6597da2e2e52f4d438ad49a14ca79324f130a9ea08745505aa174a8db51cb79d
$HOME/.cache/google-cloud-tools-java/jib/layers/8a1e5c340bc4a84bc37bcb75bbaa4b90b9255c3cf2dfbf2891b350581bc687bd/3cd87aa74fc9d063ff863f789614cdfef8fbbf9b2908c1dc5f2e868b087f0a59
$HOME/.cache/google-cloud-tools-java/jib/layers/5bed26d33875e6da1d9ff9a1054c5fef3bbeb22ee979e14b72acf72528de007b/c8be1b8f4d60d99c281fc2db75e0f56df42a83ad2f0b091621ce19357e19d853

このあたりの情報が決められているのは、こちら。

https://github.com/GoogleContainerTools/jib/blob/v0.13.1-core/jib-core/src/main/java/com/google/cloud/tools/jib/filesystem/XdgDirectories.java

https://github.com/GoogleContainerTools/jib/blob/v0.13.1-core/jib-core/src/main/java/com/google/cloud/tools/jib/cache/CacheStorageFiles.java

もっとJava向けなAPIを使う

ここまで、JibContainerBuilderを使った汎用的なコンテナイメージの作成を行ってきました。

最後に、もう少しJavaアプリケーション向けのコンテナを作るためのAPI、JavaContainerBuilderを使ってみたいと思います。

JavaContainerBuilder (jib-core 0.13.1 API)

JavaContainerBuilderを使うと「/app/libs」に依存ライブラリが、「/app/classes」にクラスファイルが、
「/app/resources」にリソースファイルが配置されるように構成され、

Where is the application in the container filesystem?

https://github.com/GoogleContainerTools/jib/blob/v0.13.1-core/jib-core/src/main/java/com/google/cloud/tools/jib/api/JavaContainerBuilder.java#L198-L202

ENTRYPOINTが「java -cp [クラスパス]」となります。

jib/JavaContainerBuilder.java at v0.13.1-core · GoogleContainerTools/jib · GitHub

この時、libsなどにはクラスパスを通した状態に設定してくれます。

サンプルコードの雛形は、こちら。先ほど作成したコンテナと、近い状態のものを実現していくことを目標にします。
src/main/java/org/littlewings/jib/JavaBuilder.java

package org.littlewings.jib;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

import com.google.cloud.tools.jib.api.CacheDirectoryCreationException;
import com.google.cloud.tools.jib.api.Containerizer;
import com.google.cloud.tools.jib.api.DockerDaemonImage;
import com.google.cloud.tools.jib.api.InvalidImageReferenceException;
import com.google.cloud.tools.jib.api.JavaContainerBuilder;
import com.google.cloud.tools.jib.api.Port;
import com.google.cloud.tools.jib.api.RegistryException;

public class JavaBuilder {
    public static void main(String... args) throws IOException, InvalidImageReferenceException, InterruptedException, ExecutionException, RegistryException, CacheDirectoryCreationException {

        // ここに、JavaContainerBuilderを使ったコードを書く

    }
}

JavaContainerBuilderも、Jibと同じようにJavaContainerBuilder#fromから始めるのですが、オーソドックスには
JavaContainerBuilder#fromDistrolessを選ぶのだと思います。

        JavaContainerBuilder
                .fromDistroless()

これが指すベースイメージは、「gcr.io/distroless/java」なのでJava 8となります。

https://github.com/GoogleContainerTools/jib/blob/v0.13.1-core/jib-core/src/main/java/com/google/cloud/tools/jib/api/JavaContainerBuilder.java#L94

今回はJava 11で作っているので、先ほどと同じくAdoptOpenJDK 11 HotSpotをベースイメージにします。

で、できたのがこんな感じ。

        JavaContainerBuilder
                .from("adoptopenjdk:11-jre-hotspot")
                .addProjectDependencies(Paths.get("container-target/simple-jaxrs-server-0.0.1-SNAPSHOT.jar"))
                .addDependencies(Files.find(Paths.get("container-target/libs"),
                        1,
                        (path, attr) -> Files.isRegularFile(path) && path.getFileName().toString().endsWith(".jar")).collect(Collectors.toList()))
                .setMainClass("org.littlewings.jaxrs.Server")
                .toContainerBuilder()  // to JibContainerBuilder
                .setExposedPorts(Port.tcp(8080))
                .setCreationTime(Instant.now())
                .containerize(
                        Containerizer
                                .to(
                                        DockerDaemonImage.named("kazuhira/simple-jaxrs-server:0.0.1")
                                )
                );

ある程度Java向けの設定を行ったら、JavaContainerBuilder#toContainerBuilderでJibContainerBuilderに切り替えます。

これはJavaContainerBuilderが内部的に持っているJibContainerBuilderで

https://github.com/GoogleContainerTools/jib/blob/v0.13.1-core/jib-core/src/main/java/com/google/cloud/tools/jib/api/JavaContainerBuilder.java#L186

JavaContainerBuilder#toContainerBuilderを呼び出した時に、ここまでに設定したJavaContainerBuilderの内容を元にJibContainerBuilderに
イメージの定義を反映したものを返してくれます。

https://github.com/GoogleContainerTools/jib/blob/v0.13.1-core/jib-core/src/main/java/com/google/cloud/tools/jib/api/JavaContainerBuilder.java#L536-L664

今回は、プロジェクトの成果物(JARファイル)と依存ライブラリ、そしてmainクラスを指定しています。

                .addProjectDependencies(Paths.get("container-target/simple-jaxrs-server-0.0.1-SNAPSHOT.jar"))
                .addDependencies(Files.find(Paths.get("container-target/libs"),
                        1,
                        (path, attr) -> Files.isRegularFile(path) && path.getFileName().toString().endsWith(".jar")).collect(Collectors.toList()))
                .setMainClass("org.littlewings.jaxrs.Server")

その他、「/app/classes」や「/app/resources」にもファイルを追加できますし、これ以外の場所にもファイルを追加してクラスパスを
通すことができます。また、JavaVMへ渡すオプションも設定可能です。

では、コンテナイメージをビルドしてみます。

$ mvn compile exec:java -Dexec.mainClass=org.littlewings.jib.JavaBuilder

コンテナを起動。

$ docker container run -it --rm -p 8080:8080 kazuhira/simple-jaxrs-server:0.0.1
Mar 22, 2020 5:43:38 AM org.littlewings.jaxrs.Server main
INFO: server start.

確認。

$ curl localhost:8080/hello
Hello World!!


$ curl localhost:8080/hello?value=JAX-RS
Hello JAX-RS!!

OKですね。

まとめ

今回は、JibのMavenプラグイン、Gradleプラグインのベースとなっている、Jib Coreを使ってコンテナイメージを作成してみました。

最初からプラグインを使ってもいいのですが、なんとなくもうちょっと基本的なAPIとかを把握しておきたいなと思いまして。

だいたい、雰囲気はわかったので良しとしましょう。今度は、Mavenプラグインを使ってみようと思います。