Как каждый раз создавать самый маленький рабочий образ докера?

19

Цель: каждый раз создавать самые маленькие рабочие изображения докеров

Текущий

REPOSITORY          TAG       IMAGE ID            CREATED             SIZE
a-docker-image      latest    x                   42 minutes ago       1.92 GB

попытка

Добавление шага очистки в конце Dockerfile:

#clean
RUN apt-get purge -y wget
RUN rm -r a-build-dir
RUN apt-get purge -y a-package

немного уменьшил размер изображения:

REPOSITORY          TAG       IMAGE ID            CREATED             SIZE
a-docker-image      latest    y                   2 minutes ago       1.86 GB

обсуждение

Я построил различные образы докеров. Каждый раз, когда я пытаюсь уменьшить размер создаваемого изображения, я всегда чувствую, что он слишком велик. Я ищу сценарий, который уже был создан кем-то на github, который удаляет все лишние пакеты из изображения, поэтому размер создаваемого изображения будет как можно меньше.

Как я уже говорил, я всегда стараюсь уменьшить размер изображения, но я хочу применить это согласованно, чтобы каждое изображение, которое я создаю с этого момента, было как можно меньше.

Вопрос

Как каждый раз создавать самый маленький рабочий образ докера?

030
источник

Ответы:

1

Здесь задействовано множество методов без единого решения. Вы, вероятно, захотите сделать несколько из следующих действий:


Во-первых, оптимизируйте слои изображения для повторного использования. Позже в 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 ГБ.

BMitch
источник
25

A Dockerfileсоздает новый слой для каждой из команд в файле. Поскольку слои хорошо, слоистый друг на друг - вы не можете удалить файлы , которые добавили предыдущий слой. Вот почему, когда вы устанавливаете пакеты, или скачиваете файлы, или создаете сборки, каждая из которых создается отдельной командой - они все еще присутствуют в образе, даже если вы удалили их в будущем слое.

Так что, если вы просто измените это:

RUN apt-get update -y
RUN apt-get install -y wget a-package
# ...
RUN apt-get purge -y wget
RUN rm -r a-build-dir
RUN apt-get purge -y a-package

К этому:

RUN apt-get update -y \
    && apt-get install -y wget a-package \
    && mkdir a-build-dir \
    && wget http://some-site/very-big-source-code.tar.gz \
    && tar xzvf very-big-source-code.tar.gz \
    && do-some-compilation \
    && apt-get purge -y wget \
    && cd .. \
    && rm -rf a-build-dir \
    && apt-get purge -y a-package

Вы получите намного меньшее изображение.


Другим вариантом является сжатие изображения после того, как вы его построили. Q: Как работает новая docker --squash?


Еще один вариант, это выбрать тонкий базовый образ. Например, образы, которые используют Alpine Linux в качестве своей базы вместо Debian, занимают всего 10-15 МБ вместо 180-250 МБ. И это до добавления вашего собственного приложения и данных. Многие официальные базовые образы на Docker Hub имеют альпийскую версию.

Евгений
источник
3
2.37vs.1.47 GB
030
4

Вероятно, не совсем ответ, но стоит дать альтернативы.

Среда обитания шеф-повара была создана с учетом этого, создавая пакет со всеми необходимыми зависимостями без посторонней загрузки дистрибутивного / базового образа, которую вы не желаете.

Извлекаем информацию о размере контейнера из этого поста в блоге с помощью простого приложения nodejs:

michael@ricardo-2:plans_pkg_part_2$ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
mfdii/node-example   latest              36c6568c606b        40 minutes ago      655.9 MB
node                 latest              04c0ca2a8dad        16 hours ago        654.6 MB
mfdii/mytutorialapp  latest              534afd80d74d        2 minutes ago       182.1 MB

mdfii/node-exampleэто изображение докера из классического файла dockerfile, в то время mfdii/mytutorialappкак изображение докера создается с помощью среды обитания.

Если ваш основной интерес - размер, и вы готовы взять курс обучения на планы Хабитат, это может быть решением для вас.

Tensibai
источник
0

Можно также использовать погружение

docker run --rm -it \
    -v /var/run/docker.sock:/var/run/docker.sock \
    wagoodman/dive:latest <dive arguments...>

получить отчет о том, какие отходы можно удалить из образа докера, чтобы уменьшить его размер.

030
источник
0

Если вы хотите иметь многократно используемые уровни разработки, но уменьшить использование диска для доставки, вы можете создать объединенный «уровень доставки», например:

  1. Убедитесь, что у вас есть контейнер, который использует ваше изображение (если у вас его нет, возможно, используйте что-то вроде docker run IMAGE echo, если доступна команда echo)
  2. Найти идентификатор контейнера (возможно, используя docker container ls -l)
  3. Труба, docker exportчтобы docker importсоздать объединенный слой (что-то вроде docker export 20f192c6530a | docker import - project:merged)

Это будет держать ваши уровни разработки вокруг, но даст вам уменьшенное объединенное изображение, которое вы можете предоставить.

Билл Бурдик
источник
0

Многоступенчатые сборки. Используйте образ со всеми вашими компонентами сборки для создания приложения и более легкий образ времени выполнения. Скопируйте только свой артефакт сборки в образ среды выполнения. Не нужно ничего удалять.

https://docs.docker.com/develop/develop-images/multistage-build/

frankd
источник
0

простой .. Докер PS проверить текущие запущенные образы .. для простого примера файла ниже ..

ОТ Ubuntu16

MAINTAINER sreeni (электронная почта / домен)

RUN apt-get update

RUN apt-get install -y nginx

ENTRYPOINT [«/ usr / sbin / nginx», «- g», «демон выключен;»]

EXPOSE 80 (порт)

простой файл докера ...

используйте ниже команду docker

docker run -d -p 80:80 - name веб-сервер ubuntu16 (имя образа), после этого проверьте localhost или ip address: 80 (откройте браузер и проверьте)

sreeniwl
источник
1
пожалуйста, исправьте формат вашего ответа ...
Pierre.Vriens