Перейти к содержанию

Контекст сборки и .dockerignore

Dockerfile предыдущего урока копирует содержимое папки api целиком. Пока в ней только учебные файлы, результат понятен. Но в настоящем проекте рядом быстро появляются node_modules, журналы, локальная конфигурация и служебные каталоги. В этом уроке ограничим контекст сборки через .dockerignore и разберём, почему его состав влияет не только на размер image.

Результат — полный список исключений для API и ожидаемый состав входа сборки. Файлы приложения сохранятся: манифест, lockfile, сервер и каталог. Команды из урока читатель выполняет самостоятельно; при подготовке серии сборка и просмотр контейнера не проводились.

Контекст — отдельная граница доступа

Контекст сборки определяет материалы, доступные обычному COPY. В нашей команде им является ./api. Это удобно: frontend, Markdown-статьи и прочие папки репозитория не попадают в набор входных файлов API случайно. Однако само по себе узкое имя папки ещё не гарантирует, что внутри нет ненужного содержимого.

Файл .dockerignore исключает совпавшие пути из контекста, который поступает builder. Его значение шире, чем подсказка одной инструкции COPY. Ненужные файлы не должны передаваться как обычные входные материалы сборки. Эта граница особенно понятна при удалённом builder: локальная папка и место выполнения сборки могут находиться на разных машинах. Правила описаны в документации о контексте Docker.

У нас .dockerignore лежит внутри api, потому что эта папка выбрана контекстом. Файл с тем же именем только в корне professorweb-container-lab не следует считать автоматически действующим для ./api. При другом контексте и специальных Dockerfile-specific правилах условия меняются; начинающему полезнее сначала научиться явно связывать один context с одним набором исключений.

Создадим полный файл исключений

Добавьте api/.dockerignore. Ниже полный файл, используемый и в следующих состояниях API.

node_modules
.git
.env
.env.*
*.log
npm-debug.log*
private
dist

node_modules исключён, потому что зависимости образа должны устанавливаться по его манифесту и lockfile. Папка с ноутбука может содержать бинарные модули другой платформы, лишние инструменты или остатки предыдущей установки. Даже у нашего API без зависимостей стоит зафиксировать это правило: будущая папка node_modules не должна менять смысл COPY неожиданно.

Файлы .env и .env.* относятся к локальной конфигурации. Такой список требует осознанного решения: он исключит и .env.example, если пример появится. В этой серии такого файла нет; позднее при необходимости можно разрешить конкретный безопасный образец через правило исключения из исключений. Не создавайте подобное разрешение для настоящего секрета под видом удобного шаблона.

Журналы нужны при диагностике работающего процесса, но не являются исходниками API. Папка private служит явной границей для возможных местных материалов, а dist не используется нашим сервером. При добавлении другой системы сборки эту строку надо пересмотреть: если программа должна копировать готовый dist, запрещать его без альтернативного build-stage нельзя.

Что оставим доступным

Путь внутри api В контексте нашего API Причина
package.json Да Описывает пакет и команды
package-lock.json Да Требуется npm ci
src/server.mjs Да Запускаемый HTTP-процесс
data/catalog.json Да Начальный fixture каталога
node_modules Нет Не переносим локальную установку
.env и журналы Нет Не являются поставляемыми материалами

Обратите внимание на lockfile. Иногда его исключают рядом с node_modules, считая оба файла результатом установки. Это разные роли: папка модулей зависит от платформы и процесса установки, а lockfile задаёт разрешённое дерево зависимостей. Если запретить package-lock.json, следующая инструкция npm ci потеряет обязательный вход и сборка должна завершиться ошибкой.

Каталог data пока входит в image как начальные данные. Позже named volume получит отдельную жизнь; это не повод заранее исключать исходный fixture. Подключённое хранилище и файлы образа различаются во времени: новый пустой volume может инициализироваться подготовленным содержимым, существующий volume сохранит собственные значения.

Пересоберём ожидаемый образ

После сохранения файла читатель может построить новое учебное состояние. Команда относится к корню professorweb-container-lab:

docker build --tag professorweb-container-lab-api:lab-05 ./api

Здесь появился новый тег, чтобы отличать состояние с контролируемым контекстом. Но наличие тега lab-05 само по себе не доказывает, что исключения сработали. Чтобы исследовать содержимое, можно запустить одноразовую команду вместо штатного API:

docker run --rm professorweb-container-lab-api:lab-05 node -e "console.log(require('node:fs').readdirSync('/app'))"

У этого диагностического запуска нет опубликованного порта и не стартует server.mjs: аргументы после имени image заменяют CMD. Ожидается наличие src, data и манифестов. Конкретный порядок имён и вспомогательные файлы npm не нужно принимать за неизменную строку. Если читатель добавил безопасный демонстрационный файл .env до сборки, он не должен оказаться в перечислении.

Не помещайте настоящий пароль в эксперимент даже ради проверки исключения. Достаточно фиктивного значения или имени файла. Если секрет когда-то попал в собранный image и его затем распространили, добавление .dockerignore к новой сборке не делает прежние артефакты безопасными автоматически. Понадобится отдельное решение для секрета и старых артефактов.

Разберём ошибки правил

Наиболее простая ошибка — правильный файл не в том контексте. Проверяйте последнюю часть docker build, а не только расположение Dockerfile в редакторе. Вторая ошибка — чрезмерно широкий шаблон. Например, запрещение всех JSON удалит и каталог, и манифесты. Тогда последующие ошибки COPY или npm являются следствием правила, а не отсутствия нужного приложения.

Правила с восклицательным знаком позволяют вернуть конкретный путь, а порядок правил имеет значение. Не усложняйте файл без потребности. Для маленького API понятный перечень лучше длинной копии чужого ignore, где трудно объяснить каждую строку. Новый тип файла следует проверять против требований проекта: нужны ли fixtures, схемы, сертификаты разработки и результат сборки внутри image.

.dockerignore не меняет файлы компьютера. Он не удаляет node_modules, не очищает logs и не защищает их от чтения другими программами. Его действие связано с конкретной сборкой. Так же он не отключает bind mount при запуске: подключённая папка в будущем сможет показать файлы, которые не были скопированы в image.

Сохраним понятное состояние

В архиве серии после этого урока API имеет Dockerfile и .dockerignore, но ещё не настроенные внешние данные. В STATE отмечено not_run, а проверка содержимого является ожидаемым упражнением. Не сравниваем размеры и скорость с чужими результатами: измерений в нашем окружении нет.

Теперь можно перейти к запуску API. Когда контейнер получит неожиданный файл, у нас уже будет два точных вопроса: входил ли он в build context и не пришёл ли он позже через mount. Такое разделение пригодится и при работе с кешем, где изменение входных материалов определяет, какие операции сборки могут быть использованы повторно.

Оглавление серии