Оптимизация хранения изображений в репозитории Docker с использованием Jib для Spring Boot - PullRequest
0 голосов
/ 18 марта 2020

Помогает ли использование Jib для создания Docker изображений оптимизировать удаленное Docker хранилище хранилища?

Мы используем Spring Boot в Docker с Gradle. В настоящее время мы создаем стандартные толстые загрузочные фляги со всеми зависимостями, упакованными внутри, и затем мы создаем изображение с ним, например так:

FROM gcr.io/distroless/java:11
COPY ./build/libs/*.jar app.jar
CMD ["app.jar"]

Это приводит к большому (250 МБ) новому изображению каждый раз мы строим, даже если очень мало кода действительно изменилось. Это связано с тем, что толстый файл jar содержит как общие зависимости (которые меняются редко), так и наш код. Это неэффективное использование дискового пространства в нашем частном репозитории, и мы хотели бы изменить это.

Для этого идея заключается в следующем:

  • Мы создаем базу изображение, которое содержит только зависимости в / opt / libs, назовем его spring-base:1.0.0 и pu sh для нашего частного Docker реестра.

  • Мы используем это изображение как родительский / base изображения приложения, которое содержит только наш код. Dockerfile выглядит примерно так (непроверено, просто чтобы представить концепцию):

    FROM our-registry/spring-base:1.0.0
    COPY ./build/classes/kotlin/main/* /opt/classes
    COPY ./build/resources/main/* /opt/resources
    ENTRYPOINT ["java", "-cp", "/opt/libs/*:/opt/resources:/opt/classes", "com.example.MainKt"]
    

Предполагается, что эти изображения намного меньше, и большое базовое изображение с зависимостями сохраняется только один раз, экономя много памяти.

Наш коллега изучил Jib и настаивает, что он делает именно это, но после прочтения всей документации и FAQ и немного поиграв с ней, я не уверен , Мы интегрировали его и используем ./gradlew jibDockerBuild, и он, кажется, создает слои для зависимостей, ресурсов и классов, но все еще есть только одно большое изображение. Кажется, Jib фокусируется на ускорении времени сборки (за счет использования Docker кеширования слоев) и воспроизводимых сборок, но я думаю, что когда мы загрузим это изображение в наш репозиторий, ничего не изменится относительно нашего текущего решения - мы все равно сохраним c 'зависимости несколько раз, но теперь у нас будет несколько слоев вместо одного в каждом новом изображении.

Может ли кто-либо с большим количеством Docker и опытом Jib объяснить, дает ли Jib оптимизацию пространства хранения, которую мы ищете?

РЕДАКТИРОВАТЬ: пока я ждал ответа, я поиграл со всем этим и использовал https://github.com/wagoodman/dive, docker system df и docker images для проверки размеров и просмотра изображений и слоев, и кажется, что Jib делает именно то, что нам нужно.

1 Ответ

1 голос
/ 18 марта 2020

Помогает ли использование Jib для создания Docker изображений оптимизировать удаленное Docker хранилище хранилища?

Да. Действительно, это помогает в значительной степени из-за высокой воспроизводимости слоя изображения. Просто используя Dockerfile, вы обычно полностью теряете воспроизводимость для большинства слоев, поскольку временные метки файлов учитываются при проверке идентичности слоев. Например, даже если байты вашего .class вообще не изменились, если файл будет сгенерирован снова, вы потеряете воспроизводимость. Это хуже для банки; может изменяться не только его метка времени, но метаданные jar (например, META-INF/MANIFEST.MF) содержат информацию времени компиляции, включая метку времени, информацию об инструменте сборки, версию JVM и т. д. c. Баночка, созданная на другой машине, будет считаться другой в мире Docker.

Это приводит к большому (250 МБ) новому образу каждый раз, когда мы строим, даже если на самом деле очень мало кода изменилось. Это связано с тем, что толстый файл содержит как общие зависимости (которые меняются редко), так и наш код.

Частично правильно, что размер большой (250 МБ), но не из-за жира баночка. Размер встроенного образа всегда будет 250 МБ, даже если это не толстая банка, и даже если вы выбрали другой слой для разделяемых библиотек. Размер вашего окончательного изображения (250 МБ) всегда будет включать в себя размер базового изображения (gcr.io/distroless/java:11) и размер общих библиотек независимо от того, каким образом с помощью какого инструмента создается изображение.

Однако * Движки 1063 * не дублируют слои, о которых они уже знают при хранении. Аналогичным образом, удаленные реестры также не дублируют слои, которые уже существуют в хранилище. Более того, часто реестры даже хранят ровно одну копию слоя в разных хранилищах. Поэтому, когда вы обновляете только свой код (и, следовательно, ваш jar), только слой, содержащий этот jar, займет новое место для хранения. Docker и Jib будут отправлять только новые слои в удаленные реестры по сети. То есть слои базового изображения для gcr.io/distroless/java:11 не будут отправлены.

Мы создадим базовое изображение, которое содержит только зависимости в / opt / libs, назовем его spring-base:1.0.0 и pu sh в наш частный Docker реестр.

Создание отдельного изображения, содержащего только общие библиотеки, не является чем-то неслыханным, и я видел, как некоторые люди пытались это сделать. Тем не менее, я не думаю, что вы намерены концептуально рассматривать этот специальный базовый образ как независимое, автономное изображение, которое предназначено для совместного использования различными видами изображений в вашей организации. Так что я думаю, что в этой ситуации нетрадиционно, и этот трюк, скорее всего, не нужен, если это всего лишь идея, касающаяся экономии места на диске (и пропускной способности сети). Пожалуйста, продолжайте читать.

Ожидается, что эти изображения намного меньше

Нет. Как я уже объяснил, вы создадите изображение одинакового размера 250 МБ, несмотря ни на что. Он включает в себя размер базового образа, который включает ваши общие библиотеки. При работе docker images ваш локальный движок Docker покажет, что размер изображения составляет 250 МБ. Но, как я уже сказал, это не означает, что ваш Docker движок занимает дополнительно 250 МБ пространства всякий раз, когда вы создаете новый образ.

большое базовое изображение с зависимостями сохраняется только один раз

Да, но это также может быть правдой, когда вы начинаете с FROM gcr.io/distroless/java:11. Не имеет смысла помещать ваши разделяемые библиотеки в другой «базовый образ», если вы можете создать отдельный слой для разделяемых библиотек и поддерживать этот уровень стабильным (то есть воспроизводимым). И Джиб очень хорошо воспроизводит создание такого слоя. Гранулярность битов, сохраняемых в реестрах, представляет собой слои, а не изображения, поэтому на самом деле нет необходимости «отмечать», что слой библиотек находится в некотором «базовом изображении» (если вы создаете свой собственный слой для библиотек). Реестры видят только слои, и понятие «изображение» формируется просто объявлением, что «это изображение состоит из слоя A, слоя B и слоя C вместе с этими метаданными». Изображение даже не имеет понятия базового изображения; это не говорит как «это изображение, помещая слой A поверх этого базового изображения». Поскольку слой B является слоем совместно используемых библиотек, вы оптимизируете его лучше, чем слой с толстыми банками.

, экономя много места для хранения.

Следовательно, это не правда. В конце концов, Docker движки и реестры не хранят один и тот же слой несколько раз без уважительной причины.

Мы интегрировали его и используем ./gradlew jibDockerBuild, и, похоже, он создает слои для зависимостей, ресурсы и классы, но есть все еще только одно большое изображение.

Да. Размер изображения будет 250 МБ. Это будет по-прежнему верно, если вы используете Dockerfile или любые другие инструменты построения изображений. Однако при использовании Jib, если вы измените только файлы приложения .java, Jib отправит только небольшой прикладной уровень (не содержащий общих библиотек или ресурсов) по сети в удаленный реестр при перестройке; он не отправляет целые 250 МБ слоев, потому что Jib сохраняет высокую воспроизводимость. Точно так же, если вы обновляете только свои совместно используемые библиотеки, Jib будет отправлять только слой библиотек, экономя время, пропускную способность и хранилище.

Обратите внимание, однако, из-за ограниченной возможности API механизма Docker, который отсутствует Чтобы Jib мог проверить, хранятся ли определенные слои в Docker движке, Jib должен загружать все 250 МБ слоев при использовании jibDockerBuild. Обычно это не проблема, потому что загрузка выполняется локально, без прохождения через сеть. Но из-за этого ограничения API удивительно, что Jib часто быстрее напрямую помещает образ sh в удаленный реестр, чем в локальный механизм Docker; Jib нужно только отправить слои, которые были изменены. Однако, как я неоднократно подчеркивал, даже если Jib (или любые другие инструменты построения изображений) загружают все 250 МБ слоев в движок Docker, движок будет сохранять только то, что необходимо (то есть, новые слои, которых он никогда не имел). видел - или он так считает). Он не будет дублировать базовое изображение или слои общих библиотек; только новые, разные слои займут память. А с Dockerfile вы, как правило, в конечном итоге создаете «новые слои», даже если они практически не являются новыми из-за плохой воспроизводимости.

...