Applicazione Java 11 come immagine docker leggera


Ispirato alla domanda Perché l'immagine Docker di base Java 11 è così grande? (openjdk: 11-jre-slim) Ho scoperto che questo argomento nel mondo Java non è ancora risolto.

Per quanto riguarda {[0] } ci sono problemi/insidie comuni (discussi nel biglietto sopra):

Come risultato di questi problemi anche slim Le immagini di base di Oracle Java 11 sono piuttosto pesanti e considerate instabili: https://hub.docker.com/_/openjdk /

Quindi la domanda è:

Cosa sono ottimizzato o consigliato modi per creare e distribuire applicazioni Java 11 come immagini docker ?

Author: radistao, 2018-12-07

5 answers

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

Prendendo come esempio di semplice applicazione di avvio a molla (con un solo endpoint REST) finora sono stato in grado di capire le seguenti soluzioni (considerando che application jar si trova in build/libs/spring-boot-demo.jar prima della compilazione di Docker:

  1. Jedi path se vogliamo usare distribuzione ufficiale Oracle OpenJDK su versione stabile slim Linux (Debian 9 "Stretch" per ora):

    • usa debian:stretch-slim (ultima stabile) immagine di base
    • Usa Creazione a più stadi della finestra mobile

      1. Prima fase di creazione della finestra mobile:

        • scarica e installa l'archivio Oracle OpenJDK nella prima fase di compilazione della finestra mobile
        • compila la distribuzione minima Java per il tuo progetto (alias JRE) usando lo strumento jlink
      2. Seconda fase di creazione della finestra mobile:

        • copia la distribuzione Java minima compilata dalla fase 1 alla nuova immagine
        • configura il percorso per accedere a Java
        • copia il jar dell'applicazione nell'immagine

    Quindi, final Dockerfile sembra smth come questo

    (attualizzare JDK VERSION, URL e HASH valore):

    # 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" ]
    

    Nota:

    • ci sono 5 moduli java inclusi nell'esempio JRE minimo (java.base,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument). Li ho trovati "manualmente" eseguendo l'applicazione e fissando ClassNotFoundException. In attesa di ulteriori sviluppatori di avvio di primavera raccomandazioni / guide quali moduli Java includere e quando, come la rimozione di alcune dipendenze ridondanti, come java.desktop, che sembra essere usato solo per PropertyEditorSupport
    • Se hai paura di perdere alcuni moduli - sono abbastanza leggeri e tutti insieme danno circa 2 MB di dimensione in aumento. Ottieni un elenco completo dei moduli java.* e jdk.* 11:

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

    La dimensione dell'immagine risultante nel mio caso era 123 MB con una molla minima di 7 Moduli di avvio e 125 MB con tutti i moduli java.*

    Come miglioramento opzionale di questo flusso di lavoro di compilazione:

    • Pre-crea un'immagine con JDK scaricato ed estratto e usala come immagine di base per il primo stadio
    • se sai quali moduli includere ogni volta, pre-costruisci un'immagine di base con JRE minimo compilato e moduli inclusi
  2. Modo semplice con le distribuzioni JDK aperte del fornitore :

    Opposto per Oracle Azul Zulu JDK 11 supporta Alpine port e ha rispettiva base Immagine Docker .

Quindi, se Zulu JVM / JDK è rispettato, la compilazione di Docker è molto più semplice:

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'immagine risultante è 73 MB, come previsto con distribuzioni Alpine stripped.

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

A partire dal 07.2019

(Nota : l'immagine del primo stadio potrebbe essere fat come desideri: si può usare debian / ubuntu / whatever e includere git / gradle / whatever - questo non influenzerà la dimensione finale dell'immagine risultante, che è completamente basata sull'ultimo (secondo) stadio)

Utilizzando Repository della comunità alpina

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"]

Utilizzando ADOTTAREOPENJDK

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"]

Leggere anche https://blog.gilliard.lol/2018/11/05/alpine-jdk11-immagini.html

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

Un elenco di jdk 11 immagini per dimensione

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

immagine

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

Puoi anche guardare liberica openjdk11 di bellsoft. Ci scusiamo per un sacco di citazioni ma comunque, eccolo qui

Liberica è un'implementazione Java 11 open source al 100%. È costruito da OpenJDK a cui BellSoft contribuisce, è accuratamente testato e superato il JCK fornito sotto la licenza di OpenJDK...

La loro versione lite pronta all'uso richiede fino a ~100 MB. Non ha moduli javafx e i suoi moduli sono compressi (jlink --compress=2 a il loro Dockerfile). Oltre a ciò, ci sono vari repository su bellsoft Docker Hub account con diverse opzioni di OS/glibc/arch. Ad esempio a liberica-openjdk-alpine-musl dicono:

Dockerfile per Alpine Linux (variante musl) supporta tre immagini di destinazione pronte all'uso:

Base: immagine runtime minima con java compresso.modulo base, VM server e file opzionali spogliati, ~37 MB con Alpine base

Lite: Liberica JDK lite immagine con ingombro minimo e VM server, ~ 100 MB (default)

Pieno: Liberica JDK immagine completa con Server VM e jmods, può essere utilizzato per creare arbitrario modulo set, ~180 MB

Per risparmiare spazio, gli utenti sono invitati a creare i propri runtime utilizzando il comando jmod sufficiente per eseguire l'applicazione di destinazione

E puoi andare ancora oltre a scapito delle prestazioni:

Se sei pronto a sacrificare le prestazioni per l'impronta statica, per favore considerare l'utilizzo di VM minima invece di VM server o VM client. Con questo, è possibile creare un runtime piccolo come

Alcuni esempi dalla mia macchina:

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

Basato sulla risposta di radistao (cose interessanti!) Ho creato un'immagine basata su Amazon Corretto JDK11. È disponibile anche su DockerHub .

L'immagine minima maslick/minimalka:jdk11Corretto è ~ 108 MB (55 MB compressi su Dockerhub).

Se si aggiunge un semplice jar Springboot ad esso, l'immagine risultante sarebbe ~ 125MB (71MB compresso su 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