Раздутый Docker-образ — это медленные сборки, долгие выкладки и лишняя площадь для атак. При этом ужать образ в несколько раз можно базовыми приёмами, без экзотики. Разберём, что даёт основной выигрыш в 2025 году и почему порядок строк в Dockerfile важнее, чем кажется.

Многоэтапная сборка: главный рычаг

Самый мощный приём — многоэтапная сборка (multi-stage build). Идея в том, чтобы разделить сборку и запуск. На одном этапе вы собираете приложение со всеми компиляторами, сборщиками и инструментами отладки. На финальный этап копируете только то, что нужно для запуска, и берёте за основу более лёгкий образ.

  • Используйте полноценный образ для сборки и тестов.
  • Для продакшена берите слим-образ только с рантаймом.
  • В финальный образ переносите лишь готовые артефакты, а не весь хлам сборки.

Результат — образ без компиляторов и dev-зависимостей, который может быть в разы меньше и имеет меньшую площадь атаки.

Порядок строк решает судьбу кэша

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

  1. Сначала установка зависимостей — она меняется реже всего.
  2. Потом копирование кода приложения — оно меняется чаще всего.
  3. В конце настройка рантайма.

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

Кэш-маунты: ускорение без раздувания слоёв

В BuildKit есть кэш-маунты, которые кэшируют отдельные операции, не записывая их в слои образа. Например, кэш npm или apt живёт между сборками, но не утяжеляет итоговый образ. В типичном Node.js-приложении кэширование сокращает время сборки до 70%. Кэш-маунты и параллельная сборка требуют включённого BuildKit — это Docker версии 23.0 и выше или переменная DOCKER_BUILDKIT равная единице.

Выбор базового образа

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

Параллелизм этапов

Многоэтапная сборка не только уменьшает образ, но и ускоряет сам процесс: независимые этапы BuildKit может выполнять параллельно. Если разнести подготовку зависимостей и сборку фронтенда по разным этапам, они посчитаются одновременно, а не одна за другой.

Короткий чек-лист

Чтобы не держать всё в голове, пройдитесь по списку:

  • Разделите Dockerfile на этап сборки и лёгкий этап запуска.
  • Поставьте установку зависимостей до копирования кода.
  • Включите BuildKit и добавьте кэш-маунты для пакетных менеджеров.
  • Возьмите слим-образ для финального этапа.

Большую часть выигрыша дают два приёма: многоэтапная сборка и правильный порядок строк ради кэша. Начните с них — и образы станут меньше, а сборки заметно быстрее, без сложных инструментов и переписывания процесса.