Application Java 11 en tant qu'image docker légère


Inspiré par la question Pourquoi l'image Docker de base Java 11 est-elle si grande? (openjdk: 11-jre-slim) J'ai trouvé que ce sujet dans le monde Java n'est toujours pas réglé.

Comme pour 07 Dec 2018 il y a des problèmes/pièges communs (discutés dans le ticket ci-dessus):

À la suite de ces problèmes, même slim Les images de base Oracle Java 11 sont assez lourdes et considérées comme instables: https://hub.docker.com/_/openjdk/

Donc la question est:

Quels sont optimisé ou recommandé façons de construire et de livrer Java 11 applications comme docker images?

Author: radistao, 2018-12-07

5 answers

UPD de 07.2019: https://stackoverflow.com/a/57145029/907576

En prenant comme exemple d'application spring boot simple (avec un seul point de terminaison REST) jusqu'à présent, j'ai pu trouver les solutions suivantes (étant donné que l'application jar est située à build/libs/spring-boot-demo.jar avant la construction de Docker:

  1. Jedi path si nous voulons utiliser distribution officielle Oracle OpenJDK sur la version stable slim Linux (Debian 9 "Stretch" pour maintenant):

    • utiliser debian:stretch-slim (dernière stable) image de base
    • Il est possible de créer une version à plusieurs étages de Docker.]}
      1. Première étape de construction Docker:

        • télécharger et installer Oracle OpenJDK archive sur la première étape de construction Docker
        • compiler la distribution minimale Java pour votre projet (aka JRE) en utilisant jlink tool
      2. Deuxième étape de construction Docker:

        • copier la distribution Java minimale compilée de l'étape 1 vers la nouvelle image
        • configurer le chemin d'accès à Java
        • copier le pot d'application dans l'image

    Donc, final Dockerfile ressemble qch comme ça

    (actualiser JDK VERSION, URL et HASH valeur):

    # First stage: JDK 11 with modules required for Spring Boot
    FROM debian:stretch-slim as packager
    
    # source JDK distribution names
    # update from https://jdk.java.net/java-se-ri/11
    ENV JDK_VERSION="11.0.1"
    ENV JDK_URL="https://download.java.net/java/GA/jdk11/13/GPL/openjdk-${JDK_VERSION}_linux-x64_bin.tar.gz"
    ENV JDK_HASH="7a6bb980b9c91c478421f865087ad2d69086a0583aeeb9e69204785e8e97dcfd"
    ENV JDK_HASH_FILE="${JDK_ARJ_FILE}.sha2"
    ENV JDK_ARJ_FILE="openjdk-${JDK_VERSION}.tar.gz"
    # target JDK installation names
    ENV OPT="/opt"
    ENV JKD_DIR_NAME="jdk-${JDK_VERSION}"
    ENV JAVA_HOME="${OPT}/${JKD_DIR_NAME}"
    ENV JAVA_MINIMAL="${OPT}/java-minimal"
    
    # downlodad JDK to the local file
    ADD "$JDK_URL" "$JDK_ARJ_FILE"
    
    # verify downloaded file hashsum
    RUN { \
            echo "Verify downloaded JDK file $JDK_ARJ_FILE:" && \
            echo "$JDK_HASH $JDK_ARJ_FILE" > "$JDK_HASH_FILE" && \
            sha256sum -c "$JDK_HASH_FILE" ; \
        }
    
    # extract JDK and add to PATH
    RUN { \
            echo "Unpack downloaded JDK to ${JAVA_HOME}/:" && \
            mkdir -p "$OPT" && \
            tar xf "$JDK_ARJ_FILE" -C "$OPT" ; \
        }
    ENV PATH="$PATH:$JAVA_HOME/bin"
    
    RUN { \
            java --version ; \
            echo "jlink version:" && \
            jlink --version ; \
        }
    
    # build modules distribution
    RUN jlink \
        --verbose \
        --add-modules \
            java.base,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument \
            # java.naming - javax/naming/NamingException
            # java.desktop - java/beans/PropertyEditorSupport
            # java.management - javax/management/MBeanServer
            # java.security.jgss - org/ietf/jgss/GSSException
            # java.instrument - java/lang/instrument/IllegalClassFormatException
        --compress 2 \
        --strip-debug \
        --no-header-files \
        --no-man-pages \
        --output "$JAVA_MINIMAL"
    
    # Second stage, add only our minimal "JRE" distr and our app
    FROM debian:stretch-slim
    
    ENV JAVA_HOME=/opt/java-minimal
    ENV PATH="$PATH:$JAVA_HOME/bin"
    
    COPY --from=packager "$JAVA_HOME" "$JAVA_HOME"
    COPY "build/libs/spring-boot-demo.jar" "/app.jar"
    
    EXPOSE 8080
    CMD [ "-jar", "/app.jar" ]
    ENTRYPOINT [ "java" ]
    

    Note:

    • il y a 5 modules java inclus dans l'exemple JRE minimal (java.base,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument). Je les ai trouvés" manuellement " en cours d'exécution de l'application et en fixant ClassNotFoundException. En attendant d'autres développeurs Spring Boot recommandations / guides quels modules Java inclure et quand, comme la suppression de certaines dépendances redondantes, comme java.desktop, qui semble être utilisé uniquement pour PropertyEditorSupport
    • Si vous avez peur de manquer certains modules - ils sont assez légers et tous ensemble donnent environ 2 Mo de taille croissante. Obtenez une liste complète des java.* et jdk.* 11 modules:

      java --list-modules | grep -E "^java\.[^@]*" | cut -d @ -f 1
      java --list-modules | grep -E "^jdk\.[^@]*" | cut -d @ -f 1

    La taille de l'image résultante dans mon cas était 123 Mo avec un ressort minimal de 7 Les modules de démarrage et 125 Mo avec tous les java.* modules

    Comme une amélioration facultative de ce flux de travail de construction :

    • Pré-construire une image avec JDK téléchargé et extrait et l'utiliser comme image de base pour la première étape
    • si vous savez quels modules inclure à chaque fois-pré-construire une image de base avec JRE minimal compilé et modules inclus
  2. Moyen facile avec distributions JDK ouvertes du fournisseur:

    Ci-contre pour Oracle Le Zulu JDK 11 d'Azul prend en charge le port alpin et a une base respective l'image Docker .

Ainsi, si Zulu JVM / JDK est respecté, Docker build est beaucoup plus simple:

FROM azul/zulu-openjdk-alpine:11 as packager

RUN { \
        java --version ; \
        echo "jlink version:" && \
        jlink --version ; \
    }

ENV JAVA_MINIMAL=/opt/jre

# build modules distribution
RUN jlink \
    --verbose \
    --add-modules \
        java.base,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument \
        # java.naming - javax/naming/NamingException
        # java.desktop - java/beans/PropertyEditorSupport
        # java.management - javax/management/MBeanServer
        # java.security.jgss - org/ietf/jgss/GSSException
        # java.instrument - java/lang/instrument/IllegalClassFormatException
    --compress 2 \
    --strip-debug \
    --no-header-files \
    --no-man-pages \
    --output "$JAVA_MINIMAL"

# Second stage, add only our minimal "JRE" distr and our app
FROM alpine

ENV JAVA_MINIMAL=/opt/jre
ENV PATH="$PATH:$JAVA_MINIMAL/bin"

COPY --from=packager "$JAVA_MINIMAL" "$JAVA_MINIMAL"
COPY "build/libs/spring-boot-demo.jar" "/app.jar"

EXPOSE 8080
CMD [ "-jar", "/app.jar" ]
ENTRYPOINT [ "java" ]

L'image résultante est 73 Mo, comme prévu avec les distributions alpines dépouillées.

 80
Author: radistao, 2019-08-26 08:36:01

À partir du 07.2019

(Note : l'image de la première étape pourrait être aussi fat que vous le souhaitez: on peut utiliser debian / ubuntu / whatever et inclure git / gradle / whatever-cela n'influencera pas la taille finale de l'image résultante, qui est complètement basée sur la dernière (deuxième) étape)

Utiliser communauté Alpine référentiel

FROM alpine:latest as packager

RUN apk --no-cache add openjdk11-jdk openjdk11-jmods

ENV JAVA_MINIMAL="/opt/java-minimal"

# build minimal JRE
RUN /usr/lib/jvm/java-11-openjdk/bin/jlink \
    --verbose \
    --add-modules \
        java.base,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument \
    --compress 2 --strip-debug --no-header-files --no-man-pages \
    --release-info="add:IMPLEMENTOR=radistao:IMPLEMENTOR_VERSION=radistao_JRE" \
    --output "$JAVA_MINIMAL"

FROM alpine:latest

ENV JAVA_HOME=/opt/java-minimal
ENV PATH="$PATH:$JAVA_HOME/bin"

COPY --from=packager "$JAVA_HOME" "$JAVA_HOME"
COPY build/libs/application.jar app.jar

ENTRYPOINT ["java","-jar","/app.jar"]

Utiliser AdoptOpenJDK

FROM adoptopenjdk/openjdk11:x86_64-alpine-jdk-11.0.4_11 as packager

ENV JAVA_MINIMAL="/opt/java-minimal"

# build minimal JRE
RUN jlink \
    --verbose \
    --add-modules \
        java.base,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument \
    --compress 2 --strip-debug --no-header-files --no-man-pages \
    --output "$JAVA_MINIMAL"

FROM alpine:latest

# magic to make Java binaries work in Alpine
# https://github.com/AdoptOpenJDK/openjdk-docker/blob/master/11/jdk/alpine/Dockerfile.hotspot.releases.slim#L24-L54
RUN apk add --no-cache --virtual .build-deps curl binutils \
    && GLIBC_VER="2.29-r0" \
    && ALPINE_GLIBC_REPO="https://github.com/sgerrand/alpine-pkg-glibc/releases/download" \
    && GCC_LIBS_URL="https://archive.archlinux.org/packages/g/gcc-libs/gcc-libs-9.1.0-2-x86_64.pkg.tar.xz" \
    && GCC_LIBS_SHA256="91dba90f3c20d32fcf7f1dbe91523653018aa0b8d2230b00f822f6722804cf08" \
    && ZLIB_URL="https://archive.archlinux.org/packages/z/zlib/zlib-1%3A1.2.11-3-x86_64.pkg.tar.xz" \
    && ZLIB_SHA256=17aede0b9f8baa789c5aa3f358fbf8c68a5f1228c5e6cba1a5dd34102ef4d4e5 \
    && curl -LfsS https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub -o /etc/apk/keys/sgerrand.rsa.pub \
    && SGERRAND_RSA_SHA256="823b54589c93b02497f1ba4dc622eaef9c813e6b0f0ebbb2f771e32adf9f4ef2" \
    && echo "${SGERRAND_RSA_SHA256} */etc/apk/keys/sgerrand.rsa.pub" | sha256sum -c - \
    && curl -LfsS ${ALPINE_GLIBC_REPO}/${GLIBC_VER}/glibc-${GLIBC_VER}.apk > /tmp/glibc-${GLIBC_VER}.apk \
    && apk add /tmp/glibc-${GLIBC_VER}.apk \
    && curl -LfsS ${ALPINE_GLIBC_REPO}/${GLIBC_VER}/glibc-bin-${GLIBC_VER}.apk > /tmp/glibc-bin-${GLIBC_VER}.apk \
    && apk add /tmp/glibc-bin-${GLIBC_VER}.apk \
    && curl -Ls ${ALPINE_GLIBC_REPO}/${GLIBC_VER}/glibc-i18n-${GLIBC_VER}.apk > /tmp/glibc-i18n-${GLIBC_VER}.apk \
    && apk add /tmp/glibc-i18n-${GLIBC_VER}.apk \
    && /usr/glibc-compat/bin/localedef --force --inputfile POSIX --charmap UTF-8 "$LANG" || true \
    && echo "export LANG=$LANG" > /etc/profile.d/locale.sh \
    && curl -LfsS ${GCC_LIBS_URL} -o /tmp/gcc-libs.tar.xz \
    && echo "${GCC_LIBS_SHA256} */tmp/gcc-libs.tar.xz" | sha256sum -c - \
    && mkdir /tmp/gcc \
    && tar -xf /tmp/gcc-libs.tar.xz -C /tmp/gcc \
    && mv /tmp/gcc/usr/lib/libgcc* /tmp/gcc/usr/lib/libstdc++* /usr/glibc-compat/lib \
    && strip /usr/glibc-compat/lib/libgcc_s.so.* /usr/glibc-compat/lib/libstdc++.so* \
    && curl -LfsS ${ZLIB_URL} -o /tmp/libz.tar.xz \
    && echo "${ZLIB_SHA256} */tmp/libz.tar.xz" | sha256sum -c - \
    && mkdir /tmp/libz \
    && tar -xf /tmp/libz.tar.xz -C /tmp/libz \
    && mv /tmp/libz/usr/lib/libz.so* /usr/glibc-compat/lib \
    && apk del --purge .build-deps glibc-i18n \
    && rm -rf /tmp/*.apk /tmp/gcc /tmp/gcc-libs.tar.xz /tmp/libz /tmp/libz.tar.xz /var/cache/apk/*

ENV JAVA_HOME=/opt/java-minimal
ENV PATH="$PATH:$JAVA_HOME/bin"

COPY --from=packager "$JAVA_HOME" "$JAVA_HOME"
COPY build/libs/application.jar app.jar

ENTRYPOINT ["java","-jar","/app.jar"]

Lire aussi https://blog.gilliard.lol/2018/11/05/alpin-jdk11-images.html

 18
Author: radistao, 2019-07-22 12:51:34

Une liste de jdk 11 images par taille

openjdk:11.0.6-jre-buster
openjdk:11.0.6-jre
openjdk:11.0.6-jre-slim-buster
openjdk:11.0.6-jre-slim
openjdk:11.0.6-jre-stretch
adoptopenjdk:11.0.6_10-jre-openj9-0.18.1
adoptopenjdk:11.0.6_10-jre-hotspot
adoptopenjdk:11.0.6_10-jre-openj9-0.18.1-bionic
adoptopenjdk:11.0.6_10-jre-hotspot-bionic
adoptopenjdk/openjdk11:jre-11.0.6_10-ubuntu
adoptopenjdk/openjdk11:jre-11.0.6_10
adoptopenjdk/openjdk11:jre-11.0.6_10-ubi-minimal
adoptopenjdk/openjdk11:jre-11.0.6_10-ubi
adoptopenjdk/openjdk11:jre-11.0.6_10-debianslim
adoptopenjdk/openjdk11:jre-11.0.6_10-debian
adoptopenjdk/openjdk11:jre-11.0.6_10-centos
adoptopenjdk/openjdk11:jre-11.0.6_10-alpine
adoptopenjdk/openjdk11:x86_64-alpine-jre-11.0.6_10
adoptopenjdk/openjdk11:x86_64-debian-jre-11.0.6_10
adoptopenjdk/openjdk11:x86_64-debianslim-jre-11.0.6_10
adoptopenjdk/openjdk11:x86_64-ubi-jre-11.0.6_10
adoptopenjdk/openjdk11:x86_64-ubi-minimal-jre-11.0.6_10
adoptopenjdk/openjdk11:x86_64-centos-jre-11.0.6_10
adoptopenjdk/openjdk11:x86_64-ubuntu-jre-11.0.6_10
mcr.microsoft.com/java/jre:11u6-zulu-alpine
mcr.microsoft.com/java/jre:11u6-zulu-centos
mcr.microsoft.com/java/jre:11u6-zulu-debian8
mcr.microsoft.com/java/jre:11u6-zulu-debian9
mcr.microsoft.com/java/jre:11u6-zulu-debian10
mcr.microsoft.com/java/jre:11u6-zulu-ubuntu
azul/zulu-openjdk-alpine:11.0.6-jre

image

 3
Author: Alisson Gomes, 2020-09-22 20:17:50

Vous pouvez également regarder liberica openjdk11 de bellsoft. Désolé pour beaucoup de citations mais de toute façon, le voici

Liberica est une implémentation 100% open-source de Java 11. Il est construit à partir d'OpenJDK auquel BellSoft contribue, est soigneusement testé et passé le JCK fourni sous la licence d'OpenJDK...

Leur version prête à l'emploi lite prend jusqu'à ~100 Mo. Il n'a pas de modules javafx et ses modules sont compressés (jlink --compress=2 at leur Dockerfile). En dehors de cela, il existe différents dépôts chez bellsoft Docker Hub account avec différentes options de OS/glibc/arch. E. g. à liberica-openjdk-alpin-musl ils disent:

Dockerfile pour Alpine Linux (variante musl) prend en charge trois images cibles prêtes à l'emploi:

Base: image d'exécution minimale avec java compressé.module de base, Serveur VM et fichiers optionnels dépouillés, ~37 Mo avec Alpine base

Lite: Liberica JDK lite image avec une empreinte minimale et une machine virtuelle de serveur, ~ 100 Mo (par défaut)

Complet: Liberica JDK image complète avec serveur VM et jmods, peut être utilisé pour créer arbitraire module ensemble, ~180 Mb

Pour économiser de l'espace, les utilisateurs sont encouragés à créer leurs propres runtimes en utilisant la commande jmod suffisante pour exécuter l'application cible

Et vous pouvez aller encore plus loin, au détriment de la performance:

Si vous êtes prêt à sacrifier les performances pour l'empreinte statique, veuillez envisagez d'utiliser une machine virtuelle minimale au lieu d'une machine virtuelle Serveur ou d'une machine virtuelle client. Avec cela, il est possible de créer un runtime aussi petit que

Quelques exemples de ma machine:

docker images 'bellsoft/liberica-openjdk-*' --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
REPOSITORY                              TAG                 SIZE
bellsoft/liberica-openjdk-alpine-musl   11.0.4-x86_64       102MB
bellsoft/liberica-openjdk-alpine        11.0.4              127MB
bellsoft/liberica-openjdk-centos        latest              307MB
 1
Author: Dmitry Khamitov, 2019-10-06 11:34:53

Basé sur la réponse deradistao (cool stuff!) J'ai créé une image basée sur Amazon Corretto JDK11 . Il est également disponible surDockerHub .

L'image minimale maslick/minimalka:jdk11Corretto est ~ 108 Mo (55 Mo compressés sur Dockerhub).

Si vous y ajoutez un simple jar Springboot, l'image résultante sera ~125 Mo (71 Mo compressés sur Dockerhub):

FROM maslick/minimalka:jdk11
WORKDIR /app
EXPOSE 8080
COPY my-cool-app.jar ./app.jar
CMD java $JAVA_OPTIONS -jar app.jar
docker build -t my-cool-app:latest .
docker run -d my-cool-app
 1
Author: maslick, 2020-05-28 19:20:54