Как уменьшить размер изображения докеры 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 МБ базового размера изображения).