CLOVER🍀

That was when it all began.

Jib Maven Pluginで、Dockerコンテナイメージを作る

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

以前、Jib Coreを使ってDockerイメージを作ってみました。

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

これでJibの基本的なところはわかった…ことにして、今度はJib Maven Pluginを使ってDockerイメージを作ってみたいと思います。

Jib Maven Plugin

文字通り、Jibの機能が使えるMaven Pluginで、DockerまたはOCIイメージを作ることができます。

https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin

作成したイメージは、コンテナレジストリにアップロードしたり、Dockerデーモンに送ったり、tarファイルにしたりできます。

それでは、使っていってみましょう。

環境

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

$ 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 Maven Pluginは2.1.0を使用します。

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

先に、お題となるサンプルアプリケーションが必要ですね。

こちらで作ったものを流用します。

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

pom。RESTEasyは、最新版にしておきました。
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.littlewings</groupId>
    <artifactId>jib-simple-jaxrs-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <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.3.Final</version>
        </dependency>
    </dependencies>
</project>

起動クラス+JAX-RSリソースクラス。
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") + "!!";
        }
    }
}

こちらを、Jibを使ってDockerイメージにしていきましょう。

Jib Maven Pluginを組み込む

まずは、なにも考えずにプラグインを組み込んでみます。

    <build>
        <plugins>
            <plugin>
                <groupId>com.google.cloud.tools</groupId>
                <artifactId>jib-maven-plugin</artifactId>
                <version>2.1.0</version>
                <configuration>
                    <to>
                        <image>kazuhira/jib-simple-jaxrs-server</image>
                    </to>
                </configuration>
            </plugin>
        </plugins>
    </build>

「jib:dockerBuild」でDockerイメージを作成しましょう。

$ mvn compile jib:dockerBuild

どうやら、「gcr.io/distroless/java:11」をベースにしてイメージを作ったみたいです。

[INFO] Containerizing application to Docker daemon as kazuhira/jib-simple-jaxrs-server...
[WARNING] Base image 'gcr.io/distroless/java:11' does not use a specific image digest - build may not be reproducible
[INFO] Using base image with digest: sha256:c94feda039172152495b5cd60a350a03162fce4f8986b560ea555de4d276ce19
[INFO] 
[INFO] Container entrypoint set to [java, -cp, /app/resources:/app/classes:/app/libs/*, org.littlewings.jaxrs.Server]
[INFO] 
[INFO] Built image to Docker daemon as kazuhira/jib-simple-jaxrs-server
[INFO] Executing tasks:
[INFO] [==============================] 100.0% complete
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  8.328 s
[INFO] Finished at: 2020-04-05T19:43:05+09:00
[INFO] ------------------------------------------------------------------------

確認。50年前のタイムスタンプのDockerイメージができました。

$ docker image ls | grep simple
kazuhira/jib-simple-jaxrs-server   latest              cf8664e3692c        50 years ago        199MB

Dockerイメージの方も確認してみましょう。

$ docker container run -it --rm -p 8080:8080 kazuhira/jib-simple-jaxrs-server:latest 
Apr 05, 2020 10:50:26 AM org.littlewings.jaxrs.Server main
INFO: server start.

OKですね。

$ curl localhost:8080/hello
Hello World!!

では、少しずつ設定を変えていってみましょう。Jib Coreの時に習う感じします。

あと、Exampleも参考にするとよいでしょう。

Example

タグを指定する

これは、単純にイメージ名の隣に「:」で区切ってタグを指定すればOKです。

            <plugin>
                <groupId>com.google.cloud.tools</groupId>
                <artifactId>jib-maven-plugin</artifactId>
                <version>2.1.0</version>
                <configuration>
                    <to>
                        <image>kazuhira/jib-simple-jaxrs-server:0.0.1</image>
                    </to>
                </configuration>
            </plugin>

ビルド後。

$ docker image ls | grep simple
kazuhira/jib-simple-jaxrs-server   0.0.1               cf8664e3692c        50 years ago        199MB
ベースイメージを変更する

最初はデフォルトで使われるベースイメージ(Distroless)を使いましたが、ベースイメージを変更してみましょう。

            <plugin>
                <groupId>com.google.cloud.tools</groupId>
                <artifactId>jib-maven-plugin</artifactId>
                <version>2.1.0</version>
                <configuration>
                    <from>
                        <image>adoptopenjdk:11-jre-hotspot</image>
                    </from>
                    <to>
                        <image>kazuhira/jib-simple-jaxrs-server:0.0.1</image>
                    </to>
                </configuration>
            </plugin>

from、imageで指定します。今回はAdoptOpenJDKにしました。

Extended Usage

ところで、README.mdを見ていると、なにも指定がない場合のベースイメージは「gcr.io/distroless/java」が使われてると書かれて
いた割には最初にダウンロードされたのは「gcr.io/distroless/java:11」でした。

Setting the Base Image

どうなっているんでしょう?

どうやら、Javaのバージョンを見てベースイメージを決めているようですね。

https://github.com/GoogleContainerTools/jib/blob/v2.1.0-maven/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessor.java#L577-L592

Java VMに渡すオプションを設定する

Jib Maven Pluginを使った場合は、ENTRYPOINTを直接指定することは少ないと思います。

代わりに、jvmFlagやmainClassなどを使用します。

entrypointという設定項目自体はあるのですが、jvmFlagsなどを設定しても無視されるそうな。

The command to start the container with (similar to Docker's ENTRYPOINT instruction). If set, then jvmFlags and mainClass are ignored. You may also set INHERIT to indicate that the entrypoint and args should be inherited from the base image.*

では、jvmFlagを使ってみましょう。「-Xmx」を指定してみます。

            <plugin>
                <groupId>com.google.cloud.tools</groupId>
                <artifactId>jib-maven-plugin</artifactId>
                <version>2.1.0</version>
                <configuration>
                    <from>
                        <image>adoptopenjdk:11-jre-hotspot</image>
                    </from>
                    <to>
                        <image>kazuhira/jib-simple-jaxrs-server:0.0.1</image>
                    </to>
                    <container>
                        <jvmFlags>
                          <jvmFlag>-Xmx256m</jvmFlag>
                        </jvmFlags>
                    </container>
                </configuration>
            </plugin>

指定する時は、container配下にするようですね。

起動後にコンテナ内に入って確認してみると、Java VMへの引数が追加されていることが確認できますね。

$ docker container exec -it [コンテナ名] bash
root@5de3f4e084f2:/# ps -ef | grep java
root         1     0 27 11:20 pts/0    00:00:01 java -Xmx256m -cp /app/resources:/app/classes:/app/libs/* org.littlewings.jaxrs.Server
root        36    26  0 11:20 pts/1    00:00:00 grep --color=auto java

https://github.com/GoogleContainerTools/jib/blob/v2.1.0-maven/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessor.java#L558-L564

なお、実行時にJava VMへの引数を指定したい場合は、JAVA_TOOL_OPTIONS環境変数を使うようです。
※ベースイメージがdistroless/javaの時に限るとは書いていますが…これはJavaのドキュメントにも記載のある環境変数ですが…

https://github.com/GoogleContainerTools/jib/blob/v2.1.0-maven/docs/faq.md#how-do-i-set-parameters-for-my-image-at-runtime

mainClassについては自動で検出するようですが、自分で指定することも可能です。

https://github.com/GoogleContainerTools/jib/blob/v2.1.0-maven/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessor.java#L554-L556

あと、プログラム自体の起動引数についてはargsで指定します。

ポートをEXPOSEする

ポートをEXPOSEするには、ports、portで指定します。

            <plugin>
                <groupId>com.google.cloud.tools</groupId>
                <artifactId>jib-maven-plugin</artifactId>
                <version>2.1.0</version>
                <configuration>
                    <from>
                        <image>adoptopenjdk:11-jre-hotspot</image>
                    </from>
                    <to>
                        <image>kazuhira/jib-simple-jaxrs-server:0.0.1</image>
                    </to>
                    <container>
                        <jvmFlags>
                          <jvmFlag>-Xmx256m</jvmFlag>
                        </jvmFlags>
                        <ports>
                            <port>8080</port>
                        </ports>
                    </container>
                </configuration>
            </plugin>

確認。

$ docker container ps
CONTAINER ID        IMAGE                                    COMMAND                  CREATED             STATUS              PORTS               NAMES
bd8aa0836e4d        kazuhira/jib-simple-jaxrs-server:0.0.1   "java -Xmx256m -cp /…"   5 seconds ago       Up 2 seconds        8080/tcp            busy_carson
イメージの作成時刻を変更する

作成時刻が50年前になっているのを変更するには、creationTimeを使用します。

            <plugin>
                <groupId>com.google.cloud.tools</groupId>
                <artifactId>jib-maven-plugin</artifactId>
                <version>2.1.0</version>
                <configuration>
                    <from>
                        <image>adoptopenjdk:11-jre-hotspot</image>
                    </from>
                    <to>
                        <image>kazuhira/jib-simple-jaxrs-server:0.0.1</image>
                    </to>
                    <container>
                        <jvmFlags>
                          <jvmFlag>-Xmx256m</jvmFlag>
                        </jvmFlags>
                        <creationTime>USE_CURRENT_TIMESTAMP</creationTime>
                        <ports>
                            <port>8080</port>
                        </ports>
                    </container>
                </configuration>
            </plugin>

「USE_CURRENT_TIMESTAMP」を使用すると、現在時刻でイメージが作成されます。

$ docker image ls | grep simple
kazuhira/jib-simple-jaxrs-server   0.0.1               36ac8b04db85        2 minutes ago       228MB

ただし、現在時刻を使ってしまうと再現性を失うことになる点に注意してください。

Why is my image created 48+ years ago?

また、ISO 8601フォーマットで指定することもできます。

            <plugin>
                <groupId>com.google.cloud.tools</groupId>
                <artifactId>jib-maven-plugin</artifactId>
                <version>2.1.0</version>
                <configuration>
                    <from>
                        <image>adoptopenjdk:11-jre-hotspot</image>
                    </from>
                    <to>
                        <image>kazuhira/jib-simple-jaxrs-server:0.0.1</image>
                    </to>
                    <container>
                        <jvmFlags>
                          <jvmFlag>-Xmx256m</jvmFlag>
                        </jvmFlags>
                        <creationTime>2020-01-15T10:30:45+09:00</creationTime>
                        <ports>
                            <port>8080</port>
                        </ports>
                    </container>
                </configuration>
            </plugin>

実体としては、DateTimeFormatter.ISO_DATE_TIMEでパースできる必要があります。

結果。

$ docker image ls | grep simple
kazuhira/jib-simple-jaxrs-server   0.0.1               bf6479b69280        2 months ago        228MB
ビルドプロセスに組み込む

Mavenプラグインとして使っているのですし、package時にDockerイメージを作って欲しいものです。

以下を参考に、Mavenのビルドプロセスに組み込んでみましょう。

Bind to a lifecycle

Profile「docker」にして、通常のパッケージングとは別にしてみました。

    <profiles>
        <profile>
            <id>docker</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>com.google.cloud.tools</groupId>
                        <artifactId>jib-maven-plugin</artifactId>
                        <version>2.1.0</version>
                        <executions>
                            <execution>
                                <phase>package</phase>
                                <goals>
                                    <goal>dockerBuild</goal>
                                </goals>
                            </execution>
                        </executions>
                        <configuration>
                            <from>
                                <image>adoptopenjdk:11-jre-hotspot</image>
                            </from>
                            <to>
                                <image>kazuhira/jib-simple-jaxrs-server:0.0.1</image>
                            </to>
                            <container>
                                <jvmFlags>
                                    <jvmFlag>-Xmx256m</jvmFlag>
                                </jvmFlags>
                                <creationTime>2020-01-15T10:30:45+09:00</creationTime>
                                <ports>
                                    <port>8080</port>
                                </ports>
                            </container>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>

「package」ゴール実行時に、「dockerBuild」するようにしてあります。

                        <executions>
                            <execution>
                                <phase>package</phase>
                                <goals>
                                    <goal>dockerBuild</goal>
                                </goals>
                            </execution>
                        </executions>

あとは、Profileを指定してビルドしましょう。

$ mvn -Pdocker package

こんなところでしょうか。

まとめ

Jib Maven Pluginを使って、Dockerイメージを作ってみました。

その他、WARに対して適用できたりしますし、設定も他にたくさんあるのですが、慣れればREADME.mdを見つつ使えそうな気がしますね。

WAR Projects

今回は、こんなところで。