По возможности я всегда объединяю команды, создающие файлы, с командами, удаляющими те же файлы, в одну RUN
строку. Это связано с тем, что каждая RUN
строка добавляет слой к изображению, вывод - это буквально изменения файловой системы, которые вы можете просматривать docker diff
во временном контейнере, который она создает. Если вы удаляете файл, который был создан на другом уровне, все, что делает файловая система union, - это регистрирует изменение файловой системы на новом уровне, файл все еще существует на предыдущем уровне, доставляется по сети и сохраняется на диске. Поэтому, если вы загружаете исходный код, извлекаете его, компилируете в двоичный файл, а затем удаляете tgz и исходные файлы в конце, вы действительно хотите, чтобы все это было сделано в одном слое, чтобы уменьшить размер изображения.
Затем я лично разделил слои на основе их потенциала для повторного использования в других изображениях и ожидаемого использования кеширования. Если у меня есть 4 образа, все с одним и тем же базовым образом (например, debian), я могу вытащить набор общих утилит для большинства этих образов в команду первого запуска, чтобы другие образы выиграли от кэширования.
Порядок в Dockerfile важен при рассмотрении повторного использования кэша изображений. Я смотрю на любые компоненты, которые будут обновляться очень редко, возможно, только когда обновится базовый образ и поместит их наверху в Dockerfile. Ближе к концу Dockerfile я включаю любые команды, которые будут выполняться быстро и могут часто меняться, например, добавление пользователя с определенным UID хоста или создание папок и изменение разрешений. Если контейнер включает интерпретируемый код (например, JavaScript), который активно разрабатывается, он добавляется как можно позже, так что при перестроении выполняется только это единственное изменение.
В каждой из этих групп изменений я максимально консолидирую их, чтобы минимизировать слои. Итак, если есть 4 разных папки с исходным кодом, они помещаются в одну папку, поэтому ее можно добавить с помощью одной команды. Любые пакеты, устанавливаемые из чего-то вроде apt-get, по возможности объединяются в один RUN, чтобы свести к минимуму накладные расходы менеджера пакетов (обновление и очистка).
Обновление для многоэтапных сборок:
Я гораздо меньше беспокоюсь об уменьшении размера изображения на незавершенных этапах многоступенчатой сборки. Если эти этапы не помечены и не отправлены на другие узлы, вы можете максимизировать вероятность повторного использования кеша, разделив каждую команду на отдельную RUN
строку.
Однако это не идеальное решение для сжатия слоев, поскольку все, что вы копируете между этапами, - это файлы, а не остальные метаданные изображения, такие как настройки переменных среды, точки входа и команды. И когда вы устанавливаете пакеты в дистрибутиве Linux, библиотеки и другие зависимости могут быть разбросаны по файловой системе, что затрудняет копирование всех зависимостей.
Из-за этого я использую многоступенчатые сборки в качестве замены для сборки двоичных файлов на сервере CI / CD, так что моему серверу CI / CD требуется только инструмент для запуска docker build
, а не jdk, nodejs, go и любые другие установленные инструменты компиляции.