Webrium
Подписаться

Образ, который собирается минуту вместо десяти

Время сборки определяется не объёмом работы, а тем, какая её часть выполняется заново. Одна перестановка строк даёт больше, чем подбор базового образа.

Кеш сборки держится до первого изменившегося шага: всё, что ниже, пересобирается зановокеш держитсяпересобирается

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

Тупик: начать с уменьшения образа

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

Это не решает ту задачу, которая болит. Размер образа влияет на выгрузку и на скачивание при запуске; время сборки он почти не определяет. Заменив базовый слой, вы получите образ меньше, а ждать будете столько же — потому что девять минут уходили не на базовый слой, а на установку зависимостей, и она никуда не делась.

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

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

Что на самом деле определяет время

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

Отсюда единственное правило, из которого следует почти всё остальное: редко меняющееся должно идти раньше часто меняющегося.

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

Правильный порядок обратный: сначала копируется только описание зависимостей, потом ставятся зависимости, и лишь потом копируется остальной код. Тогда правка в коде не трогает установку, и она берётся из кеша.

Это одно изменение обычно даёт больше, чем все остальные вместе.

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

Проверить, где именно теряется время, можно без инструментов. Соберите образ дважды подряд, ничего не меняя: вторая сборка покажет, что берётся из кеша. Затем поменяйте одну строку в коде и соберите снова. Разница между вторым и третьим прогоном — это и есть цена вашей типичной правки, и обычно она объясняет всё.

Что делать дальше

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

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

Не ставить лишнего. Часть зависимостей нужна только для тестов и линтеров. В образ, который поедет в бой, они попадать не обязаны: это и время установки, и лишняя поверхность.

Закрепить версии. Это не про скорость, а про повторяемость, но связано напрямую: сборка, которая при каждом запуске подтягивает свежие версии, не может пользоваться кешем осмысленно и вдобавок непредсказуема.

Где кеш теряется незаметно

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

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

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

Третий способ — сборка на разных архитектурах. Образ, собранный для одной архитектуры процессора, не переиспользуется для другой, и команда, где часть машин на одной, а конвейер на другой, регулярно недоумевает, почему кеш «не работает». Он работает, просто их два.

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

Как понять, что стало лучше

Мерить надо не полную сборку с нуля, а типичную: правка в одной строке кода при неизменных зависимостях. Именно она случается десятки раз в день, и именно её время определяет, ждёт разработчик или работает.

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

Стоит мерить и время до готовности, а не только до конца сборки. Образ, который собрался быстро, но стартует долго, переносит ожидание с разработчика на выкатку — суммарно не выиграли ничего, просто ждёт теперь другой человек в другом месте.

Позиция редакции

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

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

С чем это связано

Метка над заголовком говорит, зачем туда идти: продолжить тему, перейти к практике или увидеть возражение.

Вложенные слои — поколения модных инструментов; в самой глубине выделен старый механизм, то, что осталось живым.старые механизмы
Усиление

Деплой без кластера: что осталось живым

Подтверждает вывод с другой стороны: кластер из одной ноды даёт всю сложность оркестратора и ни одной его выгоды.

Стык инструкции с реальностью: у golden path шаги и система совпадают, у документации текст разошёлся с жизньюшаги зашиты в кодтекст разошёлся
Практика

Golden path вместо документации

Как сделать проторённый путь: он держится не ревью и не напоминаниями, а тем, что по нему идти проще, чем в обход.

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

Цепочка поставок: перечень, который кто-то читает

Глубже про состав: перечень становится инструментом, только когда у него появляется читатель, и в статье описано, как это устроить.

Письмо по вторникам

Один разбор недели и короткий список того, что изменилось. Без дайджестов на сорок ссылок.

Начните вводить — материалы появятся здесь.

выбрать · Enter открыть · Esc закрыть