Оптимальный Dockerfile: ускорение сборки и уменьшение образа

Медленная сборка и разросшиеся образы тормозят выпуск обновлений и усложняют развёртывание. Мы пересобираем образы под ключ: убираем лишние слои и лишние зависимости, сокращаем время сборки в разы и уменьшаем размер образа вдвое. Оставьте заявку — обсудим задачу и вернёмся с оценкой и планом работ.
Часто задаваемые вопросы

Dockerfile оптимизация: почему образы растут и сборка тормозит

Размер образа и время сборки почти никогда не упираются в одну строку Dockerfile — их определяет модель слоёв. Каждая инструкция RUN, COPY или ADD создаёт отдельный слой, и удаление файла в следующем слое его не уменьшает: данные остаются в предыдущем. Поэтому apt-get update, установка пакетов и rm -rf /var/lib/apt/lists/* в разных слоях дают образ тяжелее ожидаемого, хотя внутри контейнера лишних файлов не видно.

Вторая причина — контекст сборки. Сборщик получает каталог целиком, вместе с историей Git, локальными зависимостями и артефактами прошлых сборок: передача контекста занимает больше времени, чем сама сборка, а случайный COPY переносит мусор в финальный образ. Порядок инструкций решает не меньше: если зависимости ставятся после COPY всего проекта, любая правка в исходниках обесценивает слой с ними, и пакеты устанавливаются заново при каждом коммите.

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

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

Что даёт аккуратный образ: скорость сборки и предсказуемость

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

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

Воспроизводимость сборки — отдельный эффект. Docker даёт одинаковый результат только при одинаковых входных данных: базовый образ закреплён по неизменяемому идентификатору, а не по плавающему тегу, зависимости зафиксированы, сборка идёт в единой среде, а не на машине разработчика. Тогда уходит класс отказов «в тесте собралось, в бою нет»: артефакт собирается один раз и продвигается между средами без пересборки.

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

Dockerfile оптимизация: сравнение подходов к сборке образа

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

Что сравниваем

Вариант Когда подходит Ограничения
Одноэтапная сборка Простой сервис, скрипты, статика; набор зависимостей совпадает у сборки и у среды исполнения Инструменты сборки остаются в образе: он растёт, вместе с ним — время выкатки и число уязвимых пакетов
Многоэтапная сборка Код компилируется или собирается в один артефакт, а в среде исполнения нужен только результат Сложнее читать и поддерживать; при неаккуратном копировании в финальный слой уезжает лишнее
Тонкий финальный слой Нужен минимальный базовый образ и предсказуемый размер Каждую библиотеку добавляем руками; пропущенная зависимость вылезает только при запуске
Сборка из готового дистрибутива Есть проверенный артефакт от вендора или отдельного этапа сборки Образ не воспроизводится из исходников: шаг сборки живёт отдельно и требует своей поддержки

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

Этапы пересборки: от замера слоёв до передачи регламента

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

  1. Аудит сборки. Разбираем текущий файл сборки и журнал: какие слои перестраиваются при правке одной строки кода, где теряется кеш, сколько времени занимает полная и повторная сборка, сколько весит образ в реестре.
  2. Схема слоёв. Раскладываем образ по частоте изменений: системные пакеты, зависимости проекта, исходный код, конфигурация. Редкое — вниз, изменяемое — вверх, чтобы рабочие слои переиспользовались, а не собирались заново.
  3. Порядок инструкций. Переставляем шаги под эту схему, копируем только нужные файлы, а не весь каталог. Сборочные инструменты и временные зависимости остаются на отдельном этапе и в итоговый образ не попадают.
  4. Настройка сборки. Правила исключения лишнего, минимальный базовый образ, закреплённые зависимости, единая точка их установки — чтобы образ не рос от сборки к сборке.
  5. Проверка образа. Сверяем размер, число слоёв, отсутствие отладочных утилит и служебных файлов, время сборки на чистом кеше и повторной. Дополнительно прогоняем проверку на уязвимые пакеты и запуск сервиса в изолированной среде.
  6. Проверка повторяемости. Две сборки подряд на разных машинах дают одинаковый результат, без ручных шагов и локальных зависимостей — иначе выигрыш не закрепится.
  7. Передача регламента. Отдаём файл сборки, правила исключений, описание слоёв и критериев приёмки, короткую инструкцию «как менять и что проверять» — чтобы команда поддерживала результат сама.

Что вы получаете на руки: конфигурации, схемы, регламенты

Работа заканчивается не советом «объедините команды в одну строку», а набором файлов и правил, по которым сборка и образ живут дальше без нас. Ниже — что именно передаём и зачем каждый артефакт нужен в ежедневной работе команды.

  • Конфигурация сборки — рабочий файл с выстроенными слоями и кэшированием зависимостей: правка кода больше не тянет повторную установку пакетов, пересборка укладывается в единицы минут.
  • Описание слоёв — карта, где какой слой лежит и что сбрасывает кэш выше него; инженер сразу видит, почему изменение одного файла пересобирает половину образа.
  • Правила работы с базовыми образами — какой образ считаем эталонным, откуда берём обновления, как проверяем его перед сменой тега и что фиксируем жёстко, а что обновляем контролируемо.
  • Правила работы с зависимостями и артефактами — порядок копирования файлов, разделение этапов сборки и запуска, чтобы в итоговый образ не попадали компиляторы и временные файлы.
  • Документация сборки — команды локального запуска, переменные окружения, точки входа и типовые ошибки; новый инженер собирает образ сам, без устных подсказок.
  • Регламент обновления — периодичность пересборки базовых слоёв, порядок проверки уязвимостей и правила отката на предыдущий образ.
  • Схема проверок в конвейере — какие шаги блокируют попадание образа в реестр, а какие только предупреждают.

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

Кейс: сборка ускорилась в разы, образ стал вдвое меньше

Проект — внутренний сервис с нативными расширениями и заметным деревом зависимостей. Сборка на каждый коммит занимала 18 минут, а готовый образ в 2,5 раза превышал объём самого кода: в него попадали компиляторы, кеш пакетного менеджера и тестовые данные. Каждый десятый запуск падал из-за расхождения версий системных библиотек между сборочным и рабочим окружением, и на разбор причины уходило до получаса.

Что изменили

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

Что стало

Сборка при правке кода — 4 минуты вместо 18, при изменении только конфигурации — меньше минуты. Образ стал в 2,6 раза меньше, число слоёв сократилось с 30 до 11, поэтому выгрузка на узлы и старт контейнера ускорились во столько же. Доля падений запуска снизилась с 12% до 1%: класс отказов «несовпадение библиотек» ушёл целиком, остались только ошибки конфигурации самого сервиса.

Чем отличается многоэтапная сборка от одноэтапной на практике?

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

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

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

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

Вопросы, которые задают перед пересборкой образов

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

Нужно ли переписывать сборку целиком?

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

Как проверить, что пересборка ничего не сломала?

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

Что делать со старой конфигурацией?

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

Кто отвечает за сборку после передачи?

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

Обсудим задачу: что прислать для оценки и плана работ

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

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

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

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    815
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139