Коли це можливо, я завжди зливаю разом команди, які створюють файли, з командами, які видаляють ті самі файли в один RUN
рядок. Це пояснюється тим, що кожен RUN
рядок додає шару образу, вихід - це буквально зміна файлової системи, яку ви могли переглянути з docker diff
тимчасовим контейнером, який він створює. Якщо ви видалите файл, створений в іншому шарі, всі файлові системи об'єднання - це зареєструвати зміну файлової системи на новому шарі, файл все ще існує в попередньому шарі і надсилається через мережевий і зберігається на диску. Отже, якщо ви завантажуєте вихідний код, витягуєте його, компілюєте у двійковий файл, а потім видаляєте файли tgz та вихідні файли наприкінці, ви дійсно хочете, щоб це все було зроблено в один шар, щоб зменшити розмір зображення.
Далі я особисто розбила шари, виходячи з їх потенціалу для повторного використання інших зображень та очікуваного використання кешування. Якщо у мене є 4 зображення, всі з однаковим базовим зображенням (наприклад, debian), я можу перенести колекцію загальних утиліт для більшості цих зображень у команду першого запуску, щоб інші зображення виграли від кешування.
Порядок в Dockerfile важливий при повторному використанні кешу зображень. Я переглядаю будь-які компоненти, які оновлюватимуться дуже рідко, можливо лише тоді, коли базове зображення оновлюється та розміщуватиметься високо в Dockerfile. Під кінець Dockerfile я включаю будь-які команди, які запускаються швидко і можуть часто змінюватися, наприклад, додавання користувача із специфічним для UID користувачам або створення папок та зміна дозволів. Якщо контейнер включає інтерпретований код (наприклад, JavaScript), який активно розробляється, він додається якомога пізніше, щоб відбудова виконувала лише одну зміну.
У кожній із цих груп змін я консолідую якнайкраще, щоб мінімізувати шари. Отже, якщо є 4 різні папки вихідного коду, вони розміщуються всередині однієї папки, щоб вона могла бути додана однією командою. Будь-яка установка пакету з чогось типу apt-get об'єднується в єдиний RUN, коли це можливо, щоб мінімізувати кількість накладних витрат менеджера пакунків (оновлення та очищення).
Оновлення для багатоетапних збірок:
Я набагато менше хвилююся щодо зменшення розміру зображення на не завершальних етапах багатоетапної збірки. Коли ці етапи не позначені тегами та не надсилаються до інших вузлів, ви можете збільшити ймовірність повторного використання кешу, розділивши кожну команду на окремий RUN
рядок.
Однак це не ідеальне рішення для розшарування шарів, оскільки все, що ви копіюєте між етапами, - це файли, а не решта метаданих зображень, як-от налаштування змінної середовища, точка входу та команда. І коли ви встановлюєте пакети в дистрибутиві Linux, бібліотеки та інші залежності можуть бути розкидані по всій файловій системі, що ускладнює копіювання всіх залежностей.
Через це я використовую багатоетапні збірки як заміну для створення бінарних файлів на сервері CI / CD, так що мій сервер CI / CD повинен мати лише інструменти для запуску docker build
, а не мати jdk, nodejs, go і будь-який інший встановлений інструмент компіляції.