Понимание контекста сборки Docker (почему следует использовать Dockerignore)
Контекст сборки Docker относится к файлам и каталогам, которые будут доступны для ядра Docker при запуске docker build
. Все, что не включено в контекст сборки, не будет доступно для команд в вашем Dockerfile
.
Вы должны проверять использование docker build
, чтобы контексты сборки были небольшими. Случайное включение ненужных файлов может привести к чрезмерно большому контексту сборки, что приведет к увеличению продолжительности сборки.
Что такое контекст сборки?
Вот простая команда docker build
:
docker build . -t my-image:latest
Это создает образ Docker, используя Dockerfile
, найденный в вашем рабочем каталоге. Полученное изображение будет помечено как my-image:latest
, хотя эта деталь не важна для этого руководства.
Внутри вашего Dockerfile
вы, скорее всего, будете использовать COPY
для добавления файлов и папок в ваш образ:
FROM httpd:latest
COPY index.html /usr/local/apache2/htdocs/index.html
COPY css/ /usr/local/apache2/htdocs/css/
В этом примере копируется файл index.html
и каталог css
в ваш контейнер. На первый взгляд кажется, что оператор COPY
просто ссылается на путь, разрешенный относительно вашего рабочего каталога.
Это не совсем так. COPY
может получить доступ только к ресурсам, доступным в контексте сборки. В этом примере контекстом сборки является рабочий каталог, поэтому файлы и папки в нем доступны. По умолчанию Docker использует содержимое каталога, переданного в docker build
, в качестве контекста сборки.
Почему используется контекст сборки?
Контекст сборки важен, поскольку Docker CLI и Docker Engine могут работать на разных компьютерах. Когда вы запускаете docker build
, интерфейс командной строки отправляет файлы и папки для сборки в механизм Docker. Этот набор файлов и папок становится контекстом сборки.
Кроме того, не каждый контекст сборки так же прост, как повторное использование вашего рабочего каталога. Docker также поддерживает URL-адреса репозитория Git в качестве пути, указанного для docker build
. В этом случае контекст сборки становится содержимым указанного репозитория.
Поведение контекста сборки по умолчанию «включить все» подходит для многих небольших репозиториев. Проблемы становятся очевидными, когда вы добавляете в свой рабочий каталог файлы, которые не используются вашим Dockerfile
. Такие ресурсы, как готовые двоичные файлы, файлы документации и библиотеки зависимостей, будут включены в контекст сборки, даже если они избыточны.
Включение слишком большого количества ресурсов в контекст сборки может снизить производительность. Вы напрасно копируете файлы, которые никогда не будут использоваться. Замедление будет особенно заметно, если вы подключены к удаленному демону Docker или используете медленный механический жесткий диск. Вы увидите «отправка контекста сборки демону Docker» в вашей оболочке, когда копирование будет завершено.
Docker самостоятельно пытается свести к минимуму избыточное копирование. Серверная часть сборки BuildKit, используемая начиная с Docker 18.09, добавила поддержку добавочной передачи. Это означает, что Docker обычно нужно копировать только файлы, добавленные или измененные с момента вашей последней сборки. Он по-прежнему будет копировать всю партию в первой сборке.
Исключение ресурсов из контекста сборки
Чтобы избавиться от расточительного копирования навсегда, вы должны указать Docker, что он может исключить из контекста сборки. Начнем с создания Dockerfile
:
FROM node:latest
WORKDIR /my-app
COPY package.json package.json
COPY package-lock.json package-lock.json
COPY src/ .
RUN npm install
Этот простой Dockerfile
может использоваться приложением, написанным на Node.js. Программы Node.js используют npm
в качестве менеджера пакетов. Пакеты устанавливаются в папку node_modules
. Когда вы запускаете npm install
локально, во время разработки пакеты будут загружены в папку node_modules
в вашем рабочем каталоге.
Dockerfile
запускает сам npm install
для получения зависимостей. Это гарантирует, что изображение будет полностью автономным. Нет необходимости копировать в папку local node_modules
, так как она не используется Dockerfile
.
Несмотря на это, Docker по-прежнему будет включать папку node_modules
в контекст сборки по умолчанию. Чтобы исключить его, создайте файл .dockerignore
в своем рабочем каталоге. Синтаксис этого файла аналогичен .gitignore
.
node_modules/
Любые пути, перечисленные в .dockerignore
, будут исключены из контекста сборки. Вы должны убедиться, что .dockerignore
постоянно обновляется с учетом изменений в структуре файловой системы вашего проекта. Вы можете существенно сократить время копирования контекста сборки Docker, проверив, что в контексте сборки присутствуют только соответствующие пути (те, которые фактически используются вашим Dockerfile
).
В нашем примере папка node_modules
может содержать тысячи файлов, если в нашем проекте много зависимостей. Копирование их в демон Docker как часть контекста сборки может занять несколько секунд и будет расточительной операцией. Dockerfile
полностью игнорирует их, вместо этого загружая свои собственные зависимости через npm install
.
Другие проблемы с контекстом сборки
Если не использовать .dockerignore
, могут возникнуть и другие проблемы. Dockerfile с этой строкой особенно проблематичен:
COPY . /my-app
Это скопирует все в ваш рабочий каталог. Это может показаться хорошей идеей, пока вы не поймете, что ваша история .git
и все секретные файлы также попадут в ваш контейнер.
Копирование нефильтрованного контекста сборки также препятствует эффективной работе кэширования слоя Docker. Поскольку что-то в вашем рабочем каталоге, вероятно, будет меняться между сборками, Docker потребуется каждый раз запускать инструкцию COPY
. Это создаст новый слой — и новые слои для любых последующих инструкций — даже если интересующие вас активы не изменились.
Сжатие контекста сборки
Вы можете сжать контекст сборки для дальнейшего повышения производительности сборки. Передайте флаг --compress
в docker build
, чтобы применить сжатие gzip. Сжатие происходит до отправки контекста демону Docker.
docker build . -t my-image:latest --compress
Это может повысить производительность в некоторых сценариях. Однако сжатие добавляет свои собственные накладные расходы — теперь вашей системе необходимо сжать контекст, а принимающий демон Docker должен его распаковать. В некоторых случаях использование сжатия может оказаться медленнее, чем копирование исходных файлов. Поэкспериментируйте с каждым из ваших изображений, чтобы оценить, видите ли вы улучшения.
Заключение
Контекст сборки Docker определяет файлы, которые будут доступны для копирования в вашем Dockerfile
. Контекст сборки копируется в демон Docker перед началом сборки.
Контексты сборки по умолчанию включают содержимое каталога или репозитория Git, которое вы передали в docker build
. Вы можете исключить элементы из контекста сборки, создав файл .dockerignore
. Это повышает эффективность за счет уменьшения объема избыточных данных, передаваемых демону Docker.