Я хотел выяснить, насколько можно уменьшить размер образа Docker с помощью простого приложения Spring Boot.
Попытка с Alpine
Поэтому давайте воспользуемся базовым образом с Alpine Linux .
Файл Dockerfile:
# use Alpine Linux for build stage FROM alpine:3.10.1 as build # install build dependencies RUN apk --no-cache add openjdk11 RUN apk --no-cache add maven ...
Это позволит получить легкий образ Alpine Linux (~6MB) из Dockerhub и установить OpenJDK 11 и Maven в качестве зависимостей для сборки. После этого мы можем собрать приложение Spring Boot:
... # fetch maven dependencies WORKDIR /build COPY pom.xml pom.xml RUN mvn dependency:go-offline # build COPY src src RUN mvn clean package ...
Как вы можете видеть, мы делаем это в два этапа. Сначала мы загружаем все зависимости, а затем компилируем код. Если мы построим изображение снова без изменений в pom.xml файл результат будет взят из кэша. Это сэкономит некоторое время, так как код меняется гораздо чаще, чем зависимости.
Наконец, мы создадим второй образ Docker, который будет содержать только необходимые части для выполнения. Docker называет эту функцию “многоступенчатой”, где первые этапы являются предварительными этапами сборки, а последний этап приводит к созданию образа выполнения:
... # prepare a fresh Alpine Linux with JDK FROM alpine:3.10.1 RUN apk --no-cache add openjdk11 # get result from build stage COPY --from=build /build/target/*.jar /app.jar VOLUME /tmp EXPOSE 8080 CMD /usr/lib/jvm/default-jvm/bin/java -jar /app.jar
Мы используем аргумент --form=build , чтобы скопировать результат сборки с первого этапа. Все остальные части будут удалены: Maven, байтовый код Java, зависимости,…
Насколько велик результат?
$ docker build -t lazy . && docker image ls | grep lazy ... lazy latest 5f63318a3a0b 11 seconds ago 288MB
Я подумал, что 288 МБ – это слишком много. Я вспомнил, что в Java 9 стандартная библиотека была разделена на модули. Существует инструмент link для создания JDK только с необходимыми модулями. Мы добавляем выполнение ссылки непосредственно перед вызовами mvn :
...
# build JDK with less modules
RUN /usr/lib/jvm/default-jvm/bin/jlink \
--compress=2 \
--module-path /usr/lib/jvm/default-jvm/jmods \
--add-modules java.base,java.logging,java.xml,jdk.unsupported, \
java.sql,java.naming,java.desktop,java.management, \
java.security.jgss,java.instrument \
--output /jdk-minimal
# fetch maven dependencies
...
Кроме того, мы должны скопировать минимальный JDK на этапе выполнения:
# prepare a fresh Alpine Linux with JDK FROM alpine:3.10.1 # get result from build stage COPY --from=build /jdk-minimal /opt/jdk/ COPY --from=build /build/target/*.jar /app.jar VOLUME /tmp EXPOSE 8080 CMD /opt/jdk/bin/java -jar /app.jar
Уменьшает ли это размер?
$ docker build -t lazy . && docker image ls | grep lazy,[object Object], ...,[object Object], lazy latest fbdc64bb6826 20 seconds ago 79.5MB,[object Object],
Мы удалили ~200 МБ ненужных модулей JDK. Мне это нравится. Может быть, размер будет еще меньше, если мы используем Встроенную функцию изображения виртуальной машины Graal ?
Результат: 79,5 МБ
- Файл JAR размером 16 МБ
- 55 МБ OpenJDK 11 “связанный”
- 8,5МБ изображения Alpine Linux + Docker
Исходный код: https://gist.github.com/gofabian/8a0f951bc1edc88b918ce1145ccfbb03
Оригинал: “https://dev.to/gofabian/dockerize-spring-boot-80mb-58m9”