Dockerfile оптимізація: чому образи ростуть, а збірка гальмує
Розмір образу й час збірки майже ніколи не впираються в один рядок Dockerfile — їх визначає модель шарів. Кожна інструкція RUN, COPY або ADD створює окремий шар, і видалення файлу в наступному шарі його не зменшує: дані залишаються в попередньому.
Тому apt-get update, встановлення пакетів і rm -rf /var/lib/apt/lists/* у різних шарах дають образ важчий за очікуваний, хоча всередині контейнера зайвих файлів не видно.
Друга причина — контекст збірки. Збирач отримує каталог цілком, разом з історією Git, локальними залежностями й артефактами попередніх збірок: передавання контексту займає більше часу, ніж сама збірка, а випадковий COPY переносить сміття у фінальний образ.
Порядок інструкцій важить не менше: якщо залежності ставляться після COPY всього проєкту, будь-яка правка у вихідному коді знецінює шар із ними, і пакети встановлюються заново при кожному коміті.
Важка база додає до цього компілятори, заголовкові файли й повний набір утиліт — усе це залишається в образі, хоча для роботи потрібні лише застосунок і його бібліотеки. Розділення збірки й запуску на кілька стадій залишає в результаті лише те, що потрібно для старту процесу.
Dockerfile оптимізація — це послідовна робота з чотирма точками: базовим образом, порядком шарів, контекстом і розділенням стадій. Нижче розбираємо, як кожна з них перетворюється на конкретні правки і що це дає за часом збірки та розміром образу.
Що дає охайний образ: швидкість збірки й передбачуваність
Швидкість збірки визначається тим, скільки шарів береться з кешу. Якщо копіювати в образ усю кодову базу до встановлення залежностей, перша ж правка у вихідному коді обнуляє кеш, і встановлення пакетів запускається заново.
Зворотний порядок — спочатку маніфест залежностей, потім код — залишає важкий шар незмінним, і повторна збірка після правки у вихідному коді займає секунди замість хвилин.
Розмір образу складається з дрібниць: базовий образ загального призначення, кеш пакетного менеджера, що залишився всередині шару, інструменти збірки, які потрапили у фінальний образ.
Багатоетапна збірка й очищення кешу в тому самому шарі, де ставилися пакети, прибирають це повністю — образ зменшується кратно, а витрати на зберігання в реєстрі та на вузлах перестають зростати з кожним релізом.
Відтворюваність збірки — окремий ефект. Docker дає однаковий результат лише за однакових вхідних даних: базовий образ закріплено за незмінним ідентифікатором, а не за плаваючим тегом, залежності зафіксовано, збірка відбувається в єдиному середовищі, а не на машині розробника.
Тоді зникає клас відмов «у тесті зібралося, у бою ні»: артефакт збирається один раз і просувається між середовищами без повторної збірки.
Загалом це змінює не лише цифри, а й режим роботи. Коротка збірка дозволяє частіше перевіряти зміни, а передбачуваний образ — відкочуватися на попередній реліз без повторної збірки.
Dockerfile оптимізація: порівняння підходів до збірки образу
Вибір між підходами до збірки — це питання того, де ми платимо: часом збірки, розміром образу чи складністю супроводу. Одноетапна збірка налаштовується за один прохід, але залишає компілятори й допоміжні пакети у фінальному шарі. Багатоетапна дає менший образ, але потребує дисципліни в кеші та копіюванні артефактів.
Що порівнюємо
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Одноетапна збірка | Простий сервіс, скрипти, статика; набір залежностей збігається у збірки та середовища виконання | Інструменти збірки залишаються в образі: він зростає, разом із ним — час викочування та кількість уразливих пакетів |
| Багатоетапна збірка | Код компілюється або збирається в один артефакт, а в середовищі виконання потрібен лише результат | Складніше читати й підтримувати; за необережного копіювання у фінальний шар потрапляє зайве |
| Тонкий фінальний шар | Потрібен мінімальний базовий образ і передбачуваний розмір | Кожну бібліотеку додаємо руками; пропущена залежність виявляється лише під час запуску |
| Збірка з готового дистрибутива | Є перевірений артефакт від вендора або окремого етапу збірки | Образ не відтворюється з вихідного коду: крок збірки живе окремо й потребує власної підтримки |
Ми вибираємо за вмістом середовища виконання: якщо застосунку потрібні ті самі пакети, що й збирачу, розділення етапів виграшу не дає. Якщо на виході один бінарник або набір статики — багатоетапна схема й тонкий фінальний шар скорочують образ і прискорюють викочування.
Етапи повторної збірки: від вимірювання шарів до передавання регламенту
Роботу над образом ведемо не «на око», а від вимірювання: спочатку фіксуємо, скільки займає збірка на порожньому й прогрітому кеші шарів і з чого складається розмір образу, а потім змінюємо крок за кроком, перевіряючи результат після кожного.
Кожен етап завершується вимірюваним підсумком — часом збірки, розміром образу, часткою кроків, які виконуються без ручного втручання.
- Аудит збірки. Розбираємо поточний файл збірки й журнал: які шари перебудовуються під час правки одного рядка коду, де втрачається кеш, скільки часу займає повна та повторна збірка, скільки важить образ у реєстрі.
- Схема шарів. Розкладаємо образ за частотою змін: системні пакети, залежності проєкту, вихідний код, конфігурація. Рідкісне — вниз, змінне — вгору, щоб робочі шари перевикористовувалися, а не збиралися заново.
- Порядок інструкцій. Переставляємо кроки під цю схему, копіюємо лише потрібні файли, а не весь каталог. Інструменти збірки й тимчасові залежності залишаються на окремому етапі й у підсумковий образ не потрапляють.
- Налаштування збірки. Правила виключення зайвого, мінімальний базовий образ, закріплені залежності, єдина точка їх встановлення — щоб образ не зростав від збірки до збірки.
- Перевірка образу. Звіряємо розмір, кількість шарів, відсутність налагоджувальних утиліт і службових файлів, час збірки на чистому кеші та повторній. Додатково проганяємо перевірку на уразливі пакети й запуск сервісу в ізольованому середовищі.
- Перевірка повторюваності. Дві збірки поспіль на різних машинах дають однаковий результат, без ручних кроків і локальних залежностей — інакше виграш не закріпиться.
- Передавання регламенту. Віддаємо файл збірки, правила виключень, опис шарів і критеріїв приймання, коротку інструкцію «як змінювати й що перевіряти» — щоб команда підтримувала результат самостійно.
Що ви отримуєте на руки: конфігурації, схеми, регламенти
Робота завершується не порадою «об’єднайте команди в один рядок», а набором файлів і правил, за якими збірка й образ живуть далі без нас. Нижче — що саме передаємо і навіщо кожен артефакт потрібен у щоденній роботі команди.
- Конфігурація збірки — робочий файл із вибудуваними шарами й кешуванням залежностей: правка коду більше не тягне повторне встановлення пакетів, повторна збірка вкладається в одиниці хвилин.
- Опис шарів — карта, де який шар лежить і що скидає кеш вище нього; інженер одразу бачить, чому зміна одного файлу перебудовує половину образу.
- Правила роботи з базовими образами — який образ вважаємо еталонним, звідки беремо оновлення, як перевіряємо його перед зміною тегу і що фіксуємо жорстко, а що оновлюємо контрольовано.
- Правила роботи із залежностями й артефактами — порядок копіювання файлів, розділення етапів збірки й запуску, щоб у підсумковий образ не потрапляли компілятори й тимчасові файли.
- Документація збірки — команди локального запуску, змінні середовища, точки входу й типові помилки; новий інженер збирає образ сам, без усних підказок.
- Регламент оновлення — періодичність повторної збірки базових шарів, порядок перевірки уразливостей і правила відкату на попередній образ.
- Схема перевірок у конвеєрі — які кроки блокують потрапляння образу до реєстру, а які лише попереджають.
Усе це залишається у вашому репозиторії й читається як інструкція, а не як листування в месенджері.
Кейс: збірка прискорилася в рази, образ став удвічі меншим
Проєкт — внутрішній сервіс із нативними розширеннями й помітним деревом залежностей. Збірка на кожен коміт займала 18 хвилин, а готовий образ у 2,5 раза перевищував обсяг самого коду: до нього потрапляли компілятори, кеш пакетного менеджера й тестові дані.
Кожен десятий запуск падав через розбіжність версій системних бібліотек між середовищем збірки та робочим середовищем, і на розбір причини йшло до пів години.
Що змінили
Збірку на Docker розвели на етапи: спочатку ставляться залежності, потім копіюється код, тому правка одного файлу перестає скидати кеш шарів.
Фінальний образ збирається з мінімальної бази — до нього копіюються лише артефакти, а компілятори, тестові дані й кеш пакетного менеджера залишаються на попередньому етапі.
Версії системних пакетів зафіксовано й перевіряються під час збірки, тому склади бібліотек у збірці та в запуску збігаються.
Що стало
Збірка під час правки коду — 4 хвилини замість 18, за зміни лише конфігурації — менше хвилини. Образ став у 2,6 раза меншим, кількість шарів скоротилася з 30 до 11, тому вивантаження на вузли й старт контейнера прискорилися у стільки ж.
Частка падінь запуску знизилася з 12% до 1%: клас відмов «незбіг бібліотек» зник повністю, залишилися тільки помилки конфігурації самого сервісу.
Чим відрізняється багатоетапна збірка від одноетапної на практиці?
В одноетапній збірці компілятор, вихідний код, інструменти й кеш залежностей залишаються в тому самому образі, що й запущений застосунок. У підсумковий образ потрапляє все, що знадобилося під час збірки, включно з утилітами, які в роботі не викликаються жодного разу.
Розмір підсумкового образу зростає в рази, а разом із ним — поверхня атаки й час вивантаження образу на вузли.
Багатоетапна збірка розділяє ці ролі. Етап збірки ставить інструменти й компілює, фінальний стартує з мінімальної бази та отримує результат через перенесення артефактів між етапами — копіюванням із попереднього етапу.
У фінальний образ потрапляють лише зібрані бінарники й середовище виконання, тому розмір скорочується до розміру коду, а зайві утиліти в контейнер не проникають.
Платимо за це складністю супроводу. Середовищ стає два — збірки й запуску, і версії в них потрібно тримати узгодженими: розбіжність виявляється лише в роботі, а не під час збірки.
Кеш етапів збирається інакше, проміжні шари у фінальному образі не видно, тому звичне налагодження «зайти в контейнер і подивитися» перестає працювати — під нього додають окремий етап.
Перехід виправданий не завжди. Якщо застосунок не компілюється, артефакт надходить готовим ззовні або образ живе лише у внутрішньому контурі, де розмір не критичний, одноетапна збірка дає більш передбачуваний і простий результат.
Рішення ухвалюють за вимірами: скільки шарів і обсягу додає встановлення інструментів і як змінюється час збірки після розділення етапів.
Питання, які ставлять перед повторною збіркою образів
Перед повторною збіркою образів виринають ті самі сумніви: чи переписувати збірку цілком, як переконатися, що образ не втратив функціональність, і що робити зі старою конфігурацією.
Нижче — короткі відповіді за кожним пунктом, щоб рішення ухвалювалося до правок, а не після невдалого релізу.
Чи потрібно переписувати збірку цілком?
Ні, і зазвичай це зайвий ризик. Починаємо з розбору шарів: що змінюється під час кожного релізу, а що стабільне місяцями. Рідко змінювані залежності ставляться вище у файлі, код застосунку — нижче, тому кеш шарів перевикористовується і повторна збірка йде по зміненій частині. Далі точкові правки, і кожна підтверджується виміром часу збірки та розміру образу.
Як перевірити, що повторна збірка нічого не зламала?
Порівнюємо образ до й після за формальними ознаками: склад встановлених пакетів, точки входу, відкриті порти, змінні середовища, права користувача всередині контейнера. Додатково проганяємо перевірку змін у конвеєрі — ті самі тести, що й на релізній гілці. Розбіжність у будь-якій з ознак розбирається до викочування, а не після.
Що робити зі старою конфігурацією?
Міграція конфігурації відбувається поетапно: старий і новий варіанти якийсь час співіснують, перемикання — за одним параметром. Попередній образ залишається в реєстрі з живим тегом, тому відкат займає хвилини.
Окремо фіксуємо, які налаштування переїхали в образ, а які залишилися в зовнішньому середовищі — інакше супровід збірки перетворюється на вгадування.
Хто відповідає за збірку після передавання?
Правила збірки лежать у репозиторії поруч із кодом, а не в листуванні. До кожного неочевидного шару — короткий коментар: навіщо він і що буде, якщо його прибрати. Ваша команда править файл самостійно, наші інженери підключаються на змінах, що зачіпають базовий образ.
Обговорімо задачу: що надіслати для оцінки та плану робіт
Точність оцінки робіт залежить від вхідних даних: чим повніша картина поточного конвеєра, тим менше припущень у розрахунку. Нижче — мінімальний набір, за яким ми повертаємося з планом робіт, етапами й порядком змін. Чогось зі списку може не бути — так і напишіть, для першої ітерації частина даних не критична.
- Поточний Dockerfile цілком — базовий образ, стадії збірки, порядок інструкцій.
- Лог збірки — тривалість за кроками й розмір образу до змін.
- Структура репозиторію — де лежать залежності, маніфести й файли фіксації версій.
- Як влаштований конвеєр — чим запускається збірка і чи зберігається кеш шарів між запусками.
- Вимоги до середовища — які сервіси потрібні під час роботи, що пишеться на диск.
- Обмеження — приватний реєстр, закриті мережі, правила безпеки, ліміти на розмір артефакту.
- Критерій готовності — цільовий час збірки й розмір образу, за яких задача вважається закритою.
Якщо частина даних закрита, старт проєкту можливий з того, що є: розберемо поточну збірку й повернемося з оцінкою робіт і планом.
Консультація щодо збірки — частина цього розбору: показуємо, які шари й порядок кроків дають основний виграш за часом і розміром, що можна перенести в базовий образ, а що прибрати зовсім. Опишіть задачу — повернемося з оцінкою та планом.







