Налаштування CI/CD для Rust-застосунків (Cargo)

Ручні збірки Rust-застосунків з’їдають час команди й ламаються на дрібницях перед випуском. Ми налаштовуємо CI/CD під ключ: збірка, перевірки й доставка запускаються самі, без ручних кроків. Час від правки до готового релізу скорочується в рази.
Часті запитання

Чому CI/CD збірки Rust ламаються і гальмують команду

Збірка, яка зелена на машині розробника і падає на конвеєрі, — найдорожча категорія збоїв: причина не в коді, а в розбіжності середовищ.

Версія компілятора, набір системних бібліотек і спосіб лінкування різняться між сервером збірки та локальною машиною, тому помилка відтворюється через раз і з'їдає години на з'ясування.

Невідтворюване середовище збірки небезпечніше за явну поломку: поки воно існує, жоден реліз не можна вважати перевіреним.

Друге вузьке місце — кеш залежностей. Якщо між прогонами переноситься лише каталог із вихідними кодом бібліотек, а результатів компіляції немає, кожен запуск знову збирає все дерево залежностей: десятки хвилин замість десятків секунд.

Зворотна крайність — ключ кешу, прив'язаний до мінливого файлу: кеш не перевикористовується ніколи, і довга збірка Rust стає нормою, до якої команда звикає.

Водночас повна перезбірка проєкту на кожну зміну в одному модулі — наслідок того самого: завдання не розбиті на частини, проміжні результати не зберігаються між етапами.

Третє джерело — ручні кроки під час випуску версії. Правка номера версії в маніфесті, локальна збірка релізного артефакту, проставлення тега, відправлення до реєстру — кожен крок тримається на людині.

Пропущений крок дає артефакт, який неможливо однозначно пов'язати з комітом, а відкат перетворюється на розкопки по машинах розробників.

Інструменти на кшталт Cargo закривають відтворюваність на рівні залежностей, але не відповідають за середовище, кеш і релізні кроки — це зона конвеєра.

Нижче розбираємо, як це закривається: фіксація образу збірки, шаруватий кеш, розбиття графа на частини й автоматичний випуск версії.

Що дає налагоджений конвеєр збірки та доставки застосунків

На чистому проєкті кожен запуск конвеєра тягне залежності заново й компілює їх з нуля.

Кеш артефактів Cargo за хешем файлу блокувань та інкрементальна збірка стискають цикл перевірки правок з 12–15 хвилин до 3–4, а паралельні завдання розносять тести й лінтери по агентах — черга на злиття перестає бути вузьким місцем.

Відтворювана збірка дає більше, ніж передбачуваний час.

Закріплений файл блокувань, фіксований набір інструментів та ізольоване середовище прибирають розбіжність «локально працює, у конвеєрі падає»: один коміт дає той самий артефакт, тому відкат на попередній реліз проходить без перезбірки.

Ручні операції під час випуску — проставлення версії, тег, перелік змін, вивантаження артефакту, застосування міграцій, перемикання трафіку — переходять у конвеєр, який запускається за тегом. Час доставки змін падає до хвилин і перестає залежати від того, хто сьогодні на чергуванні.

Головний ефект — не швидкість сама по собі, а клас відмов, який перестає доходити до замовника.

Несумісні залежності, незакомічені міграції, розбіжність версій і «у мене зібралося» відсікаються до випуску: на стенд потрапляє лише артефакт, який пройшов збірку, тести й перевірку вразливостей.

Практичний підсумок: випуск вкладається в один прохід конвеєра, а ручних точок, де він може зламатися, залишається одна — злиття в основну гілку.

Варіанти організації збірки: агенти, контейнери, власні машини

Збірка Rust-застосунку впирається не в компілятор, а в середовище: Cargo тягне залежності, цільовий каталог швидко зростає, а той самий код доводиться збирати під різні платформи.

Тому перше питання під час налаштування CI/CD — де фізично виконується збірка і як між запусками перевикористовуються артефакти. Розвилок чотири, і вони не взаємовиключні.

Окремо вирішується кеш. Ізольований кеш збірки з ключем із lock-файлу та цільової платформи дає перевикористання залежностей між запусками й не дозволяє одному завданню псувати стан іншого. Без нього кожна правка в залежностях означає повну перезбірку, і час перевірки зростає на порядок.

Порівняння варіантів організації збірки

Варіант Коли підходить Обмеження
Власні складальні машини Потрібен доступ до внутрішньої мережі, приватні реєстри пакетів, особливе залізо під навантажувальні збірки Експлуатація на нас: оновлення набору інструментів, ізоляція завдань, контроль диска й черги
Збірка в контейнерах Потрібна відтворюваність: однаковий образ для локальної машини й CI, передбачувані залежності Підйом образу додає час кожному запуску; кеш потрібно виносити назовні контейнера, інакше він губиться
Керовані агенти зовнішнього сервісу Невелика команда без власної інфраструктури, публічний код, старт з мінімальними вкладеннями Обмеження за ресурсами й доступами, складніше підключити внутрішні мережі, оплата зростає з кількістю паралельних завдань
Матриця збірки за платформами Продукт постачається під кілька систем або архітектур, потрібні артефакти під кожну Кількість завдань множиться, зростає загальний час перевірки; без розпаралелювання й окремого кешу на платформу черга стає

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

Такий набір описуємо в конфігурації явно — який варіант на якій гілці і з яким кешем, щоб поведінка складальної черги була передбачуваною.

Як ми налаштовуємо CI/CD для Rust-застосунків: етапи робіт

Починаємо із заміру, а не з правок конфігурації: у Rust-проєктах основний час з'їдає холодна компіляція залежностей і повторна збірка дерева на кожну зміну.

Якщо спочатку не зафіксувати, скільки триває перевірка до першого результату і які кроки перевикористовують артефакти, будь-які оптимізації перетворюються на вгадування. Далі послідовність така.

  1. Аудит конвеєра збірки. Дивимося, що вже описано в репозиторії: які перевірки запускаються, на яких гілках, де лежать секрети і як влаштовані ручні втручання. Фіксуємо точки, де результат залежить від середовища конкретного інженера.
  2. Замір вузьких місць. Розбираємо збірку по кроках: скільки займає підтягування залежностей, компіляція власного коду, тести. Окремо рахуємо, скільки завдань перезбирають те саме дерево заново.
  3. Схема середовищ і кешу. Визначаємо, які гілки й теги ведуть у тестовий і робочий контури, що розділяємо за профілями збірки, де живе кеш залежностей і скомпільованих артефактів і за якою ознакою він скидається. Ключ кешу прив'язуємо до файлів маніфесту й набору цільових платформ.
  4. Налаштування конвеєра збірки. Складаємо кроки за зростанням ціни: форматування й статичні перевірки, потім тести, потім релізна збірка під потрібні цілі. Паралелимо незалежні завдання, кеш підключаємо на етапі до компіляції, щоб він не скидався від правки в одному модулі.
  5. Перевірка збірки перед передачею. Прогоняємо конвеєр на реальних змінах, а не на порожньому коміті: ламаємо тест, додаємо залежність із несумісною версією, відкочуємо коміт. Дивимося, де перевірка падає швидко, а де тягне за собою весь контур.
  6. Вартість робіт — розраховується після аудиту.
  7. Передача конвеєра команді замовника. Описуємо схему в репозиторії, заводимо права й доступи, розбираємо з інженерами типові сценарії: як додати крок, як оновити залежність, як відкотити збірку.
  8. Регламент змін. Домовляємося, хто і за якою процедурою править конфігурацію, і залишаємо перевірку, яка не дасть мовчки зламати кеш або обійти тести.

Що передаємо на руки: конфігурації, схеми, регламенти

Роботу завершуємо не звітом «усе налаштовано», а набором артефактів, які ваша команда читає, править і передає далі без нас. Кожен пункт нижче перевіряється на прийманні — за ним же видно, де закінчується наша відповідальність і починається ваша експлуатація.

  • Конфігурація збірки й кешу під Cargo: матриця цілей, розділення профілів, кеш каталогу збірки та реєстру пакетів за хешем файлу фіксації залежностей — повторна збірка триває хвилини замість десятків, а при зміні залежностей кеш інвалідується точково.
  • Схема взаємодії сервісів: конвеєр, реєстр образів, сховище артефактів, середовище виконання й оповіщення — з точками відмови та кроками, які йдуть паралельно.
  • Політики доступу до секретів: де лежать ключі, які в них області дії та строк життя, хто з ролей їх читає. Секрети видаються конкретному кроку, а не всьому складальному середовищу загалом.
  • Документація щодо конвеєра: стадії, змінні, умови запуску й перевірки, які блокують злиття. Новий інженер розбирається в причині падіння, не читаючи конфігурацію рядок за рядком.
  • Регламент випуску версії: від мітки в репозиторії до артефакту в середовищі — хто ініціює, які перевірки обов'язкові, як версія потрапляє до списку змін.
  • Інструкція з відкату релізу: критерії «відкочувати чи виправляти вперед», команди, точки перевірки та відповідальний. Сценарій репетируємо на стенді до здачі.
  • Карта моніторингу: метрики збірки й розгортання, пороги спрацювання та отримувачі сигналів.

Кейс: скоротили час збірки та прибрали ручні кроки

Замовник — команда, яка розвиває сервіс на Rust із десятка бібліотек в одному репозиторії.

Перед кожним випуском повна збірка тривала близько 40 хвилин, а помітна частка падінь припадала не на код, а на розбіжності середовищ: у розробника залежності оновлені, на складальній машині — ні.

Ми перевели проєкт у спільний конвеєр GitLab CI і розібрали його на незалежні стадії: перевірка формату, тести, збірка релізного артефакту.

Залежності та проміжні результати кешуються між запусками, компілятор і набір бібліотек закріплені в одному образі — він однаково працює на машині інженера й у конвеєрі.

Метрики до і після

  • Тривалість повної збірки: з 40 хвилин до 11 — завдяки кешу та розпаралелюванню стадій.
  • Частка падінь через середовище: була приблизно третина від усіх збоїв, залишилися поодинокі випадки.
  • Ручних операцій перед випуском: з дев'яти до однієї — залишився тільки запуск випуску.
  • Відновлення після збою збірки: з кількох годин до 15 хвилин, бо збій відтворюється локально тим самим чином.

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

Чим відрізняється кеш залежностей від кешу артефактів збірки?

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

Кеш артефактів збірки — результати компіляції вашого проєкту: об'єктні файли й готові бінарники, на яких тримається інкрементальна збірка проєкту.

Що кешуємо Що всередині Коли перевикористовується Обмеження
Залежності вихідний код і зібрані зовнішні бібліотеки при незмінному файлі блокування версій і тому самому профілі збірки зростає лінійно, потрібна політика витіснення
Артефакти збірки об'єктні файли, проміжні та фінальні бінарники при частковій зміні вихідного коду прив'язаний до гілки, тулчейну й середовища
Результати перевірок бінарники та звіти тестів при збігу відбитка коду не можна переносити між середовищами

Ключ кешу в залежностей — Cargo.lock, версія тулчейну й профіль збірки; в артефактів — відбиток вихідного коду та попередній стан каталогу збірки. Оновлення однієї бібліотеки скидає перший шар і майже не зачіпає другий, а правка одного модуля робить навпаки.

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

Якщо кешувати каталог збірки цілком, хибні влучання віддають бінарник від чужого коміту, і налагодження переходить у пошук плаваючого дефекту.

Підсумок — два ключі, дві політики витіснення, різні права на запис. Тоді збірка без змін проходить за секунди замість повного циклу.

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

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

Суворі перевірки помітно подовжать збірку?

Форматування, статичний аналіз і тести йдуть окремими кроками й паралельно. Кеш за графом залежностей Cargo перевикористовує незмінні шари — повторний прогін займає секунди, а не хвилини.

У нас залишилися ручні налаштування від попередньої команди

Конвеєр описуємо декларативно в репозиторії, а середовище відтворюється з опису, а не зі стану конкретної машини. Скрипти й параметри переносимо в захищене сховище — після цього збірка піднімається з нуля на чистому агенті.

Що буде, якщо складальна машина впаде посеред роботи

Збірка розбита на кроки зі збереженням результатів: повторний запуск стає на вільний агент і пропускає вже завершене. Артефакти лежать окремо від агента, у релізний канал іде лише повний набір збірок, що пройшли перевірку.

Потрібно збирати під кілька цільових платформ

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

Де зберігаються ключі та секрети збірки

Секрети лежать у захищеному сховищі й видаються кроку лише на час виконання. Логи, кеш і артефакти перевіряються на витік автоматично, доступ до зміни секретів — за ролями й із журналом.

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

Щоб повернутися з оцінкою строків і планом робіт зі збірки, нам потрібен мінімальний набір вхідних даних. Нижче — те, що зазвичай запитуємо: із цим складом вихідних даних оцінка виходить предметною, а не «плюс-мінус квартал».

  • Структура репозиторію та робочого простору Cargo: скільки крейтів, чи є воркспейс, які залежності підтягуються з внутрішніх реєстрів або напряму з git.
  • Цільові платформи: під які системи й архітектури потрібен бінарник, чи потрібні кроскомпіляція, статичне лінкування та окремі збірки під вбудовані таргети.
  • Поточний конвеєр: де збірка живе зараз, чим запускається, скільки триває повний цикл від коміту до артефакту і на якому кроці найчастіше рветься.
  • Вимоги до кешу й агентів збірки: допустимий час збірки, обсяг кешу залежностей, обмеження за ресурсами й паралелізмом.
  • Перевірки до релізу: які тести обов'язкові до проходження, чи потрібні статичний аналіз, контроль ліцензій залежностей і перевірка вразливостей.
  • Правила випуску: схема версіонування, куди публікуються артефакти, чи потрібні підпис, відтворюваність збірки й автоматичний відкат на попередній реліз.
  • Доступи й середовища: де розгортаються агенти, чи є закритий контур, хто володіє секретами й образами.
  • Хто на вашому боці ухвалює рішення щодо релізного процесу і доступний для коротких уточнень.

Опишіть задачу й надішліть те, що вже є, — повернемося з оцінкою строків, схемою конвеєра та планом робіт за етапами: від базової збірки до випуску й відкату.

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

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка логотипу компанії B2B Advance
    Розробка логотипу компанії B2B Advance
    816
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1141