Java アプリケーションの Docker イメージサイズは重い
シンプルな Java アプリケーションの Dockerfile はこのようになっているのではないでしょうか?
# syntax=docker/dockerfile:1
FROM amazoncorretto:21.0.9-al2023
WORKDIR /deployments
ARG JAR_FILE=build/libs/distroless.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
これだと、非常に重いイメージになってしまいます。
amazoncorretto:21.0.9-al2023 のサイズが 213.79 MB なので、このイメージはそこに fat jar の重さが加わります。
Docker イメージが重いと push, pull に時間がかかり、開発者体験の悪化に繋がります。
そこで、軽量なベースイメージを使用することで、イメージの軽量化を行います。
なぜ Distroless なのか
軽量なベースイメージとして、以下が挙げられます。
- Alpine
- Debian-slim
- Distroless
Alpine は軽量なベースイメージとしてよく名前を聞き、amazoncorretto のイメージとしても Alpine ベースの物が提供されています。
サイズは 157.71 MB で、先ほどの al2023 ベースの物よりは軽量です。
しかし、C 言語のライブラリである glibc を使用していないことからトラブルがあると聞くので、私は使用したことがありません。(参考)
Debian-slim は Debian ベースなので glibc による問題はありません。
しかし、amazoncorretto から Debian-slim がベースとなったイメージの提供はないため、Debian-slim に JVM を載せる必要があります。
もしかしたら、amazoncorretto 以外のイメージだと Debian-slim ベースのものがあるかもしれません。
Distroless は、Debian ベースのイメージでありながら、shell を含んでいないためさらに軽量です。
Java 用のイメージも提供されています。
私は軽量なイメージが好きなので、Debian-slim よりも軽量な Alpine, Distroless。
Alpine よりもトラブルが少なそうな Distroless を使いたいと思いました。
Distroless の Java 用イメージ
Java や Debian のバージョンによって何種類かあるのですが、大まかに 2 種類あります。
- JVM が入っているイメージ
- JVM が動く環境があるイメージ
詳細や使い方は Distroless の Readme にありますが、2 種類のイメージの使用例を紹介します。
JVM が入っている Distroless
# syntax=docker/dockerfile:1
FROM gcr.io/distroless/java21-debian12:nonroot
WORKDIR /deployments
ARG JAR_FILE=build/libs/distroless.jar
COPY --chown=nonroot:nonroot ${JAR_FILE} app.jar
CMD ["app.jar"]
Readme にある通り、ベースの ENTRYPOINT に java -jar が指定されているので、CMD で fat jar を指定すれば動きます。
JVM が動く環境がある Distroless
# syntax=docker/dockerfile:1
FROM amazoncorretto:21.0.9-al2023 AS build
WORKDIR /jlink
RUN jlink \
--verbose \
--compress=2 \
--strip-java-debug-attributes \
--no-header-files \
--no-man-pages \
--add-modules java.base,java.desktop,java.instrument,java.logging,java.management,java.naming,jdk.jfr,jdk.unsupported \
--output jre-min
FROM gcr.io/distroless/java-base-debian12:nonroot
WORKDIR /deployments
COPY --from=build --chown=nonroot:nonroot /jlink/jre-min /opt/jre-min
ENV JAVA_HOME=/opt/jre-min
ENV PATH=$JAVA_HOME/bin:$PATH
ARG JAR_FILE=build/libs/distroless.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
JVM が動く環境はあるが、JVM は入っていないイメージです。
そのため、アプリケーションを動かすための最小限の JVM を構成することで、さらにイメージの軽量化が可能です。
この例では、マルチステージビルドを使用して、amazoncorretto から最小限のモジュールを含んだ JRE を作成し、Distroless にコピーしています。
3 つのイメージの重さ比較
M4 Mac でのビルドで arm64 プラットフォームになるため、amd64 よりはイメージが重くなっています。
| amazoncorretto | Distroless | Distroless-base |
|---|---|---|
| 811 MB | 312 MB | 200 MB |
まとめ
Distroless とマルチステージビルドによるカスタム JRE を使用することで、JVM 提供のイメージよりも 1/4 軽量なイメージを作成することができます。
イメージサイズを小さくすることで、開発者体験を良くしましょう。