Если вы какое-то время использовали язык на основе JVM (Java, Kotlin, Scala и т. д.), возможно, вы заметили, что, начиная с Java 11, для среды выполнения Java (JRE) больше не существует отдельного дистрибутива. Из-за этого решения многие распространители образов Docker Java Development Kit (JDK) (например: OpenJDK, Amazon Correto и т. д.) не предоставляют JRE как отдельный образ Docker, при использовании этих образов общий размер образа Docker составляет около 360 МБ, в то время как общий размер образа Docker составляет около 360 МБ. Фактический размер пакета Jar приложения составляет примерно 26 МБ. Мне кажется, что размер всего образа Docker слишком велик и приложение должно его уменьшить, чтобы сэкономить место и пропускную способность сети для всех, кто будет использовать этот образ Docker. Теперь давайте посмотрим, как радикально уменьшить размер образа Docker.
Система модулей платформы Java (JPMS) была представлена в Java 9. Мы можем использовать JPMS для создания собственной JRE, подходящей для конкретного приложения. Например, если приложение не использует функции аудио, обработки изображений или функции, связанные с JavaBeans, мы можем полностью удалить модуль java.desktop, чтобы освободить место в образе Docker. Как упоминалось ранее, начиная с Java 11, отдельного дистрибутива JRE больше не существует. Это означает, что даже если мы просто хотим запустить простое приложение на основе JVM, нам придется установить весь JDK. Это связано с модуляризацией, представленной в Java 9. Основная идея заключается в том, что каждое приложение должно иметь возможность создавать свою собственную JRE, а не предоставлять общую JRE, отвечающую потребностям каждого. Многие поставщики изображений JDK следуют той же философии и исключают распространение JRE. К сожалению, использование таких образов значительно увеличивает размер Docker-образа. Чтобы лучше понять проблему, давайте взглянем на базовый файл Dockerfile, необходимый для запуска простого приложения на основе JVM.
# greetings.Dockerfile
FROM amazoncorretto:17-alpine
EXPOSE 8080
COPY ./greetings/build/libs/greetings.jar /app/
WORKDIR /app
CMD ["java", "-jar", "greetings.jar"]
Мы используем его здесь amazoncorretto:17-alpine в качестве базового образа и копируем в него пакет Jar приложения. Наконец, мы запускаем пакет Jar. Давайте запустим этот Dockerfile, чтобы увидеть, насколько он велик.
ls -lh greetings/build/libs/greetings.jar | awk '{print $5, $9}'
# 26M greetings/build/libs/greetings.jar
docker build -t greetings:jdk -f greetings.Dockerfile .
docker image ls | grep greetings
# The output looks like following
# greetings jdk ca39786a6f62 2 hours ago 361MB
Тем не менее, образ размером 361 МБ довольно велик для пакета Jar размером 26 МБ, не так ли? Итак, как мы можем сделать его меньше?
Помимо модульности, Java 9 также содержит инструмент jlink. Основная цель этого инструмента — помочь нам создать Пользовательскую систему в соответствии с нашими потребностями. JRE. Этот инструмент предоставляет некоторые возможности для тонкой настройки JRE и необходимых модулей, но также предоставляет возможность создать универсальную JRE, содержащую все модули.
Давайте сначала посмотрим на общий образ Docker.
# greetings.Dockerfile
FROM amazoncorretto:17-alpine as corretto-jdk
# required for strip-debug to work
RUN apk add --no-cache binutils
# Build small JRE image
RUN jlink \
--add-modules ALL-MODULE-PATH \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress=2 \
--output /jre
FROM alpine:latest
ENV JAVA_HOME=/jre
ENV PATH="${JAVA_HOME}/bin:${PATH}"
COPY --from=corretto-jdk /jre $JAVA_HOME
EXPOSE 8080
COPY ./greetings/build/libs/greetings.jar /app/
WORKDIR /app
CMD ["java", "-jar", "greetings.jar"]
Давайте бегло взглянем на этот файл.
Теперь давайте создадим новый Dockerfile и проверим размер образа.
docker build -t greetings:jre -f greetings.Dockerfile .
docker image ls | grep greetings
# The output looks like following
# greetings jre d5f20dab834c 2 hours ago 123MB
Другими словами, размер нового образа составляет всего 123 МБ, что составляет почти треть исходного размера, и включает в себя все модули. Можем ли мы еще больше уменьшить размер, включив только необходимые модули? Да, но основная проблема заключается в том, как определить, какие модули необходимы для корректной работы приложения.
Мы можем использовать команду jdeps для определения необходимых модулей. Первый раз на Яве 8 jdeps
введено в,Используется для проверки зависимостей в приложениях. также. Вы также можете обнаружить каждый модуль Java, используемый каждой зависимостью библиотеки. перед запуском команды,Нам нужно извлечь файл Jar, чтобы он работал правильно.
unzip ./greetings/build/libs/greetings.jar -d temp
jdeps \
--print-module-deps \
--ignore-missing-deps \
--recursive \
--multi-release 17 \
--class-path="./temp/BOOT-INF/lib/*" \
--module-path="./temp/BOOT-INF/lib/*" \
./greetings/build/libs/greetings.jar
# The output will look like following
# java.base,java.compiler,java.desktop,java.instrument,java.management,java.naming,java.prefs,java.rmi,java.scripting,java.security.jgss,java.sql,jdk.httpserver,jdk.jfr,jdk.unsupported
rm -rf temp
Сначала мы извлекаем Jar приложения во временный каталог, а затем запускаем команду jdeps с несколькими параметрами конфигурации. Наконец, мы удаляем временный каталог.
Примечание. jdeps не сможет распечатать Reflection. Например, если приложение включает Spring Security, нам нужно вручную добавить модули jdk.crypto.ec и jdk.crypto.cryptoki.
Теперь мы заменим ALL-MODULE-PATH распечатанным списком jdeps.
# greetings.Dockerfile
FROM amazoncorretto:17-alpine as corretto-jdk
# required for strip-debug to work
RUN apk add --no-cache binutils
# Build small JRE image
RUN jlink \
--verbose \
--add-modules java.base,java.compiler,java.desktop,java.instrument,java.management,java.naming,java.prefs,java.rmi,java.scripting,java.security.jgss,java.sql,jdk.httpserver,jdk.jfr,jdk.unsupported,jdk.crypto.ec,jdk.crypto.cryptoki \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress=2 \
--output /jre
FROM alpine:latest
ENV JAVA_HOME=/jre
ENV PATH="${JAVA_HOME}/bin:${PATH}"
COPY --from=corretto-jdk /jre $JAVA_HOME
EXPOSE 8080
COPY ./greetings/build/libs/greetings.jar /app/
WORKDIR /app
CMD ["java", "-jar", "greetings.jar"]
Теперь давайте создадим новый Dockerfile и проверим размер образа.
docker build -t greetings:slimjre -f greetings.Dockerfile .
docker image ls | grep greetings
# The output looks like following
# greetings slimjre 450a64815cb3 46 minutes ago 89.8MB
Размер нового изображения составляет всего 90 МБ, что составляет почти четверть исходного размера. Разве это не лучше?
Из предыдущих результатов мы знаем, что Compact JRE лучше, чем Universal JRE. Однако у Slim JRE есть еще один небольшой недостаток. Если приложение все еще находится в стадии разработки, возможно, нам придется часто менять Dockerfile. Кроме того, поскольку мы меняем Dockerfile, Docker, возможно, не сможет повторно использовать все слои Dockerfile.
Если вы продолжите полагаться на использование урезанной JRE, мы сможем, по крайней мере, автоматизировать описанный выше процесс и немного облегчить себе жизнь. Чтобы автоматизировать процесс, ознакомьтесь со следующей сутью GitHub:
FROM amazoncorretto:17-alpine as corretto-deps
COPY ./greetings/build/libs/greetings.jar /app/
RUN unzip /app/greetings.jar -d temp && \
jdeps \
--print-module-deps \
--ignore-missing-deps \
--recursive \
--multi-release 17 \
--class-path="./temp/BOOT-INF/lib/*" \
--module-path="./temp/BOOT-INF/lib/*" \
/app/greetings.jar > /modules.txt
FROM amazoncorretto:17-alpine as corretto-jdk
COPY --from=corretto-deps /modules.txt /modules.txt
# hadolint ignore=DL3018,SC2046
RUN apk add --no-cache binutils && \
jlink \
--verbose \
--add-modules "$(cat /modules.txt),jdk.crypto.ec,jdk.crypto.cryptoki" \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress=2 \
--output /jre
# hadolint ignore=DL3007
FROM alpine:latest
ENV JAVA_HOME=/jre
ENV PATH="${JAVA_HOME}/bin:${PATH}"
COPY --from=corretto-jdk /jre $JAVA_HOME
EXPOSE 8080
COPY ./greetings/build/libs/greetings.jar /app/
WORKDIR /app
CMD ["java", "-jar", "greetings.jar"]
Как видите, нам удалось уменьшить размер изображения почти в три раза с минимальными усилиями. У нас есть два варианта.
Вам решать, какая JRE лучше всего подходит для вашего приложения. Однако независимо от того, какой вариант вы используете, вы можете значительно уменьшить размер изображения.