Улучшение кеширования Docker

Эта статья является второй частью нашего исследования кеширования Docker - если вы еще этого не сделали, ознакомьтесь с первой частью, где мы представили слои Docker и механизм кэширования. Теперь давайте посмотрим на Docker Compose и некоторые проблемы при одновременном использовании Docker и Docker Compose.

Docker Compose - отличный инструмент для разработчиков

Мы использовали команды docker build и docker run для локальной сборки / запуска нашего приложения из образа Docker. Если бы у нас был проект с несколькими компонентами (например, клиентом и сервером), создание и запуск каждого из них может быстро стать громоздким и неэффективным. К счастью, есть Docker Compose - инструмент командной строки, предназначенный для запуска приложений с несколькими контейнерами.

Docker Compose полностью отделен от Docker, но внутренне использует движок Docker для управления построением и запуском нескольких контейнеров. Обзор Docker Compose и инструкции по установке можно найти на официальном сайте.

Хотя каждый компонент по-прежнему будет иметь свой собственный файл Dockerfile, описывающий, как должен быть построен образ, теперь у нас также есть файл docker-compose.yml - дескриптор YAML всех контейнеров, которые необходимо запускать вместе, и его свойства времени выполнения.

Примечание. Вы можете получить более подробную информацию о том, как использовать Docker Compose во время разработки, здесь.

Давайте посмотрим, как docker-compose.yml будет выглядеть в нашем примере (который довольно прост, поскольку у нас есть только один компонент):

Мы просто определяем службу с именем hello-world-react-docker, устанавливаем контекст сборки в каталог, содержащий Dockerfile, и открываем соответствующие порты - как мы это делали при использовании docker run для запуска контейнера.

Примечание. stdin_open: true - это обходной путь для открытой ошибки в сценариях реакции 3.4.1 на момент написания, когда команда React npm start завершается с кодом состояния 0, как только сервер разработки запускается.

Теперь запуск docker-compose up создаст и соответствующие изображения, и запустит их, поэтому, войдя в браузер, вы снова увидите знакомую домашнюю страницу React. Ура, мы запускаем приложение React из образа Docker с помощью Docker Compose!

Проблема: совместное использование кеша между сборками Docker и Docker Compose

Итак, у нас есть образец приложения - мы можем упаковать его как образ Docker и запускать где угодно. Мы можем использовать стандартные команды Docker (build и run) или использовать более удобные для разработчиков docker-compose для создания и тестирования нашего приложения локально.

Однако вы, возможно, заметили небольшую проблему - хотя мы сначала создали образ Docker с помощью команды docker build (и кэшировали все соответствующие слои), сборка с использованием docker-compose привела к перестроению всего образа (поэтому нам пришлось ждать npm install закончить на несколько минут). Когда мы запускали последующие сборки с помощью Docker Compose, кеширование работало так, как ожидалось, с быстрым циклом сборки.

Посмотрите на вывод ниже - хотя образ был ранее собран с помощью Docker, сборка с помощью Docker Compose не использует кэшированный слой и перестраивает все (как мы видим, была запущена медленная команда npm install).

Таким образом, Docker и Docker Compose по отдельности ведут себя должным образом, повторно используя кэшированные слои из предыдущей сборки. Но при использовании обоих инструментов и переключении между ними кеширование, похоже, не работает.

Почему это проблема? Что ж, давайте представим, что у вас есть проект, содержащий несколько контейнерных компонентов, создание которых с нуля требует времени. Вы работаете над одним компонентом, но другие разработчики в вашей команде постоянно вносят изменения в другие. Каждый коммит создается на CI-сервере, поэтому вы можете его скачать.

Это почему?

Что ж, причина скрыта глубоко в базе кода Docker Compose. Docker Compose использует клиентскую библиотеку Docker Python для взаимодействия с движком Docker - в то время как команды Docker для этого изначально - различные реализации приводят к различным идентификаторам изображений для образов, созданных с помощью любого инструмента, что оказывает описанный выше эффект на кэшированный слой.

Проблема сообщается и обсуждается в нескольких тикетах в проектах Docker и Docker Compose:

Решение: введите BuildKit

Хотя нет соглашения о том, как исправить это в текущей версии движка Docker, существует простое решение, использующее все еще экспериментальный (но вскоре массовый) движок Docker BuildKit.

BuildKit предлагает долгожданную новую архитектуру и рефакторинг движка Docker, что должно привести к множеству улучшений в производительности, управлении хранилищем и безопасности, включая добавление согласованности кеша между сборками образов Docker и Docker Compose.

Мы ожидаем, что BuildKit станет по умолчанию в следующих версиях Docker, но пока, хотя он все еще экспериментальный, его можно включить, просто установив переменную среды DOCKER_BUILDKIT=1.

Примечание. На момент написания BuildKit доступен только для контейнеров Linux.

Давайте перестроим наше приложение с помощью команды docker build:

Вы увидите гораздо более простой встроенный вывод процесса сборки, характерный для BuildKit, вместе с таймингами для каждого слоя - как и раньше, команда npm install занимает больше всего времени. Вы заметите, что весь образ создается с нуля - BuildKit имеет другую стратегию хранения слоев, поэтому слои, созданные с помощью устаревших механизмов, не могут быть повторно использованы в качестве кеша.

Повторение команды приведет к гораздо более быстрой сборке, поскольку все слои будут кэшироваться и повторно использоваться без перестройки. Вы можете увидеть результат CACHED перед этапом сборки, на котором использовалось кеширование слоев, в приведенном ниже фрагменте:

А что насчет Docker Compose? Ранее мы видели, что Docker Compose не использовал повторно кэшированные слои, созданные с помощью команды docker build - давайте посмотрим, как это изменилось.

Чтобы включить BuildKit для Docker Compose, ему нужна дополнительная переменная среды COMPOSE_DOCKER_CLI_BUILD=1. Давайте перестроим приложение с помощью Docker Compose:

У-у-у! Сборка была молниеносной, так как все слои были повторно использованы, хотя это была первая сборка Docker Compose образа. Слои, созданные с помощью команды docker build, были повторно использованы из кеша, как видно по тегу CACHE в выходных данных выше.

В этой статье я попытался пролить свет на кеширование Docker и проблемы, связанные с использованием инструментов Docker и Docker Compose во время разработки. Помните, какой бы движок или инструмент вы не использовали, ключевым моментом является структурирование дескрипторов Dockerfile таким образом, чтобы они учитывали кеширование слоев - убедитесь, что вы копируете / запускаете наиболее часто изменяемые файлы или команды в нижней части Dockerfile, используя несколько COPY, ADD , и RUN команд в зависимости от жизненного цикла компонентов, на которые имеется ссылка!