Здесь задействовано множество методов без единого решения. Вы, вероятно, захотите сделать несколько из следующих действий:
Во-первых, оптимизируйте слои изображения для повторного использования. Позже в 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 (который я лично не проверял, и хотел бы выразить осторожность, так как он работает с полным доступом root). на вашем хосте), или вы можете создавать образы докеров, не обрезая промежуточные контейнеры, а затем просматривать различия с помощью:
# 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
перед каждым именем файла). То, что показывает diff, - это файловая система чтения / записи, специфичная для контейнера, которая представляет собой любой файл, измененный контейнером из состояния изображения с использованием функции копирования при записи.
Лучший способ уменьшить размер изображения - исключить ненужные компоненты, такие как компиляторы, из вашего поставляемого изображения. Для этого многоэтапные сборки позволяют выполнить компиляцию за один этап, а затем скопировать только полученные артефакты из этапа сборки в образ среды выполнения, в котором есть только минимум, необходимый для запуска приложения. Это позволяет избежать необходимости оптимизировать любые этапы сборки, поскольку они не поставляются с полученным изображением.
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 изображения могут занимать 3 ГБ вместо 2,1 ГБ.
2.37
vs.1.47 GB