見出し画像

Quarkusアプリケーションをコンテナー化する方法


はじめに

Quarkus in Actionのサンプルアプリケーションを使ってQuarkusの機能を紹介するシリーズの3回目です。

第1回 SDKMANを使ってQuarkusを学習するための環境を整備する
第2回 GraalVMを使ってQuarkusアプリケーションからネイティブイメージを生成する

第3回目の今回はQuarkusアプリケーションをコンテナーで動かすための方法について説明します(Quarks in ActionのSection 2.6 あたりです)。

Quarkusは、最初からKubernetes上で動かすことが意図されているので、コンテナー化が簡単にできるようになっています。コンテナー内でネイティブイメージを直接実行すればJavaのランタイムを使わないのでアプリケーションを起動が早くなります。

インストール環境の前提

動作環境としてはLinuxを想定しています。Fedora 41とUbuntu 24.10で動作することを確認済みです。SDKMANで以下のJavaツールがインストール済みであることを前提にしています。

$ sdk current
Using:
java: 21.0.5-tem
maven: 3.9.9
quarkus: 3.17.7

これらのLinux環境でdockerやpodmanコマンドがインストール済みであれば、GraalVMが環境にインストールされていなくても、ネイティブイメージを生成することができます。

注意
今回もネイティブイメージを生成しますので、MacOSの方は、GraalVM CEを事前にインストールしておいてください。SDKMANを使ったGraalVM CEのインストール方法は前回の記事をご覧ください。

今回もコマンドの実行ログの一部を参考例として添付します。実行環境はFedora 41を使いました。実行環境の詳細は前回の記事を見てください。

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

前回同様、Quarkus CLIを使ってREST API実装の雛形を生成し、これにもとづいてコンテナーイメージを生成していきます。

$ quarkus create app org.acme:quarkus-in-action --extension quarkus-rest

Quarkusが生成するJARの形式

QuarkusをビルドとするとJARまたはネイティブイメージを生成することができますので、アプリケーションをコンテナーで実行するときは、以下の2択になります。

  • コンテナー内で、Javaランタイムを使ってJARファイルを実行する。

  • コンテナー内で、ネイティブイメージを直接実行する。

3種類のJAR形式

Quarkusを使った場合、実はJARファイルの形式にはいくつかの種類が存在します。Quarkusは以下の3種類の形式のJARを生成することができます(デフォルトはfast-jar)。これらはビルド時の設定によって指定可能です。

  • fast-jar: Quarkus およびデフォルトの設定オプション用に最適化された JAR ファイル。その結果、起動時間がわずかに短縮され、メモリー使用量がわずかに減少します。

  • legacy-jar: 典型的な JAR ファイル。

  • uber-jar: 単一のスタンドアロン JAR ファイル。

legacy-jarやuber-jarはJavaでよく知られているJAR形式ですが、fast-jarは馴染みのない方が多いと思いますので少し補足します。

Fast-jar のディレクトリ構造

fast-jarは、以下のようにtargetの下のquarkus-appディレクトリに依存関係のあるJARファイルが展開されています。JARの中にさらにJARを含むような入れ子の構造を避けたり、起動時に必要なJARだけを読み込むようにClassloaderを工夫することで起動が早くなっているのです。

$ tree -L 2 target/quarkus-app/
target/quarkus-app/
├── app
│   └── quarkus-in-action-1.0.0-SNAPSHOT.jar
├── lib
│   ├── boot
│   └── main
├── quarkus
│   ├── generated-bytecode.jar
│   ├── quarkus-application.dat
│   └── transformed-bytecode.jar
├── quarkus-app-dependencies.txt
└── quarkus-run.jar

uber-jarのように単一のJARファイルに詰め込まれている訳ではないので、アプリケーションをデプロイするときにはこのquarkus-appディレクトリごと扱う必要があります。後述するように、コンテナーイメージを作るときには、これらのディレクトリをイメージにコピーします。

fast-jarを起動するときには、以下のようにquarkus-appの下のquarkus-run.jarが起点になります。

java -jar target/quarkus-app/quarkus-run.jar

参考
fast-jarの詳細を知りたい方は以下の記事を参照してください。
[1]
 https://ja.quarkus.io/blog/quarkus-1-12-0-final-released/
[2] https://developers.redhat.com/blog/2021/04/08/build-even-faster-quarkus-applications-with-fast-jar#
[3]
 https://blog.worldline.tech/2023/09/05/quarkus-fast-jar.html?utm_source=chatgpt.com

Quarkusプロジェクトに含まれるDockerfileの種類

quarkus create appコマンドで生成したプロジェクトには、最初からDockerfileが含まれているので、すぐにコンテナーイメージを生成することができます。

プロジェクトには以下の4種類のDockerfileが含まれています。

$ tree quarkus-in-action/src/main/docker/
quarkus-in-action/src/main/docker/
├── Dockerfile.jvm
├── Dockerfile.legacy-jar
├── Dockerfile.native
└── Dockerfile.native-micro

0 directories, 4 files
  • Dockerfile.jvm: fast-jarを含むコンテナーイメージを生成

  • Dockerfile.legacy-jar: legacy-jarを含むコンテナーイメージを生成

  • Dockerfile.native: ネイティブイメージを含むコンテナーイメージを生成 (親イメージとしてubi8/ubi-minimal:8.10を指定)

  • Dockerfile.native-micro: ネイティブイメージを含むコンテナーイメージを生成 (親イメージとしてより軽量なquay.io/quarkus/quarkus-micro-image:2.0を指定)

Dockerfileの例 (fast-jarの場合)

コンテナーイメージのビルド方法や、コンテナーの実行方法はこれらのDockerfileのコメントとして書かれています。以下、サンプルとしてDockerfile.jvmのコマンド部分のみを抜き出してみます。

# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
#
# Before building the container image run:
#
# ./mvnw package
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/quarkus-in-action-jvm .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/quarkus-in-action-jvm

このコマンドをそのまま実行すれば、コンテナーイメージを作成し、コンテナーを実行することができます。

次に、Dockerfile.jvmの本体を見てみましょう。親イメージとして、ubi8/openjdk-21が指定されています(つまりJDKがイメージに含まれています)。また、fast-jarをイメージ内に配置するため、COPY命令によってtarget/quarkus-app配下のファイルやディレクトリがまるごとイメージ内にコピーされていることがわかります。

FROM registry.access.redhat.com/ubi8/openjdk-21:1.20

ENV LANGUAGE='en_US:en'


# We make four distinct layers so if there are application changes the library layers can be re-used
COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/
COPY --chown=185 target/quarkus-app/*.jar /deployments/
COPY --chown=185 target/quarkus-app/app/ /deployments/app/
COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/

EXPOSE 8080
USER 185
ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"

ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]

Dockerfileを使ったコンテナーイメージのビルド

Dockerfile.jvmの場合

Dockerfile.jvmを使ってfast-jarを含むコンテナーイメージをビルドし、実行してみます。上のコメントでは./mvnw packageを実行していましたが、ここでは代わりにquakus buildコマンドを使ってみます。FedoraなどRed Hat系のOS上で動かす場合は、dockerコマンドではなく、podmanを使います。

$ quarkus build
$ podman build -f src/main/docker/Dockerfile.jvm -t quarkus/quarkus-in-action-jvm .

podman buildの結果としてコンテナーイメージが生成されていることを確認します。quarkus/quarkus-in-action-jvmのサイズは427MBです。

$ podman image ls
REPOSITORY                                  TAG         IMAGE ID      CREATED         SIZE
localhost/quarkus/quarkus-in-action-jvm     latest      f31f61651207  18 seconds ago  427 MB
registry.access.redhat.com/ubi8/openjdk-21  1.20        e50fb251f7bd  2 months ago    410 MB

quarkus/quarkus-in-action-jvmのイメージを実行します。以下の例では、起動に0.347秒かかっています。

$ podman run -i --rm -p 8080:8080 quarkus/quarkus-in-action-jvm
<略>
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2025-01-30 14:05:21,243 INFO  [io.quarkus] (main) quarkus-in-action 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.18.1) started in 0.347s. Listening on: http://0.0.0.0:8080
2025-01-30 14:05:21,245 INFO  [io.quarkus] (main) Profile prod activated. 
2025-01-30 14:05:21,245 INFO  [io.quarkus] (main) Installed features: [cdi, rest, smallrye-context-propagation, vertx]

Dockerfile.native-microの場合

次に比較のため、Dockerfile.native-microを使って、ネイティブイメージを含む軽量なコンテナーイメージをビルドして、実行してみます。

$ quarkus build --native
$ podman build -f src/main/docker/Dockerfile.native-micro -t quarkus/quarkus-in-action-micro .

podman build --nativeの結果としてコンテナーイメージが生成されていることを確認します。quarkus/quarkus-in-action-microのサイズは81MBです。JDKを含むイメージよりだいぶ小さくなりました。

$ podman image ls
REPOSITORY                                         TAG         IMAGE ID      CREATED         SIZE
localhost/quarkus/quarkus-in-action-micro          latest      36f0e4620a91  43 seconds ago  81 MB
localhost/quarkus/quarkus-in-action-jvm            latest      f31f61651207  14 minutes ago  427 MB
quay.io/quarkus/quarkus-micro-image                2.0         f80f4bcc92c3  4 weeks ago     30.4 MB
quay.io/quarkus/ubi-quarkus-mandrel-builder-image  jdk-21      2bf317620adf  4 weeks ago     1.15 GB
registry.access.redhat.com/ubi8/openjdk-21         1.20        e50fb251f7bd  2 months ago    410 MB

quarkus/quarkus-in-action-microのイメージを実行します。以下の例では、起動に0.009秒かかっています。Javaを使わずに起動しているので早いですね。

$  podman run -i --rm -p 8080:8080 quarkus/quarkus-in-action-micro
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2025-01-30 14:19:15,496 INFO  [io.quarkus] (main) quarkus-in-action 1.0.0-SNAPSHOT native (powered by Quarkus 3.18.1) started in 0.009s. Listening on: http://0.0.0.0:8080
2025-01-30 14:19:15,496 INFO  [io.quarkus] (main) Profile prod activated. 
2025-01-30 14:19:15,496 INFO  [io.quarkus] (main) Installed features: [cdi, rest, smallrye-context-propagation, vertx]

まとめ

この記事ではQuarkusアプリケーションをコンテナー化する方法について紹介しました。

quarkus create appコマンドを使うといくつかの用途の異なるDockerfileが用意されていました。これらのDockerfileを使えば、簡単にコンテナーイメージを作ることができました。

実は、quarkus image buildコマンドを使うとpodmanやdockerコマンドを使わずにコンテナーイメージを生成することができます。

# JVMを含むビルド
$ quarkus image build jib

# ネイティブイメージを含むビルド
$ quarkus image build jib --native

上のコマンドではJibというQuarkus Extensionを指定していますが、他にもDockerやPodman、BuildPack、OpenShiftといったExtensionを指定することでさまざまなコンテナーイメージのビルドが可能になります(OpenShiftを指定するとマニフェストファイルが自動生成されます)。

でも、今回説明したDockerfileの仕組みは理解しておいた方が良いです。quarkus image buildのオプションを設定することで、この記事で説明したDockerfileのパスを指定することができるからです(つまり、カスタマイズしたDockerfileも指定可能ということ)。

quarkus image buildコマンドについて説明を始めると長くなりそうです。Quarkus in Actionでは、このあたりの説明はChapter 11にありますので、また別の機会に説明しますね。


いいなと思ったら応援しよう!