PodmanではじめるRed Hatのミドルウェア製品:OpenJDK(2)

Red Hatでソリューションアーキテクトをしている田中司恩(@tnk4on)です。 前回記事の続きになります。前回記事ではOpenJDKのコンテナイメージでjarファイルが実行される仕組みなどを深掘りしました。 今回はOpenJDKのコンテナイメージのサイズの確認やコンテナイメージを自作した場合との比較などを行います。

(前回記事はこちら) rheb.hatenablog.com

-目次-


OpenJDKのコンテナイメージとベースイメージとの差分を確認する

OpenJDK 21のコンテナイメージ
OpenJDK 21のコンテナイメージ

前回の記事でOpenJDKのコンテナイメージはUBI-minimalがベースイメージに使用されていることが分かりました。 では、元のUBI-minimalとの差分を確認してみましょう。UBI-minimalをダウンロードしpodman image treeを使ってレイヤーの差分を確認します。 今回もOpenJDKのコンテナイメージはregistry.access.redhat.com/ubi9/openjdk-21を使用します。手元にない方はpodman pullでコンテナイメージを入手してください。

$ podman pull registry.access.redhat.com/ubi9/openjdk-21
$ podman pull registry.access.redhat.com/ubi9/ubi-minimal
$ podman images
REPOSITORY                                   TAG         IMAGE ID      CREATED      SIZE
registry.access.redhat.com/ubi9/openjdk-21   latest      985daf3b31fb  10 days ago  422 MB
registry.access.redhat.com/ubi9/ubi-minimal  latest      d254c4a73500  11 days ago  104 MB
$ podman image tree openjdk-21:latest
Image ID: 985daf3b31fb
Tags:     [registry.access.redhat.com/ubi9/openjdk-21:latest]
Size:     422.4MB
Image Layers
├── ID: 256cf40975c5 Size: 104.3MB
└── ID: 25d8d0ec0c6b Size: 318.1MB Top Layer of: [registry.access.redhat.com/ubi9/openjdk-21:latest]

ベースとなるUBI-minimalが104MB、その差分の318MB程がOpenJDK用として追加されたとわかります。

podman diffコマンドで2つのコンテナイメージを比較します。

$ podman diff openjdk-21 ubi-minimal
A /help.1
C /home
A /home/default
A /home/default/.bash_logout
A /home/default/.bash_profile
A /home/default/.bashrc
A /home/default/.m2
A /home/default/.m2/settings.xml
C /var
C /var/run
C /var/cache
A /var/cache/ldconfig
A /var/cache/ldconfig/aux-cache
A /var/cache/libX11
A /var/cache/libX11/compose
C /var/lib
C /var/lib/rpm
C /var/lib/rpm/.rpm.lock
C /var/lib/rpm/rpmdb.sqlite
C /var/lib/rpm/rpmdb.sqlite-shm
C /var/lib/rpm/rpmdb.sqlite-wal
C /var/lib/alternatives
A /var/lib/alternatives/java_sdk_21
A /var/lib/alternatives/java_sdk_21_openjdk
A /var/lib/alternatives/java_sdk_openjdk
A /var/lib/alternatives/javac
A /var/lib/alternatives/jre_21
A /var/lib/alternatives/jre_21_openjdk
A /var/lib/alternatives/jre_openjdk
C /var/lib/alternatives/libnssckbi.so
A /var/lib/alternatives/java
A /var/lib/alternatives/mvn
...

すごい量のログが出力されます。7,000行以上の差分があるようです。

$ podman diff openjdk-21 ubi-minimal | wc -l
7096

Red Hatが作成したコンテナイメージではありますが、UBI-minimalのベースイメージに対してかなりの追加ファイルがあるようです。 Javaを動かすだけなのにここまでの追加が必要なのか?とちょっと疑ってしまいます。 そこで、OpenJDK入りのコンテナイメージを自作してできる限りスリムにしたものと、Red Hat提供のものとを比較してみようと思います。

OpenJDK入りの自作コンテナイメージを作成する

Red Hatが提供するOpenJDKのコンテナイメージではなく、自分でビルドしたOpenJDK入りのコンテナイメージの作成を行います。

まず、UBI-minimalをベースイメージとして使用し、OpenJDKのみをインストールするContainerfileを作成します。 UBI-minimalのパッケージマネージャーはdnfコマンドではなくmicrodnfコマンドを使用します。

$ cat > Containerfile <<EOF
FROM registry.access.redhat.com/ubi9/ubi-minimal
RUN microdnf install java-21-openjdk --setopt install_weak_deps=0 --nodocs -y \
&& microdnf clean all
EOF
  • OpenJDKはUBIリポジトリで配布されているパッケージのためサブスクライブされたRHELホスト以外でもインストール可能です
  • 可能な限りサイズを小さくしたいので、依存関係の少ないパッケージを除外する--setopt install_weak_deps=0オプションと、ドキュメント類のインストールを行わない--nodocsオプションを追加します

ubi-openjdkというタグを指定して新しいコンテナイメージをビルドします。ビルド後にイメージのリストを表示します。OpenJDKのコンテナイメージよりもサイズを減らすことができました。

$ podman build -t ubi-openjdk .
$ podman images
REPOSITORY                                   TAG         IMAGE ID      CREATED         SIZE
localhost/ubi-openjdk                        latest      4e2b302567f1  16 seconds ago  394 MB
registry.access.redhat.com/ubi9/openjdk-21   latest      985daf3b31fb  10 days ago     422 MB
registry.access.redhat.com/ubi9/ubi-minimal  latest      d254c4a73500  11 days ago     104 MB

作成したコンテナイメージのレイヤーを確認するとレイヤーが2つあります。

$ podman image tree ubi-openjdk
Image ID: 4e2b302567f1
Tags:     [localhost/ubi-openjdk:latest]
Size:     394.1MB
Image Layers
├── ID: 69d96fccdf6f Size: 104.3MB Top Layer of: [registry.access.redhat.com/ubi9/ubi-minimal:latest]
└── ID: 3a36d880c1ec Size: 289.9MB Top Layer of: [localhost/ubi-openjdk:latest]

これらを1つにまとめてさらに容量を減らしてみます。ビルド時に--squash-allを追加します。

$ podman build --squash-all -t ubi-openjdk-squash .
$ podman images
REPOSITORY                                   TAG         IMAGE ID      CREATED             SIZE
localhost/ubi-openjdk-squash                 latest      70909257c076  4 seconds ago       384 MB
localhost/ubi-openjdk                        latest      4e2b302567f1  About a minute ago  394 MB
registry.access.redhat.com/ubi9/openjdk-21   latest      985daf3b31fb  10 days ago         422 MB
registry.access.redhat.com/ubi9/ubi-minimal  latest      d254c4a73500  11 days ago         104 MB

先ほど作ったコンテナイメージより容量を減らすことができました。 さらにベースイメージを変更して容量の削減を目指してみます。

UBI-microを使ってOpenJDKのコンテナイメージを作成する

UBI-minimalよりサイズの小さいUBI-microを使ってみます。

UBI-microについては以前書いた記事がありますので、こちらを参考にしてください。 【UBI】Red Hatの新しい最軽量コンテナーイメージ:UBI Microの紹介 - 赤帽エンジニアブログ

なお、ここからはローカルホストにRHELを使用します。PodmanとBuildahが導入された環境を用意ください。

UBI-microを入手しサイズを確認します。UBI-minimalよりも小さいサイズであることが分かります。

$ cat /etc/redhat-release
Red Hat Enterprise Linux release 9.4 (Plow)
$ podman pull registry.access.redhat.com/ubi9/ubi-micro
$ podman images
REPOSITORY                                   TAG         IMAGE ID      CREATED             SIZE
localhost/ubi-openjdk-squash                 latest      70909257c076  27 seconds ago      384 MB
localhost/ubi-openjdk                        latest      4e2b302567f1  About a minute ago  394 MB
registry.access.redhat.com/ubi9/openjdk-21   latest      985daf3b31fb  10 days ago         422 MB
registry.access.redhat.com/ubi9/ubi-minimal  latest      d254c4a73500  11 days ago         104 MB
registry.access.redhat.com/ubi9/ubi-micro    latest      2d8598608a5d  2 weeks ago         24.7 MB

BuildahでUBI-microにパッケージを追加する流れ
BuildahでUBI-microにパッケージを追加する流れ

Buildahでマウントしたコンテナイメージに対話的にパッケージを追加します。ルートレスで実行する場合はユーザーネームスペースに入る必要があるためbuildah unshareまたはpodman unshareを実行します。

$ buildah unshare
# container=$(buildah from registry.access.redhat.com/ubi9/ubi-micro)
# mnt=$(buildah mount $container)
# dnf install --installroot $mnt --setopt install_weak_deps=false --nodocs java-21-openjdk -y
# dnf clean all --installroot $mnt
# buildah commit $container ubi-micro-openjdk
  • buildah from registry.access.redhat.com/ubi9/ubi-micro:作業用コンテナの元イメージを指定します
  • buildah mount $container:作業用コンテナをファイルシステムにマウントします
  • dnf install --installroot $mnt:マウントした領域に対してdnfコマンドでパッケージのインストールを行います
  • buildah commit $containerbuildah commitコマンドを使ってコンテナイメージをコミットします。

作成したコンテナイメージを確認するとUBI-minimalベースの自作コンテナイメージよりも小さいイメージができました。

# podman images
REPOSITORY                                   TAG         IMAGE ID      CREATED             SIZE
localhost/ubi-micro-openjdk                  latest      0aef77730e2e  7 seconds ago       378 MB
localhost/ubi-openjdk-squash                 latest      70909257c076  About a minute ago  384 MB
localhost/ubi-openjdk                        latest      4e2b302567f1  2 minutes ago       394 MB
registry.access.redhat.com/ubi9/openjdk-21   latest      985daf3b31fb  10 days ago         422 MB
registry.access.redhat.com/ubi9/ubi-minimal  latest      d254c4a73500  11 days ago         104 MB
registry.access.redhat.com/ubi9/ubi-micro    latest      2d8598608a5d  2 weeks ago         24.7 MB

イメージレイヤーを確認すると2つのレイヤーがあります。これを1つにまとめて(squash)してさらに容量を削減します。

# podman image tree ubi-micro-openjdk:latest
Image ID: 0aef77730e2e
Tags:     [localhost/ubi-micro-openjdk:latest]
Size:     378.3MB
Image Layers
├── ID: b7c370fe2e29 Size: 24.64MB Top Layer of: [registry.access.redhat.com/ubi9/ubi-micro:latest]
└── ID: 7377f93815e2 Size: 353.7MB Top Layer of: [localhost/ubi-micro-openjdk:latest]

buildah commit--squashオプションをつけて実行します。レイヤーが1つにまとめられ、さらに容量が減りました。

# buildah commit --squash $container ubi-micro-openjdk-squash
# podman images
REPOSITORY                                   TAG         IMAGE ID      CREATED         SIZE
localhost/ubi-micro-openjdk-squash           latest      507bfdf69b15  7 seconds ago   370 MB
localhost/ubi-micro-openjdk                  latest      0aef77730e2e  39 seconds ago  378 MB
localhost/ubi-openjdk-squash                 latest      70909257c076  2 minutes ago   384 MB
localhost/ubi-openjdk                        latest      4e2b302567f1  3 minutes ago   394 MB
registry.access.redhat.com/ubi9/openjdk-21   latest      985daf3b31fb  10 days ago     422 MB
registry.access.redhat.com/ubi9/ubi-minimal  latest      d254c4a73500  11 days ago     104 MB
registry.access.redhat.com/ubi9/ubi-micro    latest      2d8598608a5d  2 weeks ago     24.7 MB
# podman image tree ubi-micro-openjdk-squash
Image ID: 507bfdf69b15
Tags:     [localhost/ubi-micro-openjdk-squash:latest]
Size:     369.7MB
Image Layers
└── ID: afb99d712887 Size: 369.7MB Top Layer of: [localhost/ubi-micro-openjdk-squash:latest]

ビルド作業が終わったので作業用コンテナを削除してユーザーネームスペースから抜けます。

# buildah rm -a
# exit
exit
$

前回の記事と同様に、自作したコンテナイメージをベースイメージとして使ったjarファイルを実行するコンテナイメージをビルドします。 hello.jarは前回作成したものを流用します。

$ cat > Containerfile <<EOF
FROM ubi-micro-openjdk-squash
COPY hello.jar /hello.jar
EOF
$ podman build -t java-test .
$ podman images
REPOSITORY                                   TAG         IMAGE ID      CREATED             SIZE
localhost/java-test                          latest      8e82cd787a48  8 seconds ago       370 MB
localhost/ubi-micro-openjdk-squash           latest      507bfdf69b15  About a minute ago  370 MB
localhost/ubi-micro-openjdk                  latest      0aef77730e2e  About a minute ago  378 MB
localhost/ubi-openjdk-squash                 latest      70909257c076  3 minutes ago       384 MB
localhost/ubi-openjdk                        latest      4e2b302567f1  4 minutes ago       394 MB
registry.access.redhat.com/ubi9/openjdk-21   latest      985daf3b31fb  10 days ago         422 MB
registry.access.redhat.com/ubi9/ubi-minimal  latest      d254c4a73500  11 days ago         104 MB
registry.access.redhat.com/ubi9/ubi-micro    latest      2d8598608a5d  2 weeks ago         24.7 MB

作成したコンテナイメージを実行します。javaコマンドに続けて-jarオプションと実行するファイル(コンテナ内のパスを指定)を指定してコンテナを実行します。

$ podman run --rm java-test java -jar /hello.jar
Hello, World!

期待通りにHello Worldが実行されました。

このようにシンプルなjarファイルを1つ実行するだけなら自作のコンテナイメージを作るのとOpenJDKのコンテナイメージを使うのとでは大した差はありません。 むしろRed Hat提供のOpenJDKのコンテナイメージは決められたディレクトリに配置するだけでjarファイルを読み込んでくれたり、S2Iが実行できたり、チューニング用の環境変数がいろいろ使えたりなど、より実務で使うために必要な機能が豊富に盛り込まれています。 その機能の豊富さとコンテナイメージを自作しメンテナンスする手間を比べた場合のメリットがどれほどのものかをよく考える必要があります。

まとめ

OpenJDKのコンテナイメージについて、使用されているベースイメージやイメージのレイヤー、イメージのサイズという観点で深堀りしてみました。 イメージのサイズが小さいということはコンテナのライフサイクルにおけるPushやPull(それらに伴う圧縮解凍)といった作業時間の軽減に寄与します。 また、コンテナレジストリやローカルストレージの保存領域の観点でも容量削減が見込めますが、昨今のストレージの規模から考えたらそこまで気にする点でもありません。 さらに、コンテナプロセスのリソース消費やコンテナの起動速度は実行するアプリに依存するため、コンテナサイズが直接的に影響するものではありません。

コンテナのサイズは利便性とのバランスになります。UBI-microはサイズが小さく、またパッケージの追加更新作業は手間がかかりますが、静的リンクでコンパイルされた実行バイナリをコピーするだけの用途ではとても有用です。Red Hatが提供するOpenJDKのコンテナイメージはS2IやJavaアプリの自動実行に対応し、さらにRed Hatがコンテナイメージのメンテナンスもして容量もそこそこ小さいです。 自作のコンテナイメージと比較するとRed Hatが提供するOpenJDKのコンテナイメージを活用する利点は十分あると考えます。

Red Hatが提供するOpenJDKのコンテナイメージはよく考えられて作られているので、みなさんどんどん活用しましょう!

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。