Поиск по сайту:

Как использовать несколько контекстов сборки Docker для оптимизации сборки образа


Концепция Docker о «контексте сборки» — одна из его наиболее ограничительных и неправильно понятых характеристик. Контекст сборки определяет локальные файлы и папки, на которые вы можете ссылаться в своем Dockerfile. Контент вне его нельзя использовать, что часто мешает сложным процедурам сборки.

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

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

Назначение контекста сборки

Docker основан на демоне. Процесс, который запускает ваши сборки образа, не зависит от процесса CLI, который выдает команду. Демон может находиться на удаленном хосте, который не может напрямую обращаться к файловой системе вашего компьютера.

Контекст сборки относится к файлам, которые передаются демону Docker при выполнении сборки. Вот почему ваш Dockerfile может ссылаться только на содержимое в контексте.

Обычно docker build запускается с . в качестве аргумента, что делает ваш рабочий каталог контекстом сборки:

docker build -t my-website:latest .

Это разрешает ссылки на любой путь внутри вашего рабочего каталога:

FROM httpd:latest
COPY index.html /var/www/html/index.html

Вы не можете скопировать что-либо выше рабочего каталога в вашей файловой системе:

FROM httpd:latest
COPY index.html /var/www/html/index.html
COPY ../company-css/company.css /var/www/html/company.css

Каждый файл, который вам нужен в образе контейнера, должен существовать в одном каталоге, который вы можете использовать в качестве контекста сборки. Это может быть проблематично в ситуациях, подобных показанной выше, когда вы хотите получить зависимости из источников, которых нет в дереве вашего проекта.

Использование нескольких контекстов сборки

Несколько контекстов сборки теперь поддерживаются в BuildKit версии 0.8 и новее, когда вы соглашаетесь на синтаксис Dockerfile версии 1.4. Эти выпуски поставляются с интерфейсом командной строки Docker, начиная с версии 20.10.13. Вы сможете использовать их сегодня, если используете последнюю версию Docker.

Вы должны создать свой образ с помощью BuildKit, чтобы использовать несколько контекстов. Они не поддерживаются устаревшим билдером. Используйте команду docker buildx build вместо простой docker build:

$ docker buildx build -t my-website:latest .

Теперь вы можете использовать флаг --build-context для определения нескольких именованных контекстов сборки:

$ docker buildx build -t my-website:latest . \
    --build-context company-css=../company-css \
    --build-context company-js=../company-js \

Настройте свой файл Docker, чтобы он ссылался на контент из следующих контекстов:

#syntax=docker/dockerfile:1.4
FROM httpd:latest
COPY index.html /var/www/html/index.html
COPY --from=company-css /company.css /var/www/html/company.css
COPY --from=company-js /company.js /var/www/html/company.js

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

Объявление синтаксиса Dockerfile v1.4 требуется для включения поддержки этой функции. Затем вы можете использовать параметр --from с инструкциями ADD и COPY для извлечения файлов из именованных контекстов сборки, аналогично ссылке на ресурс в более ранний этап сборки.

Приоритетный заказ

Несколько контекстов сборки изменяют порядок разрешения ресурсов для флага --from. Теперь Docker будет сопоставлять предоставленный вами ключ (--from=key), используя следующую процедуру:

  • Ищите именованный контекст сборки, установленный с флагом --build-context.
  • Ищите более раннюю стадию сборки, созданную с помощью FROM my-image:latest AS stage-name.
  • Создайте новую встроенную стадию сборки, используя указанный ключ в качестве изображения стадии.

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

Рассмотрим этот пример:

#syntax=docker/dockerfile:1.4
FROM my-org/company-scss:latest AS css
RUN sass company.scss company.css

FROM httpd:latest
COPY index.html /var/www/html/index.html
COPY --from=css /company.css /var/www/html/company.css

Этот образ Docker извлекает некоторые удаленные ресурсы из другого общего образа Docker. Это может создать трудности при тестировании вашего проекта — в зависимости может быть ошибка, которую вы хотите быстро исправить.

Именованные контексты сборки позволяют переопределить имя стадии css, чтобы вместо этого предоставить локальный файл:

$ docker buildx build -t my-website:latest . --build-context css=css/

Это скопирует файл css/company.css вашего рабочего каталога в окончательный образ вместо версии, предоставляемой зависимостью my-org/company-scss:latest.

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

$ docker buildx build -t my-website:latest . --build-context my-org/company-scss:latest=css/

Удаленные URL-адреса

Именованные контексты сборки поддерживают все источники, которые docker build уже приняли:

  • --build-context my-context=../local/path – путь в вашей файловой системе.
  • --build-context my-context=https://github.com/user/repo.git — удаленный репозиторий Git.
  • --build-context my-context=https://example.com/data.tar – удаленный архив, предоставленный HTTP-сервером.
  • --build-context my-context=docker-image://busybox:latest – содержимое другого образа Docker.

Удаленные источники еще больше упрощают переопределение зависимостей. Вы можете напрямую указать на разветвленный репозиторий Git или другой тег образа Docker, оставив свой Dockerfile без изменений.

Подключение файлов из контекста сборки

Именованные контексты сборки также работают с инструкциями RUN. Вы можете использовать --mount=from для запуска исполняемого файла из другого контекста сборки.

#syntax=docker/dockerfile:1.4
RUN --mount=from=name-of-build-context demo-executable

Это монтирует файл, не копируя его в текущий слой, что помогает повысить производительность. demo-executable не будет существовать в финальном образе.

Точное восстановление изображений

Другой вариант использования именованных контекстов сборки касается перестроения образов в будущем. Файлы Docker с такими инструкциями, как FROM alpine:3.15, воспроизводятся не полностью. Теги изображений изменяемы, поэтому alpine:3.15 может содержать другой контент в будущем после выпуска нового патча. Это означает, что восстановленные изображения не гарантируют создание тех же слоев, что и исходные версии.

Вы можете решить эту проблему, проверив метаданные первой сборки, чтобы обнаружить точное используемое базовое изображение:

$ docker buildx imagetools inspect --format '{{json .BuildInfo}}' my-image:latest
{
    ...
    "sources": [
        {
            "type": "docker-image",
            "ref": "docker.io/library/alpine:3.15",
            "pin": "sha256:4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454"
        }
    ]
    ...
}

Теперь вы можете определить именованный контекст сборки с именем alpine:3.15, который указывает на точную версию, которая использовалась ранее:

$ docker buildx build -t my-image:latest . --build-context alpine:3.15=docker-image://alpine3.15@4edbd2beb5f78b1014028f4fbb99f3237d9561100b6881aabbf5acce2c4f9454

Это упрощает создание точной перестройки ранее созданного образа без изменения его Dockerfile.

Заключение

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

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

Вы можете начать работу с несколькими контекстами сборки уже сегодня, обновив Docker до версии 20.10.13 или новее и используя docker buildx для создания образов. Также доступны отдельные дистрибутивы BuildKit, если вы не хотите устанавливать весь Docker CLI.