Слои, кэширование и .dockerignore

Почему образ состоит из слоёв, как кэш ускоряет сборку и зачем порядок инструкций так важен.

Слой — результат одной инструкции Dockerfile; образ — это стопка слоёв, наложенных друг на друга.

Образ — это слои

Каждая инструкция (FROM, COPY, RUN) создаёт новый слой — снимок изменений файловой системы. Образ — это слои, сложенные стопкой. Такая структура позволяет переиспользовать слои между образами и кэшировать их при сборке.

Кэш сборки

При сборке Docker для каждой инструкции проверяет: изменилось ли что-то? Если инструкция и её входные данные не поменялись, Docker берёт готовый слой из кэша вместо повторного выполнения. В выводе сборки такие шаги помечены как CACHED — они мгновенные.

Кэш действует «до первого изменения»: как только один слой пересобран, все слои после него тоже пересобираются заново.

Почему порядок инструкций важен

Из правила «после изменённого слоя пересобирается всё» следует ключевой приём. Сравните два варианта:

# Плохо: код копируется ДО установки зависимостей
FROM node:22-alpine
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "server.js"]

Здесь любое изменение кода меняет слой COPY . ., и тяжёлый RUN npm install запускается заново при каждой сборке.

# Хорошо: сначала зависимости, потом код
FROM node:22-alpine
WORKDIR /app
COPY package.json package-lock.json .
RUN npm install
COPY . .
CMD ["node", "server.js"]

Теперь, пока package.json не изменился, слой с npm install берётся из кэша, и сборка после правки кода занимает секунды. Правило: то, что меняется реже (зависимости), ставьте выше; то, что меняется часто (код), — ниже.

.dockerignore

Файл .dockerignore исключает файлы из контекста сборки — по аналогии с .gitignore. Это уменьшает контекст, ускоряет сборку и не даёт мусору попасть в образ:

node_modules
.git
*.log
.env
Dockerfile
.dockerignore

Особенно важно исключать node_modules и .git: они большие и не нужны внутри образа (зависимости ставятся через RUN npm install). Заодно .dockerignore защищает от случайного попадания секретов вроде .env в образ.

Итог

  • Образ состоит из слоёв; каждая инструкция Dockerfile добавляет слой.
  • Неизменённые слои берутся из кэша, но после первого изменения всё ниже пересобирается.
  • Зависимости копируют и ставят до кода, а .dockerignore убирает лишнее из контекста.
Проверьте себя
1. Почему COPY package.json и npm install стоит размещать до копирования всего кода?
AТак требует синтаксис Dockerfile
BЧтобы слой с установкой зависимостей брался из кэша, пока зависимости не менялись
CЧтобы образ стал меньше по размеру
DИначе npm install не сработает
2. Зачем нужен файл .dockerignore?
AЧтобы скрыть Dockerfile от Docker Hub
BЧтобы исключить файлы из контекста сборки и не тащить лишнее в образ
CЧтобы задать переменные окружения
DЧтобы перечислить устанавливаемые пакеты
3. Что происходит с кэшем после изменения одного слоя?
AНичего, остальные слои всё равно из кэша
BВсе слои, идущие после изменённого, пересобираются заново
CВесь образ удаляется
DКэш отключается навсегда
Поддержать проект