Как уменьшить размер изображения докеры java/gradle?
У меня есть файл Docker, например:
FROM openjdk:8
ADD . /usr/share/app-name-tmp
WORKDIR /usr/share/app-name-tmp
RUN ./gradlew build \
mv ./build/libs/app-name*.jar /usr/share/app-name/app-name.jar
WORKDIR /usr/share/app-name
RUN rm -rf /usr/share/app-name-tmp
EXPOSE 8080
RUN chmod +x ./docker-entry.sh
ENTRYPOINT [ "./docker-entry.sh" ]
Проблема в том, что конечный размер изображения составляет 1,1 ГБ, я знаю, что это происходит, потому что gradle загружает и сохраняет все зависимости. Каков наилучший способ удалить эти ненужные файлы и просто сохранить банку?
Ответы
Ответ 1
Каждая инструкция RUN создает новый слой поверх существующей файловой системы. Таким образом, новый слой после инструкции RUN, который удаляет ваш каталог app-name-tmp
, просто маскирует предыдущий слой, содержащий загруженные библиотеки. Следовательно, ваше изображение докеры все еще имеет такой размер из всех построенных слоев.
Удалите отдельную инструкцию RUN rm -rf /usr/share/app-name-tmp
и включите ее в ту же инструкцию RUN, которая построит gradle, как показано ниже.
RUN ./gradlew build \
mv ./build/libs/app-name*.jar /usr/share/app-name/app-name.jar \
rm -rf /usr/share/app-name-tmp/*
Итак, ваш окончательный файл Docker будет
FROM openjdk:8
ADD . /usr/share/app-name-tmp
WORKDIR /usr/share/app-name-tmp
RUN ./gradlew build \
mv ./build/libs/app-name*.jar /usr/share/app-name/app-name.jar \
rm -rf /usr/share/app-name-tmp/*
WORKDIR /usr/share/app-name
EXPOSE 8080
RUN chmod +x ./docker-entry.sh
ENTRYPOINT [ "./docker-entry.sh" ]
Созданное изображение по-прежнему будет содержать размер из каталога/usr/share/app-name-tmp.
Ответ 2
Я действительно смущен размером вашего изображения. У меня типичные приложения Spring для загрузки, предлагающие услугу REST, включая встроенный контейнер сервлетов менее 200 МБ! Похоже, что ваши зависимости проекта могут и должны быть оптимизированы.
Изображение Docker
openjdk:8
(сжатие 243 МБ) можно заменить на единицу с уменьшенным альпийским unix-изображением, таким как openjdk:8-jdk-alpine
(52 МБ) в качестве базового изображения, но если вам не нужны возможности компилятора (например, не используйте JSP) вы также можете перейти на openjdk:8-jre-alpine
(42 МБ), который включает только время выполнения, посмотрите Docker Hub. Я использую это для Spring сервисов REST на основе загрузки, работающих отлично.
Зависимости Java
Зависимости Java, необходимые для компиляции и времени выполнения, должны быть включены, но вы можете включить неиспользуемые зависимости:
- проверьте свои зависимости, текущие зависимости от компиляции/времени выполнения, которые действительно используются или, возможно, могут быть удалены или перенесены для тестирования, см. Gradle Java Plugin
- некоторые зависимости имеют много транзитивных зависимостей (отображение с использованием
gradle dependencies
), проверьте ненужные и исключите их, если они не используются, см. Gradle Зависимость Управление. Обязательно выполните интеграционные тесты перед окончательным применением, некоторые транзитивные зависимости недостаточно документированы, но могут быть важны!
Ответ 3
С Docker 17.05+ вы можете использовать многоступенчатые сборки.
"С многоступенчатыми сборками вы используете несколько инструкций FROM в вашем файле Docker. Каждая инструкция FROM может использовать другую базу, и каждый из них начинает новый этап сборки. Вы можете выборочно копировать артефакты с одного этапа на другой, оставляя за собой все, чего вы не хотите в конечном изображении".
Итак, ваш файл Dockerfile может выглядеть так:
#
# first stage (build)
#
FROM openjdk:8 as build
ADD . /usr/share/app-name-tmp
WORKDIR /usr/share/app-name-tmp
RUN ./gradlew build && \
mv ./build/libs/app-name*.jar /usr/share/app-name/app-name.jar
#
# second stage. use alpine to reduce the image size
#
FROM openjdk:8-jre-alpine
WORKDIR /usr/share/app-name
COPY --from=build /usr/share/app-name/app-name.jar .
EXPOSE 8080
RUN chmod +x ./docker-entry.sh
ENTRYPOINT [ "./docker-entry.sh" ]
Таким образом, вы сохраняете только банку, а все ненужные файлы не включаются в окончательное изображение.
Ответ 4
Кажется, что ваше изображение происходит от
FROM openjdk:8
поэтому из
https://github.com/docker-library/openjdk/blob/e6e9cf8b21516ba764189916d35be57486203c95/8-jdk/Dockerfile
и фактически Debian
FROM buildpack-deps:jessie-scm
вам следует попробовать использовать альпийскую базу
https://github.com/docker-library/openjdk/blob/9a0822673dffd3e5ba66f18a8547aa60faed6d08/8-jdk/alpine/Dockerfile
Я думаю, ваше изображение будет не менее половины размера
Ответ 5
Это контейнер, который вы развертываете для производства? Если это так, не используйте его для реальной сборки. Сделайте сборку (и тестирование) в другом месте и как только она будет благословлена, скопируйте только JAR в контейнер для производства Docker.
Ответ 6
Для OpenJDK-12
Мое приложение написано на Kotlin вместе с весенней загрузкой и Maven.
У меня была такая же проблема с openJDK-12, а размер OracleOpenJDK-12 составляет 470 МБ.
Я хотел уменьшить размер своего контейнера, поэтому я выбрал accepttopenjdk/openjdk12: x86_64-alpine-jre-12.33 и получил 189 МБ, как показано ниже.
FROM adoptopenjdk/openjdk12:x86_64-alpine-jre-12.33
RUN mkdir /app
COPY ./target/application-SNAPSHOT.jar /app/application-SNAPSHOT.jar
WORKDIR /app
CMD ["java", "-jar", "application-SNAPSHOT.jar"]
Мой окончательный размер контейнера составляет 189 МБ (размер файла приложения Jar 34 МБ + 155 МБ базового размера изображения).