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):
-
JRE non è distribuito come "pacchetto" separato. I moduli da JDK dovrebbero essere usati invece
-
Oracle OpenJDK 11 non supporta Linux Alpine , quindi leggero le immagini non possono essere create facilmente
- Allo stesso tempo le attuali versioni stabili di Debian non hanno ancora pacchetti Java 11 (Ubuntu ha installato Java 10 sotto i pacchetti openjdk-11), ecco perché le versioni sid instabili vengono utilizzate per le immagini docker di base
-
Attualmente disponibile Oracle openjdk-11 immagini costruire unstripped
libjvm.so
modulo, che ha centinaia di megabyte e deve essere spogliato separatamente:
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 ?
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:
-
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
-
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
- scarica e installa l'archivio
-
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
eHASH
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 fissandoClassNotFoundException
. In attesa di ulteriori sviluppatori di avvio di primavera raccomandazioni / guide quali moduli Java includere e quando, come la rimozione di alcune dipendenze ridondanti, comejava.desktop
, che sembra essere usato solo perPropertyEditorSupport
-
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.*
ejdk.*
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
- usa
-
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.
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
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
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
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