Почему Docker layer caching перестаёт работать: где теряются слои
Docker layer caching опирается на простую механику: каждая инструкция в описании образа создаёт слой, и при следующей сборке движок переиспользует его, если не изменились ни сама инструкция, ни содержимое копируемых файлов, ни родительский слой. Зависимость цепная: одна изменившаяся ступень обнуляет все последующие, и дальше сборка идёт с нуля.
Самая частая причина — порядок инструкций. Если сначала копируется весь проект, а потом ставятся зависимости, любая правка в исходниках меняет слой копирования, инвалидация слоёв каскадом доходит до установки пакетов, и она выполняется заново при каждом коммите. Обратный порядок — сначала перечень зависимостей, потом их установка, и только затем исходный код — оставляет кэш слоёв целым при обычных изменениях.
Второй источник — изменчивые метки и контекст сборки. Метка времени или номер сборки, подставляемые в инструкцию, дают новый отпечаток на каждом запуске. Туда же отсутствующий список исключений: в контекст попадают каталоги сборки, журналы, локальные настройки, и его контрольная сумма меняется даже без правок кода.
Цена — медленная сборка образа. Там, где хватало десятков секунд, уходит несколько минут, и они умножаются на число сборок в день и на число инженеров. Проверки в общих ветках перестают успевать за коммитами, обратная связь приходит с задержкой, а сброс кэша сборки превращается в ручную операцию, которую каждый делает по-своему.
Что даёт переиспользование слоёв: время сборки и доля попаданий
Время сборки и частота отказов
Слои образа Docker кэшируются по содержимому инструкций: пока не менялся файл зависимостей, шаг установки пакетов берётся готовым. На типичном проекте сборка сокращается с 8–12 минут до 2–3, а доля попаданий в кэш держится на уровне 80–90% при правках в коде приложения. Частота отказов сборки падает отдельно: сетевой сбой при скачивании пакетов больше не роняет прогон целиком, повтор не начинается с нуля.
Объём хранилища и предсказуемость выката
Слои переиспользуются между сборками, поэтому в реестр попадает только изменившееся — объём хранилища образов растёт долями, а не кратно числу выкатов. Правила очистки по числу версий на тег не дают накапливаться старым сборкам, при этом откат на предыдущий релиз остаётся возможным. Предсказуемость выката держится на воспроизводимости: одна и та же последовательность слоёв даёт один и тот же образ, и то, что проверили на тестовом контуре, приезжает в прод тем же составом.
Эффект виден не в одной цифре, а в связке: чем выше доля попаданий, тем короче цикл правки и тем меньше поводов перезапускать выкат. Мы измеряем это на каждом проекте — время сборки образа, долю попаданий, число повторных прогонов — чтобы ускорение было управляемым, а не «вроде стало быстрее».
Способы кэширования: локальный кэш, реестр образов, общий том
Кэш слоёв Docker — это не галочка в настройках, а привязка к топологии сборки. Локальный кэш сборки живёт на конкретной машине и даёт максимум на постоянном узле, но обнуляется, как только узел пересоздали. Когда сборки идут на нескольких машинах, вопрос уже не в скорости, а в том, где хранятся слои и кто их делит.
Режим под одну команду
На одном узле и при стабильном наборе зависимостей достаточно локального кэша: он самый быстрый и не требует инфраструктуры. Как только появляются параллельные ветки и несколько исполнителей, мы упираемся в ограничения кэширования — слои не переносятся между машинами, а смена базового образа пересобирает всё дальше по цепочке.
| Вариант | Когда подходит | Ограничения |
|---|---|---|
| Локальный кэш сборки | один постоянный узел, частые пересборки одного проекта | не переживает пересоздание узла, не делится между машинами |
| Кэш в реестре образов | несколько узлов и веток, сборка в изолированных окружениях | нужен доступ к реестру и чистка тегов; приватные слои не должны уходить наружу |
| Общий том кэша | парк сборочных узлов с общим сетевым хранилищем | блокировки при параллельной записи, сеть становится узким местом |
Когда узлов несколько
Здесь кэш между ветками и машинами нужен постоянно, иначе каждый узел греет кэш заново. Кэш в реестре образов удобнее: узел тянет готовые слои по тегу и не зависит от того, кто собирал предыдущим. Общий том кэша даёт больше попаданий, но требует дисциплины — две одновременные сборки в один том конфликтуют, а часть слоёв приходится признавать недействительной.
Если стабильность важнее максимального совпадения, тома разграничивают по группам узлов и держат отдельный кэш под каждую ветку сборки.
Как мы настраиваем кэш сборки Docker: этапы работ
Порядок работ одинаков для любого проекта: сначала разбираемся, какие слои пересобираются на каждом коммите, затем строим схему кэширования и только потом правим конвейер. Так релизы не встают на время правок, а эффект виден в цифрах — по времени сборки и доле слоёв, поднятых из кэша.
Подготовка и проверка
- Проводим аудит конвейера сборки: смотрим порядок инструкций в описании образа, что меняется каждый коммит, а что раз в месяц. Часто меняющиеся слои поднимаем выше, редко меняющиеся оставляем внизу — именно они и становятся кэшируемыми.
- Собираем схему кэширования: определяем, какие слои общие для веток, где кэш будет жить — на сборочном узле или во внешнем хранилище, доступном всем узлам.
- Выполняем настройку кэша в реестре: экспорт и импорт кэша в образ, раздельные теги под ветки и базовые образы, чтобы слои не перетирали друг друга.
- Делаем проверку попаданий: по логу сборки сверяем, сколько слоёв поднято из кэша, замеряем время до и после, убираем из контекста сборки лишние файлы — они рвут кэш раньше срока.
Выкат и передача
- Выкатываем на рабочие ветки постепенно: сначала основная, затем остальные, следим за временем сборки и ростом хранилища кэша.
- Передаём регламенты сборки: правила правки описания образа, что нельзя менять без согласования, памятка для разработчиков и порядок полного сброса кэша.
Что остаётся в репозитории: конфигурации, схемы и регламенты
Результат настройки измеряется не только временем сборки, но и тем, что остаётся у команды заказчика. Мы передаём не работающий кэш на нашей стороне, а файлы, схемы и правила, по которым сборку можно менять и воспроизводить без нас.
Код и инфраструктура
- Конфигурация сборочного конвейера — файлы сборки в репозитории: порядок стадий, разбиение на слои, вынесенные зависимости.
- Схема кэша — какие слои Docker кэшируются, какой ключ используется, от чего он зависит и где хранится: на сборщике или в общем реестре.
- Регламент инвалидации — что сбрасывает кэш (смена базового образа, файла зависимостей) и порядок аварийного полного сброса.
- Политики доступа к реестру — роли на чтение и запись, отдельные учётные данные для сборки, запрет анонимной записи, срок жизни токенов.
- Наблюдаемость — доля попаданий в кэш и оповещение при её падении: замедление видно раньше, чем становится проблемой.
Документация и доступы
- Документация по сборке — как собрать образ локально, как повторить в конвейере, где смотреть логи и как отличить промах кэша от ошибки в слое.
- Карта владения — кто отвечает за базовый образ, кто за политики реестра.
- Инструкция по откату — как вернуть предыдущую конфигурацию, если сборка замедлилась или стала нестабильной.
- Чек-лист приёмки — проверки, по которым принимается обновление: время сборки, доля попаданий, воспроизводимость на чистом сборщике.
Всё это лежит рядом с кодом и проходит ревью как обычное изменение — конфигурация не живёт в переписке и не теряется при смене инженера.
Кейс: сборка монорепозитория ускорилась, доля отказов упала
Что было до работ
Монорепозиторий на сорок сервисов собирался в одном конвейере Docker: общий реестр образов, общий исполнитель, одна ветка на все команды. Слои с зависимостями пересобирались при любом изменении в дереве — правка в одном сервисе тянула переустановку пакетов по всему репозиторию, полный проход занимал 22–26 минут.
Очередь росла: пока собирается один сервис, остальные ждут освобождения исполнителя. Инженеры перестали ждать результат и стали проверять локально, часть ошибок доходила до основной ветки. Доля отказов держалась на 18%, и класс сбоев «собрано на устаревших зависимостях» воспроизводился регулярно.
Что изменилось после
Разделили сборку на этапы и переписали порядок инструкций: зависимости ставятся отдельным слоем, изменяемый код копируется последним. Кэш монтируется на общий том исполнителя и переиспользуется между заданиями, а ключ привязан к контрольной сумме файла зависимостей — подмена версий не даёт собрать на устаревших слоях.
Полный проход сократился с 22–26 до 6–8 минут, повторная сборка неизменённого сервиса укладывается в минуту. Доля отказов упала с 18% до 4%: тот самый класс ошибок закрыт конструкцией кэша, а не регламентом. Реестр перестал расти — политика жизненного цикла держит последние десять образов на тег и чистит остальное, откат на предыдущую сборку остаётся возможным.
Чем отличается кэш слоёв от кэша зависимостей и пакетов?
Уровней кэширования в сборке три, и они не подменяют друг друга. Кэш слоёв работает на уровне инструкций: Docker вычисляет ключ слоя из текста инструкции и хеша родительского слоя. Кэш зависимостей живёт внутри менеджера пакетов — это его собственный склад скачанных библиотек. Монтируемый том — каталог на хосте сборки, который переживает удаление контейнера и в готовый образ не попадает.
Кэш слоёв ломается каскадом: изменилась одна инструкция — всё, что ниже, пересобирается заново. Поэтому порядок слоёв значит больше любых настроек. Сначала копируем только файл со списком зависимостей, затем ставим их и лишь потом копируем исходники — правка в коде перестаёт тянуть за собой повторную установку пакетов.
Кэш зависимостей закрывает другой сценарий. Слой с установкой может быть признан недействительным, а склад пакетов при этом цел — менеджер заберёт из него всё, кроме новых версий. Монтируемый том под склад даёт тот же эффект, но живёт отдельно от образа и переживает сброс кэша сборки и правки в файле зависимостей.
Отсюда и разница подходов. Кэш слоёв отвечает за воспроизводимость: образ собирается ровно из зафиксированных состояний файловой системы. Кэш зависимостей и том отвечают за скорость и трафик к внешним репозиториям. Тому нужен отдельный ключ на каждую ветку сборки — иначе параллельные сборки перетирают друг другу склад и дают нестабильный результат.
Частые технические сомнения по кэшированию сборок
Кэш слоёв ускоряет сборку только тогда, когда ведёт себя предсказуемо. Ниже — ответы на вопросы, которые инженеры задают до внедрения: параллельные сборки, обновление базового образа, перенос кэша между узлами и проверка корректности.
Две сборки идут одновременно — не перемешают ли они кэш?
Ключ строится из содержимого задачи и ветки, а не из общего тега, поэтому слои одной сборки не попадают в другую. Общий кэш выносим в отдельное хранилище с правом записи по задаче, а локальный наполняется от него.
Обновился базовый образ — кэш останется валидным?
Инвалидация идёт по содержимому, а не по имени тега: базовый образ фиксируем по неизменяемому идентификатору, и смена базовых слоёв сама сбрасывает зависимые. Раз в сутки запускаем полную пересборку без кэша и сверяем артефакты — расхождение означает, что правило ключа слишком широкое.
Перенесётся ли кэш на другой узел или сборочный агент?
Да: общий слой кэша кладём в реестр или объектное хранилище, новый агент читает его только на чтение и догревает локальный. Первая сборка на холодном узле получает большую часть слоёв готовыми вместо полной пересборки.
Кэш безопасен? Не останутся ли в нём секреты?
Промежуточные слои могут сохранить то, что попало в образ. Секреты подаём на этапе сборки, а не копируем в слой, ограничиваем доступ к хранилищу и держим срок жизни с регулярной чисткой — тогда кэш не превращается в канал утечки.
Обсудим вашу задачу: вернёмся с планом и оценкой
Чтобы вернуться с оценкой, а не с общими обещаниями, нам нужно увидеть ваш конвейер сборки целиком: где собираются образы, сколько шагов между правкой в репозитории и готовым артефактом, какие слои меняются на каждом изменении, а какие остаются неизменными. Достаточно ссылки на репозиторий, файла сборки, логов двух-трёх типичных сборок и описания того, где живут хранилище образов и сборочные агенты. С этим набором мы считаем, что именно даст ускорение, а что в вашем случае работать не будет — и скажем об этом прямо.
Что отдаём в ответ:
- Схема кэширования под проект: какие слои кэшировать, где и сколько хранить, как разделять кэш между ветками, агентами и параллельными сборками.
- План внедрения по шагам: порядок правок в конвейере, точки проверки, способ вернуться к прежней схеме без простоя.
- Оценка работ: состав, трудозатраты, сроки по этапам и то, что нужно с вашей стороны — доступы, стенд, окно на изменения.
- Правила инвалидации и очистки: чтобы хранилище не разрасталось, а сборки не подхватывали устаревшие слои.
- Готовые конфигурации и файлы сборки, которые можно применить в вашем репозитории без переписывания с нуля.
- Передача и обучение команды: разбор схемы, типовые сбои и что делать дежурному, когда сборка пошла мимо кэша.
- Короткая документация: где что лежит, как менять политику и как измерять выигрыш по времени сборки.
Опишите задачу в том виде, в котором она есть, — с текущими временами сборки и узкими местами. Вернёмся с планом внедрения и оценкой работ, а схему кэширования разберём на ваших реальных сборках.







