Кешування залежностей CI/CD: де збірка втрачає хвилини
Кожен запуск CI/CD-конвеєра починається з чистого середовища, і перше, що воно робить, — знову завантажує та збирає ті самі залежності.
Повторне завантаження пакетів займає від половини до двох третин часу збірки конвеєра: це хвилини очікування правки після кожного коміту замість роботи. А коли зовнішній реєстр відповідає повільно або обмежує частоту запитів, конвеєр падає не через код.
Кешування залежностей CI/CD знімає саме цю ділянку, але працює лише за точного ключа. Ключ, прив'язаний до lock-файлу, інвалідує кеш рівно тоді, коли змінився набір версій, і дає влучання майже на кожному запуску.
Ключ за гілкою або номером збірки дає протилежне: або регулярні промахи, або тихе залипання на застарілих версіях, яке випливає вже на стенді.
Друге вузьке місце збірки — шлях до зовнішніх реєстрів, а не обчислення. Локальний проксі-реєстр і артефакт-сховище переводять завантаження у внутрішню мережу: пакети надходять з диска поруч із раннерами й перестають залежати від доступності публічного джерела. Сюди ж лягає прогрів кешу за розкладом, щоб перший запуск після оновлення не тягнув усе заново.
На виході час збірки конвеєра скорочується в рази, а сама збірка перестає залежати від зовнішньої інфраструктури. Розбираємо, як це закривається на практиці: ключі та шари кешу, спільний кеш для паралельних задач і політика очищення, яка не дає йому розростатися.
Що змінюється у швидкості збірки та надійності доставки
Кеш залежностей змінює профіль збірки: крок встановлення перестає бути найдорожчим. Коли сховище прогріте, час визначається компіляцією та тестами, а не завантаженням — цикл перевірки змін скорочується у 2-4 рази, і правка перевіряється, поки контекст задачі ще в голові.
Ключ кешу рахується за контрольною сумою файлу залежностей, тому кроки, які раніше ходили до реєстру, забирають готові артефакти з локального сховища. Мережа випадає з 80-90% збірок: зовнішні звернення залишаються лише там, де залежності справді змінювалися.
Навантаження на реєстр пакетів падає кратно — зникають черги, обмеження на частоту запитів і таймаути на піках. Заодно реєстр перестає бути єдиною точкою відмови: за його недоступності конвеєр доходить до кінця на прогрітому кеші, падає лише крок оновлення залежностей.
Окремий ефект — класи відмов, які зникають зовсім. Більше не відтворюються обриви завантаження на середині, розходження версій непрямих залежностей між запусками та збірки, зелені з другого прогону. Стабільність конвеєра стає властивістю конфігурації, а не удачі.
Передбачуваний час збірки — прямий наслідок: розкид між швидким і повільним прогоном стискається до 10-15% замість двократного. За такої тривалості вже можна планувати випуск і тримати вікно доставки фіксованим.
Способи зберігання артефактів збірки: порівняння варіантів
Від того, де лежить кеш, залежить, скільки крок встановлення залежностей додає до кожної збірки.
Локальний кеш на раннері бере файли прямо з диска і працює найшвидше, але прив'язаний до конкретного вузла: задача переїхала на інший раннер або машину перестворено — кеш порожній, і встановлення йде з нуля.
Спільний кеш в об'єктному сховищі прибирає прив'язку до вузла, але потребує продуманого ключа та обмежень за одночасного запису з кількох збірок.
| Варіант | Область видимості | Швидкість | Супровід | Паралельні збірки |
|---|---|---|---|---|
| Локальний кеш на раннері | Один вузол і його робочий каталог | Максимальна, файли на диску | Мінімальний: періодичне очищення | Нестійкий: вузли не ділять дані, зростає дублювання |
| Спільний кеш в об'єктному сховищі | Усі раннери, гілки та середовища — за ключем | Висока за теплого кешу, залежить від мережі | Середній: правила інвалідації, строк життя об'єктів | Стійкий за блокування запису за ключем |
| Кеш у реєстрі образів | Проєкт цілком, шари доступні всім задачам | Висока, шари підтягуються паралельно | Низький: життєвий цикл задається політикою реєстру | Стійкий: шари незмінні, конфліктів запису немає |
| Спільна мережева файлова система | Усі вузли, де змонтовано том | Середня, впирається в затримки введення-виведення | Високий: права, квоти, узгодженість даних | Обмежено: запис стає вузьким місцем |
Область видимості задається ключем: якщо до нього входить хеш файлу залежностей і версія середовища, застарілі дані не перевикористовуються, а гілки не витісняють одна одну.
Правила інвалідації важливіші за обсяг — кеш без обмеження за ключем і строком життя зростає швидше, ніж приносить прискорення. Тому варіант обирають за парою «швидкість + супровід», а не за максимальною швидкістю відірвано від решти.
Порядок впровадження: від аудиту до передачі команді
Кеш не налаштовують на око: спочатку заміряємо, яку частку часу циклу займає встановлення залежностей і як часто повторні запуски перевикористовують уже завантажені пакети.
Знімаємо базовий зріз — час за кроками, частку влучань, обсяг завантажуваних архівів — і лише потім чіпаємо конфігурацію.
- Аудит конвеєра. Розбираємо завдання: де ставляться залежності, чи читається локальний кеш між запусками, які гілки та середовища використовують той самий набір. Фіксуємо поточну поведінку та причини промахів.
- Схема ключів кешу. Ключ збираємо з незмінних ознак: файл блокування залежностей, версія рантайму, тип завдання, базовий образ. Він має змінюватися під час оновлення набору пакетів і залишатися стабільним під час правки коду застосунку.
- Розділення областей. Окремий кеш на збірку, тести та розгортання — змішування робить ключ надто широким, і завдання починають отримувати чужі набори пакетів.
- Перевірка влучань. Серія з холодної та повторних збірок: звіряємо частку влучань і час кроку встановлення. Окремо проганяємо гілку з оновленням залежностей — ключ зобов'язаний змінитися.
- Інвалідація. Правило зберігання: строк життя та ліміт обсягу, щоб кеш не розростався і не віддавав застарілі версії пакетів.
- Передача налаштувань. Конфігурація лежить у репозиторії поруч із кодом; схему ключів, правило очищення та порядок дій за промахів віддаємо команді короткою документацією.
Комплект постачання: конфігурації, політики, регламенти
Комплект постачання — це те, що залишається в репозиторії та в системі після того, як кешування запрацювало.
Без нього прискорення збірок тримається на одному інженері: за зміни команди або переїзду сховища конвеєр тихо повертається до повного завантаження залежностей, а причина видна лише за зрослим часом збірки.
- Конфігурація конвеєра з явними кроками відновлення та збереження кешу і порядком їх виконання щодо встановлення залежностей.
- Схема ключів: з чого формується ключ (файл блокувань, гілка, тип задачі), які шари спільні, а які прив'язані до конкретної збірки.
- Політика зберігання: строк життя записів, граничний обсяг, правило витіснення старих шарів і частота повної інвалідації.
- Правила доступу та розділення: хто може перезаписувати спільний кеш, що принципово не потрапляє до нього — передусім секрети та приватні реєстри.
- Схема відмовостійкості: поведінка збірки за недоступного сховища — конвеєр деградує до звичайного завантаження, але не падає.
- Документація зі збірки: як відтворити середовище локально, що робити за незбігу ключа і як перевірити, що залежності взяті з кешу, а не завантажені заново.
- Регламент супроводу: періодичність ревізії записів, порядок інвалідації за зміни базового образу та відповідальний за зміни схеми ключів.
- Інструкція з діагностики типових збоїв: хибні влучання після оновлення залежностей, зростання обсягу сховища, уповільнення на етапі розпакування.
Окремо фіксуємо точку входу: як додати кеш для нового сервісу, не ламаючи вже налаштовані. Це той мінімум, після якого прискорення збірок відтворюване, а не разове.
Кейс: збірка монорепозиторію прискорилася в рази
Монорепозиторій на 40+ пакетів збирався 35–40 хвилин, і майже весь цей час ішов на повторне завантаження тих самих залежностей із зовнішніх джерел. Кеш формально був увімкнений, але ключ будувався за номером гілки — за кожного коміту він змінювався, і сховище не влучало в ціль.
Гірше того, за мережевих збоїв на боці реєстру падав увесь конвеєр: 5–7 зламаних збірок на тиждень, кожна — ручний перезапуск і втрачені години команди.
Що змінили
Перезібрали схему ключів: базовий шар із системними пакетами та інструментами фіксованої гілки оновлюється за розкладом, шар залежностей прив'язаний до lock-файлу, шар артефактів — до вмісту самих пакетів. Так зміна коду в одному модулі не інвалідує кеш сусідніх.
Поруч підняли локальне дзеркало реєстру з прогрівом за найчастішими пакетами — конвеєр перестав залежати від доступності зовнішньої мережі. На GitLab CI це дало ще й паралельний прогрів: гілка графа, зачеплена правкою, збирається заново, решта підтягується зі сховища.
Метрики до і після
| Показник | До | Після |
|---|---|---|
| Медіанна тривалість збірки | 38 хв | 9 хв |
| Частка влучань у кеш | 12% | 78% |
| Збої через недоступність джерел пакетів | 5–7 на тиждень | 0 |
| Частка збірок без перезбирання залежностей | 8% | 84% |
Окремо перевірили інвалідацію: під час оновлення lock-файлу перезбирається лише зачеплена частина графа, відкат на попередній набір залежностей залишається можливим. Правки в кеш-конфігурації проходять рев'ю як звичайний код — регресії ловляться на першому ж прогоні.
Чим кешування залежностей відрізняється від пошарового зберігання?
Це різні рівні прискорення збірки, і плутають їх часто. Пошарове зберігання образів кешує результат збірки самого образу: кожен шар — набір змін файлової системи, прив'язаний до інструкції, яка його створила.
Кеш залежностей живе окремо від образу і зберігає артефакти менеджера пакетів, завантажені або зібрані під час роботи конвеєра.
Інвалідація в них різна. Інвалідація шарів іде ланцюжком: зміна будь-якої інструкції вище за описом скидає всі наступні шари, тому маніфест залежностей копіюють до вихідного коду — тоді правка коду перестає перезбирати встановлення пакетів.
Кеш залежностей інвалідується за ключем: хеш маніфесту із зафіксованими версіями. За промаху береться найближчий попередній ключ і довантажується лише те, чого бракує.
У цьому і сенс розділення рівнів: образ відповідає за середовище виконання — системні бібліотеки, середовище виконання, зібраний застосунок.
Кеш залежностей прискорює кроки, які до образу взагалі не потрапляють: модульні тести, перевірки стилю, статичний аналіз, збірку артефактів. Якщо кешувати лише образи, ці кроки на кожному запуску заново тягнуть ті самі пакети.
Ефект видно на контрасті гарячого та холодного прогону: холодний вимірюється хвилинами, гарячий — десятками секунд, а вузьке місце зміщується зі встановлення залежностей на сам прогін тестів. На практиці кеш тримають спільним для гілки, щоб паралельні збірки не перетирали записи одна одної.
Перевіряється це за логами кроків: влучання в кеш і причина промаху друкуються явно. Якщо промах повторюється на кожному запуску — у ключ затесалося змінне значення на кшталт номера збірки, або шар із залежностями стоїть після копіювання вихідного коду.
Обмеження зберігання та типові помилки налаштування
Кеш залежностей поводиться передбачувано рівно до моменту, коли змінюється те, що лежить за його межами: версія бібліотеки, склад середовища або джерело пакетів. Нижче — чотири ситуації, які найчастіше випливають уже в робочому процесі.
Розберемо їх по одній, з ознакою збою та способом налаштування.
Кеш віддає застарілі залежності — як це зловити?
Ознака: збірка проходить, але в артефакті залишається минула версія бібліотеки. Ключ запису має будуватися на хеші файлу блокування залежностей, а не на імені гілки — тоді будь-яке оновлення версій автоматично промахується повз старий запис. Плюс строк життя: дані, не запитані довше за задану кількість циклів, вичищаються.
Два середовища перетирають кеш одне одного
Це конфлікт ключів: ключ зібраний з імені гілки, і однакові імена в різних репозиторіях або середовищах потрапляють в один запис. Ключ робимо складеним — ідентифікатор репозиторію, середовище, платформа збірки.
Ціна помилки невелика: за промаху залежності збираються заново, чужий артефакт у збірку не потрапить.
Що робити із закритим реєстром пакетів?
Внутрішній реєстр не віддає пакети назовні й вимагає авторизації на кожен запит. Кеш ставимо на боці агента збірки, всередині периметра: назовні йдуть лише перевірені артефакти.
Облікові дані зберігаємо в секретах, а не в конфігурації кешу, — інакше вони витікають разом із дампом.
Сховище зростає безкінечно — як обмежити?
Без політики очищення кеш зростає швидше за код: кожна збірка додає шари, старі ніхто не видаляє. Правило утримання — останні кілька записів на ключ плюс видалення всього, що не запитувалося за заданий період. Обсяг виходить на плато, частка промахів залишається в допустимих межах.
Обговоримо задачу: що уточнити перед стартом
Щоб повернутися з оцінкою та планом робіт, а не із загальними обіцянками, нам потрібні ввідні щодо вашого конвеєра. Нижче — що уточнюємо на першій розмові: цього набору достатньо, щоб порахувати обсяг, порядок етапів і ризики.
- Як влаштований конвеєр: одна збірка на репозиторій чи матриця цілей, які стадії обов'язкові, що вже кешується.
- Менеджери пакетів і файли фіксації версій: скільки їх і як часто змінюються — від цього залежить ключ кешу та поведінка під час оновлення залежностей.
- Де зберігається кеш: локальний диск агента збірки, спільний мережевий том чи об'єктне сховище — впливає на швидкість і на очищення застарілого.
- Топологія агентів: власні машини чи хмарні, скільки одночасних збірок, чи є обмеження мережі до зовнішніх реєстрів.
- Структура репозиторіїв і спільних бібліотек: тримати кеш спільним чи розділяти за проєктами.
- Доступи та периметр: хто володіє налаштуваннями агентів, закрита мережа, чи дозволено проксі.
- Цільовий час збірки: скільки займає типова збірка зараз і яка ціль за часом — за цим рахуємо ефект.
З цими ввідними ми повертаємося з оцінкою термінів, порядком етапів і переліком змін у конфігурації агентів. Опишіть задачу текстом або посиланням на поточну конфігурацію — запропонуємо план робіт.







