Існує безліч методів, без єдиного рішення. Ви, ймовірно, захочете виконати декілька наступних дій:
Спочатку оптимізуйте шари зображень для повторного використання. Надайте часто змінюються кроки пізніше в Dockerfile, щоб збільшити шанси на те, що ранні шари кешуються з попередніх збірок. Повторно використаний шар відображатиметься як більше дискового простору в а docker image ls
, але якщо вивчити базову файлову систему, на диску завжди зберігається лише одна копія кожного шару. Це означає, що 3 зображення по 2 ГБ кожен, але у яких лише 50 Мбайт різних за останні кілька шарів збірки, займуть лише 2,1 ГБ дискового простору, навіть якщо в списку видно, що вони використовують 6 ГБ, оскільки ви подвійний підрахунок кожного з повторно використаних шарів.
Повторне використання шару - це те, чому ви бачите зображення з нечасто мінливими залежностями збірки, які встановлюються першими перед копіюванням у код. Дивіться будь-який приклад python, який має такий зразок:
FROM python
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
# note how the code is copied only after the pip install
# since code changes but requirements.txt doesn't
COPY . .
CMD ["gunicorn", "app:app"]
Виберіть мінімальне базове зображення. Ось чому ви бачите людей перейти від ubuntu
до debian:slim
(в тонкі варіанти менше, поставляються з меншою кількістю інструментів), або навіть alpine
. Це зменшує розмір початкової точки і дуже корисно, якщо ви постійно перетягуєте нові версії базового зображення. Однак якщо ваше базове зображення рідко змінюється, повторне використання шарів видаляє більшу частину переваги мінімального базового зображення.
Найменше базове зображення, яке ви можете вибрати, - scratch
це нічого, не оболонка чи бібліотеки, і корисне лише зі статично складеними бінарними файлами. В іншому випадку виберіть базове зображення, яке включає необхідні інструменти без безлічі інструментів, які вам не потрібні.
Далі будь-який крок, який змінює або видаляє файл, повинен поєднуватися з попередніми кроками, які створюють цей файл. Інакше шарувата файлова система, яка використовує копіювання під час запису навіть у таких речах, як зміна дозволу на файл, матиме вихідний файл у попередньому шарі, і розмір зображення не зменшиться при видаленні файлів. Ось чому ваші rm
команди не впливають на отриманий дисковий простір. Натомість ви можете ланцюжок команд, наприклад:
RUN apt-get update \
&& apt-get install -y \
a-package \
wget \
&& ... \
&& apt-get purge -y wget \
&& rm -r a-build-dir \
&& apt-get purge -y a-package
Зауважте, що надмірне використання командних ланцюгів може уповільнити ваші збірки, оскільки вам потрібно буде перевстановити один і той же набір інструментів будь-коли, коли необхідні умови (наприклад, код, який витягується за допомогою wget). Дивіться багатоетапність нижче для кращої альтернативи.
Будь-який створений вами файл, який не потрібен у вашому отриманому зображенні, слід видалити на кроці, який його створює. Сюди входять кеші пакетів, журнали, вказівні сторінки тощо. Щоб дізнатись, які файли створюються в кожному шарі, ви можете використовувати такий інструмент, як wagoodman / dive (якого я особисто не перевіряв і висловлював би обережність, оскільки він працює з повним кореневим доступом на своєму хості), або ви можете створювати свої зображення докера, не обрізаючи проміжні контейнери, а потім переглядати відмінності з:
# first create and leave containers from any RUN step using options on build
docker image build --rm=false --no-cache -t image_name .
# review which layers use an unexpectedly large amount of space
docker image history image_name
# list all containers, particularly the exited ones from above
docker container ps -a
# examine any of those containers
docker container diff ${container_id}
# ... repeat the diff for other build steps
# then cleanup exited containers
docker container prune
З кожним з цих проміжних контейнерів, то різниця покаже , що додані файли, змінені або видалені в цій стадії (вони помічені за допомогою A
, C
або D
перед кожним ім'ям файлу). Що відрізняється, це специфічна для контейнера файлова система читання / запису, яка є будь-яким файлом, зміненим контейнером із стану зображення, використовуючи функцію копіювання на запис.
Найкращий спосіб зменшити розмір зображення - це усунення непотрібних компонентів, як-от компіляторів, із завантаженого зображення. Для цього багатоступеневі збірки дозволяють компілювати на одному етапі, а потім копіювати лише отримані артефакти зі стадії збірки на зображення часу виконання, яке має лише мінімальний необхідний для запуску програми. Це дозволяє уникнути необхідності оптимізації будь-якого з етапів збирання, оскільки вони не постачаються із отриманим зображенням.
FROM debian:9 as build
# still chain update with install to prevent stale cache issues
RUN apt-get update \
&& apt-get install -y \
a-package \
wget \
RUN ... # perform any download/compile steps
FROM debian:9-slim as release
COPY --from=build /usr/local/bin/app /usr/local/bin/app
CMD [ "/usr/local/bin/app" ]
Багатоетапність ідеально підходить зі статично складеними бінарними файлами, які можна запускати з подряпиною як базове зображення або переходити з середовища компіляції, наприклад, JDK, до часу виконання, як JRE. Це найпростіший спосіб різко зменшити розмір зображення, незважаючи на швидке нарощування. Ви все ще можете виконати ланцюжок кроків на етапі випуску, якщо у вас є кроки, які змінюють або видаляють файли, створені на попередніх етапах, але здебільшого, COPY
з іншої стадії ізолює етап випуску від будь-якого прошарку шару, що виник на попередніх етапах збірки.
Зауважте, я не рекомендую знімати зображення, оскільки це зменшує розмір одного зображення за рахунок усунення повторного використання шару. Це означає, що майбутні збірки одного зображення вимагатимуть більше дискового та мережевого трафіку для надсилання оновлень. Щоб повернутися до першого прикладу, сквашина може зменшити ваше зображення з 2 ГБ до 1 ГБ, але не три зображення можуть займати 3 Гб замість 2,1 ГБ.
2.37
vs.1.47 GB