LoginSignup
7

More than 5 years have passed since last update.

OpenJDK11のdokcerイメージ(1GB)が大きいのでalpine linux+ jlinkで小さいイメージ(85MB)を作成する

Last updated at Posted at 2018-08-14

はじめに

openjdk 11のdocker imageは、docker hubのofficial respoitoryで公開されています。

ただ・・・これを使う上でちょっと気になるのが、

  • イメージサイズがそもそも大きい。約1GBもあります。
  • jlinkを使っても、300MB以上ある

という問題があります。もっと小さくしたいな。という所で、どれくらい小さくできるか試してみました!

前提

  • Java Flight Recorderが動くこと
  • Spring Boot アプリケーションが動くこと

結果

1GB→85MBと1/10以下に圧縮できました:grinning:
個人的には大満足です。

docker images でサイズを確認した結果は下記の通りです。

imageの種類 jlink使用 size
openjdk:11-jdk しない 1GB
openjdk:11-jdk する 468MB
独自alpinelinux しない 336MB
独自alpinelinux する 84.6MB

jdk11はea版です。そのうちちゃんとopenjdk 11のofficial imageでも小さいイメージが作れるようになると思います。
独自alpinelinuxは、
 https://hub.docker.com/r/hirokimatsumoto/alpine-openjdk-11/
です。

問題

OpenJDK 11のofficial imageの問題を詳細に記載します。

イメージサイズが大きい

$ sudo docker images |grep jdk
docker.io/openjdk                            11-jdk   f684efd78557  2 weeks ago         979 MB
$

jreではちょっとflightrecorderが動かないので、jdkを使いたいのに・・・ちょっと大きすぎます。
参考:Qiita - OpenJDK11-JRE-SLIMじゃFlightRecorderは動きません。

openjdk 11(official)ではjlink使っても小さくならない。

下記issueでも同じ問題を取り上げています。
https://github.com/docker-library/openjdk/issues/217

試しにやると・・・下記のようになぜか400MBほどになり、imageサイズも450MBくらいになってしまいます。

$ docker run -it --rm openjdk:11-jdk /bin/sh
# ls -l /usr/lib/jvm/java-11-openjdk-amd64/lib/server/
total 34944
-rw-r--r-- 1 root root     1322 Jul 27 03:41 Xusage.txt
-r--r--r-- 1 root root 18210816 Jul 27 22:22 classes.jsa
-rw-r--r-- 1 root root    14440 Jul 27 03:41 libjsig.so
-rw-r--r-- 1 root root 17551048 Jul 27 03:41 libjvm.so  #まだ小さい
圧縮してみる
# jlink \
     --module-path /opt/java/jmods \
     --compress=2 \
     --add-modules java.base,java.logging,jdk.jfr  \
     --no-header-files \
     --no-man-pages \
     --output /opt/jdk-11-mini-runtime
#  ls -l /opt/jdk-11-mini-runtime/lib/server/
total 414452
-rw-r--r-- 1 root root      1322 Aug 14 09:41 Xusage.txt
-rw-r--r-- 1 root root     25384 Aug 14 09:41 libjsig.so
-rw-r--r-- 1 root root 424362808 Aug 14 09:41 libjvm.so #なんでや・・・
#

イメージサイズ小さくするぞ!

alpine linuxを使う、そしてjlinkを使って小さくするというアプローチをとってみます。

alpine linuxのベースのbuild用イメージを作成する。

他の人でも約に立つかな?と思って公開してます。

image: https://hub.docker.com/r/hirokimatsumoto/alpine-openjdk-11/
docker file: https://hub.docker.com/r/hirokimatsumoto/alpine-openjdk-11/~/dockerfile/

checksumしてなくてすみません…。

jlinkを使って小さい実行イメージを作る。

multi stageを使ってビルドします。

  1. 上記で作ったalpine linux + openjdk 11のimageをベースに、jlinkで小さいjava runtime moduleを作成します。
  2. runtime module、jar moduleをコピーしたalphine linuxのimageを作成します。
FROM hirokimatsumoto/alpine-openjdk-11:latest as jlink-package
# First: generate java runtime module by jlink.

RUN jlink \
     --module-path /opt/java/jmods \
     --compress=2 \
     --add-modules jdk.jfr,jdk.management.agent,java.base,java.logging,java.xml,jdk.unsupported,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument \
     --no-header-files \
     --no-man-pages \
     --output /opt/jdk-11-mini-runtime

# Second: generate run image.
FROM alpine:3.8

ENV JAVA_HOME=/opt/jdk-11-mini-runtime
ENV PATH="$PATH:$JAVA_HOME/bin"

COPY --from=jlink-package /opt/jdk-11-mini-runtime /opt/jdk-11-mini-runtime
COPY target/k8s-jmc-sample-0.1.0-SNAPSHOT.jar /opt/spring-boot/

EXPOSE 30001 7199
CMD java \
    -XX:StartFlightRecording=name=sample,filename=/spring-boot/jfr/sample.jfr,delay=30s,maxage=2h,maxsize=10m,dumponexit=true,settings=/spring-boot/config/profile.jfc \
    -Dcom.sun.management.jmxremote.port=7199 \
    -Dcom.sun.management.jmxremote.rmi.port=7199 \
    -Dcom.sun.management.jmxremote.authenticate=false \
    -Dcom.sun.management.jmxremote.ssl=false \
    -jar /opt/spring-boot/k8s-jmc-sample-0.1.0-SNAPSHOT.jar \
    --server.port=${SPRING_SERVER_PORT} \
    --spring.config.location=/spring-boot/config/application.yaml

実行アプリケーションは、
https://github.com/h-r-k-matsumoto/k8s-jmc-sample
です。

ハマったこと

Spring Bootアプリケーションをjdepsで依存関係見ても正確ではない

最初jdepsを使い、下記のように必要なmoduleを確認してました。

> jdeps --list-deps .\target\k8s-jmc-sample-0.1.0-SNAPSHOT.jar
   java.base
   java.logging
>

これでruntime作っても・・・実行時に、下記のようにエラーになります。

Caused by: java.lang.ClassNotFoundException: java.sql.SQLException
        at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:471)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
        at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:93)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)

うーん、spring boot内部でauto configurationしている箇所は上手く追跡できないのかな・・・。
めんどくさかったので、
 https://dev.solita.fi/2018/01/24/Java9-modules-Spring-Boot-2-Docker.html
みて適当に追加しました。

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
7