jibでコンテナ化するJavaアプリケーションでSIGTERMを無視する方法

こんにちは。 エキサイト株式会社の三浦です。

Javaアプリケーションをコンテナとして動かしたいとき、jibを使うのは大きな選択肢の1つです。

jibを使えば非常に簡単にJavaアプリケーションをコンテナ化することが出来ますが、一方でカスタマイズしたい場合に少し困ってしまう場合もあるのではないでしょうか。

今回はその一例として、jibでコンテナ化するJavaアプリケーションでSIGTERMを無視させる方法を紹介します。

jibとは

jibは、Javaアプリケーションを簡単にコンテナ化してくれるライブラリです。

MavenやGradleでプラグインとして使用できます。

github.com

Jib builds optimized Docker and OCI images for your Java applications without a Docker daemon - and without deep mastery of Docker best-practices. It is available as plugins for Maven and Gradle and as a Java library.

今回は、Gradleで使っていきます。

使い方としては簡単で、 build.gradle

plugins {
  id 'com.google.cloud.tools.jib' version '3.2.1'
}

...

jib {
    from {
        image = 'eclipse-temurin:17.0.2_8-jdk-alpine'
    }
}

こんな形で設定をした後、

./gradlew jibDockerBuild -Djib.to.image=出力イメージ名

を実行すれば、ローカルのDockerデーモンにイメージが作成されます。

JVMの設定をしたい場合などは、以下のように少し設定を加えます。

jib {
    from {
        image = 'eclipse-temurin:17.0.2_8-jdk-alpine'
    }
    container {
        jvmFlags = ['-Duser.timezone=GMT+09', '-Xms1024M', '-Xmx1536M']
    }
}

単にイメージを作りたいだけならこれで良いのですが、今回の「SIGTERMを無視させる」ということをさせたい場合、少し複雑な設定をする必要があります。

jibでコンテナ化するJavaアプリケーションでSIGTERMを無視する方法

こちらで少し説明していますが、今回はSIGTERMを無視するために、dumb-initというライブラリを使用します。

tech.excite.co.jp

このライブラリは受け取ったOSシグナルを別のシグナルに変換してアプリケーションに流すことができるのですが、設定次第で指定したOSシグナルをアプリケーションに流さず落とすことも出来ます。

今回はその設定を使うのですが、その場合、2つ課題があります。

  1. dumb-initを、DockerfileのRUNを使ってダウンロード・インストールする
  2. DockerfileのENTRYPOINTを書き換える

これらは、それぞれ別の方法で解決する必要があります。

jib使用時に、dumb-initをDockerfile内でダウンロード・インストールする

jibでは、Dockerfileにおける RUN を設定できる項目は存在しません。

github.com

Running commands like apt-get slows down the container build process. We do not recommend or support running commands as part of the build.

そのため、jibの設定から RUN を設定するのではなく、すでに RUN を使って dumb-init をインストールし終わったイメージをベースのイメージとして使用する必要があります。

まずは、以下のDockerfileを作成し、適当な名前でイメージを作成します。 (なお、Javaのバージョンは適宜変更してください)

FROM eclipse-temurin:17.0.2_8-jdk-alpine

RUN apk update \
    && apk add --upgrade dumb-init

今回は eclipse-temurin-custom:17.0.2_8-jdk-alpine として作成したとします。

イメージを作成したら、以下のように設定すれば、そのイメージをjibがベースのイメージとして使用してくれます。

jib {
    from {
        image = 'docker://eclipse-temurin-custom:17.0.2_8-jdk-alpine'
    }
    container {
        jvmFlags = ['-Duser.timezone=GMT+09', '-Xms1024M', '-Xmx1536M']
    }
}

まずはこれで、 dumb-init がインストールされたイメージをjibに使わせることが出来ました。

DockerfileのENTRYPOINTを書き換える

続いて、インストールをした dumb-init を、 ENTRYPOINT から使用します。

SIGTERMを無視するには、 ENTRYPOINT で以下のように指定する必要があります。

ENTRYPOINT ["/usr/bin/dumb-init", "--rewrite", "15:0", "--", "以降は実行したいコマンド"]

jibでは、 ENTRYPOINT を書き換える事自体は可能なのですが、少し複雑な設定が必要になります。

github.com

実はjibで ENTRYPOINT を指定すると、先程の例に上げた jvmFlags を含むいくつかの設定が使えなくなってしまうのです。

そこで、例えば

jib {
    from {
        image = 'docker://eclipse-temurin-custom:17.0.2_8-jdk-alpine'
    }
    container {
        jvmFlags = ['-Duser.timezone=GMT+09', '-Xms1024M', '-Xmx1536M']
    }
}

この設定をそのまま使用して dumb-init を使用したい場合、jvmFlags の設定自体も移植して以下のように指定する必要があります。

jib {
    from {
        image = 'docker://eclipse-temurin-custom:17.0.2_8-jdk-alpine'
    }
    container {
        creationTime = 'USE_CURRENT_TIMESTAMP'
        entrypoint = [
                '/usr/bin/dumb-init', '--rewrite', '15:0', '--',
                'java',
                '-Duser.timezone=GMT+09', '-Xms1024M', '-Xmx1536M',
                '-cp', '@/app/jib-classpath-file',
                '@/app/jib-main-class-file'
        ]
    }
}

クラスパス・メインクラスも自分で設定する必要があるので注意しましょう。

なお、クラスパスは /app/jib-classpath-file というファイルに、メインクラスは /app/jib-main-class-file というファイルに自動的に書き出されるので、直接文字列で指定しなくてもそれらを使用すれば問題ないようです。

これでようやく、jibでコンテナ化したJavaアプリケーションがSIGTERMを無視してくれるようになりました。

最後に

jibは非常に便利なライブラリですが、その分複雑なことをしようとすると少し手間がかかる場合があります。

今回の記事が、そういったときに役に立てると幸いです。