Dockerfile

Теги: docker 

Докер билдит контейнер автоматически с помощью. docker build, получая инструкции из Dockerfile и используя context.

Контекст сборки — это набор файлов в указанном месте PATH или URL. PATH — это каталог в вашей локальной файловой системе. URL-адрес — это расположение репозитория Git. Контекст сборки обрабатывается рекурсивно. Таким образом, PATH включает любые подкаталоги, а URL-адрес включает репозиторий и его подмодули.

Сборка выполняется демоном Docker, а не интерфейсом командной строки. Первое, что делает процесс сборки, это отправляет весь контекст (рекурсивно) демону. В большинстве случаев лучше начать с пустого каталога в качестве контекста и хранить файл Dockerfile в этом каталоге. Добавьте только те файлы, которые необходимы для создания Dockerfile. Не используйте свой корневой каталог / в качестве PATH для вашего контекста сборки, так как это приводит к тому, что сборка передает все содержимое вашего жесткого диска демону Docker.

Можно использовать флаг -f, чтобы указать на Dockerfile в любом месте вашей файловой системы.

docker build -f /path/to/a/Dockerfile .

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

docker build -t shykes/myapp .

# for multyple repositories
docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .

Перед запуском инструкции докер валидирует Dockerfile и поднимает ошибку, если синтаксис некорректен. Инструкции Dockerfile выполняются демоном одна за одной. Каждая инструкция запускается независимо и вызывает создание нового образа, поэтому RUN cd /tmp не окажет никакого влияния на следующие инструкции.

Когда это возможно, Docker использует кэш сборки, чтобы значительно ускорить процесс сборки Docker. На это указывает сообщение CACHED в выводе консоли. Опция --cache-from также позволяет вам использовать кеш сборки, который распространяется через реестр образов.

Buildkit

BuildKit может:

  • Обнаруживать и пропускать выполнение неиспользуемых этапов сборки
  • Параллелизовать этапы сборки, не зависящие от сборки
  • Поэтапно передавать только измененные файлы в контексте сборки между сборками
  • Обнаруживать и пропускать передачу неиспользуемых файлов в контексте сборки
  • Использовать внешние реализации Dockerfile со многими новыми функциями
  • Избегать побочных эффектов (промежуточные образы и контейнеры)
  • Устанавливать приоритет кэша сборки для автоматической обрезки.

BuildKit включается установкой DOCKER_BUILDKIT=1 через CLI перед выполнением docker build .

С Buildkit можно использовать syntax. Директива синтаксиса определяет расположение синтаксиса Dockerfile, который используется для создания Dockerfile.

Пользовательские реализации Dockerfile позволяют:

  • Автоматически получать исправления ошибок без обновления демона Docker
  • Быть уверенным, что все пользователи используют одну и ту же реализацию для создания вашего Dockerfil
  • Использовать новейшие функции без обновления демона Docker
  • Тестировать новые функции или сторонние функции до их интеграции в демоне Docker.
  • Использовать альтернативные определения сборки или создайте свои собственные.

Формат Dockerfile

  • Инструкции не чувствительна к регистру, при этом есть соглашение писать прописными
  • Dockerfile должен начинаться с инструкции FROM (это может быть после директив синтаксического анализатора, комментариев и глобальных ARG.)
  • коментарии начинаются с #, если это не валидные директивы дял парсера
  • директивы парсера не обязательны и влияют на то как обрабатываются последующие строки. Пишутся как комментарии специального типа в виде # directive=value. Директивы не отображаются при сборке и одна директива используется только однажды. После обработки комментария, пустой строки или инструкции по сборщику Docker больше не ищет директивы синтаксического анализатора, поэтому они должны быть как можно выше в Dockerfile. Директивы не чувствительны к регистру.

      # directive=value1
      # directive=value2
    
      FROM ImageName
    
  • escape директива устанавливает символ, используемый для экранирования символов в Dockerfile. Если он не указан, управляющим символом по умолчанию является .

Использование переменных окружения

Переменные, заданные в инструкции ENV могут использоваться в некоторых инструкциях Dockerfile. Синтаксис: $variable_name или ${variable_name}. Кроме того, поддерживается некоторое количество модификаторов bash:

  • ${variable:-word} указывает, что если переменная установлена, результатом будет это значение. Если переменная не установлена, результатом будет word.
  • ${variable:+word} указывает, что если переменная установлена, результатом будет word, в противном случае результатом будет пустая строка.

Word может быть любой строкой, включающей в т.ч. переменные. Эскейпинг реализуется через \

FROM busybox
ENV FOO=/bar
WORKDIR ${FOO}   # WORKDIR /bar
ADD . $FOO       # ADD . /bar
COPY \$FOO /quux # COPY $FOO /quux

Переменные поддерживаются в след.инструкциях:

  • ADD
  • COPY
  • ENV
  • EXPOSE
  • FROM
  • LABEL
  • STOPSIGNAL
  • USER
  • VOLUME
  • WORKDIR
  • ONBUILD

.dockerignore file

.dockerignore просматривается перед отправкой контекста дкмону. Влияет на инструкции ADD и COPY

Rule Behavior
# comment Ignored
/temp Exclude files and directories whose names start with temp in any immediate subdirectory of the root. For example, the plain file /somedir/temporary.txt is excluded, as is the directory /somedir/temp.
//temp* Exclude files and directories starting with temp from any subdirectory that is two levels below the root. For example, /somedir/subdir/temporary.txt is excluded.
temp? Exclude files and directories in the root directory whose names are a one-character extension of temp. For example, /tempa and /tempb are excluded.

Больше подробностей

Инеструкции

FROM

FROM [--platform=<platform>] <image> [AS <name>]

# or
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]

# or
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

Инструкция FROM инициализирует новую стадию сборки и устанавливает базовый образ для последующих инструкций. Таким образом, действительный файл Dockerfile должен начинаться с инструкции FROM.

  • Перед FROM можно использовать только инструкцию ARG
  • FROM может появляться несколько раз в одном Dockerfile для создания нескольких образов или использования одного этапа сборки в качестве зависимости для другого. Просто запишите идентификатор последнего изображения, выведенный фиксацией, перед каждой новой инструкцией FROM. Каждая инструкция FROM очищает любое состояние, созданное предыдущими инструкциями.
  • При желании можно дать имя новому этапу сборки, добавив имя AS в инструкцию FROM. Это имя можно использовать в последующих инструкциях FROM и COPY --from=<name> для ссылки на образ, созданный на этом этапе.
  • Значения тега или дайджеста являются необязательными. Если вы опустите любой из них, построитель по умолчанию примет последний тег. Билдер возвращает ошибку, если не может найти значение тега.
  • Необязательный флаг --platform можно использовать для указания платформы образа в случае, если FROM ссылается на многоплатформенный образ

ARG, объявленная перед FROM, находится вне этапа сборки, поэтому ее нельзя использовать ни в одной инструкции после FROM. Чтобы использовать значение по умолчанию ARG, объявленное перед первым FROM, используйте инструкцию ARG без значения внутри этапа сборки

ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version

RUN

Два формата:

  • RUN <command> (форма оболочки, команда запускается в оболочке, которая по умолчанию /bin/sh -c в Linux или cmd /S /C в Windows)
  • RUN ["executable", "param1", "param2"] (exec form)

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

Форма exec позволяет выполнять команды с использованием базового образа, который не содержит указанный исполняемый файл оболочки. Дефолтный shell можно задать через команду SHELL

RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'

# or
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'

Через exec можно задать другую оболочку

RUN ["/bin/bash", "-c", "echo hello"]

Форма exec анализируется как массив JSON, что означает, что вы должны использовать двойные кавычки (“) вокруг слов, а не одинарные кавычки (‘).

В отличие от shell формы, форма exec не вызывает командную оболочку. Это означает, что нормальной обработки оболочки не происходит. Например, RUN [ "echo", "$HOME" ] не будет выполнять подстановку переменных в $HOME. Если вам нужна обработка оболочки, то либо используйте форму оболочки, либо запустите оболочку напрямую, например: RUN [ "sh", "-c", "echo $HOME" ]. При использовании формы exec и непосредственном выполнении оболочки, как и в случае с формой оболочки, расширение переменной среды выполняет оболочка, а не docker.

Кэш для инструкций RUN не становится недействительным автоматически во время следующей сборки. Кэш для такой инструкции, как RUN apt-get dist-upgrade -y, будет повторно использован во время следующей сборки. Кэш для инструкций RUN можно сделать недействительным с помощью флага --no-cache, например, docker build --no-cache.

CMD

Три формы:

  • CMD ["executable","param1","param2"] (exec form, this is the preferred form)
  • CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
  • CMD command param1 param2 (shell form)

В Dockerfile может быть только одна инструкция CMD. Если вы укажете более одного CMD, вступит в силу только последний CMD.

Основная цель CMD — предоставить значения по умолчанию для исполнения контейнера. Эти значения по умолчанию могут включать исполняемый файл или исключать исполняемый файл, и в этом случае вы также должны указать инструкцию ENTRYPOINT. Обе инструкции длолжны быть заданы с учетом формата json.

В отличие от shell формы, форма exec не вызывает командную оболочку. Это означает, что нормальной обработки оболочки не происходит.

Не путайте RUN с CMD. RUN фактически запускает команду и фиксирует результат; CMD ничего не выполняет во время сборки, но указывает запланированную команду для образа.

LABEL

LABEL <key>=<value> <key>=<value> <key>=<value> ...

Инструкция LABEL добавляет к образу метаданные. LABEL — это пара ключ-значение. Чтобы включить пробелы в значение LABEL, используйте кавычки и обратную косую черту, как при синтаксическом анализе командной строки. Можно задать любое количество меток для образа.

LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."

LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"

Метки родителей наследуются. Если метка уже есть - используется последняя.

MAINTAINER (deprecated)

EXPOSE

EXPOSE <port> [<port>/<protocol>...]

Открывает порты между контейнерами в сети в рантайм. Можно задать TCP или UDP.

Инструкция EXPOSE фактически не публикует порт. Он функционирует как тип документации между человеком, который создает образ, и человеком, который запускает контейнер, о том, какие порты предназначены для публикации. Чтобы фактически опубликовать порт при запуске контейнера, используйте флаг -p при запуске docker, чтобы опубликовать и сопоставить один или несколько портов, или флаг -P, чтобы опубликовать все открытые порты и сопоставить их с внешними портами.

ENV

ENV <key>=<value> ...

Инструкция ENV устанавливает для переменной среды <key> значение <value>. Это значение будет в среде исполнения для всех последующих инструкций на этапе сборки и во многих случаях может быть заменено. Значение будет интерпретировано, поэтому символы кавычек будут удалены, если они не экранированы. Как и при синтаксическом анализе командной строки, для включения пробелов в значения можно использовать кавычки и обратную косую черту.

ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffy

ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \
    MY_CAT=fluffy

Переменные среды, установленные с помощью ENV, будут сохраняться при запуске контейнера из полученного образа.

Если переменные нужны только при сборке - рассмотирте вариант установления их только для исполняемой команды

RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y ...

# or
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y ...

ADD

Две формы:

ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]

Последняя форма требуется для путей, содержащих пробелы.

Инструкция ADD копирует новые файлы, каталоги или URL-адреса удаленных файлов из <src> и добавляет их в файловую систему образа по пути <dest>. Можно указать несколько ресурсов <src>, но если они являются файлами или каталогами, их пути интерпретируются как относительные к источнику контекста сборки. Каждый <src> может содержать подстановочные знаки, и сопоставление будет выполняться с использованием правил Go’s filepath.Match.

<dest> — это абсолютный путь или путь относительно WORKDIR, в который будет скопирован источник внутри целевого контейнера.

Пример для <WORKDIR>/relativeDir/:

ADD test.txt relativeDir/

Пример для /absoluteDir/:

ADD test.txt /absoluteDir/

Все новые файлы и каталоги создаются с UID и GID, равными 0, если только необязательный флаг --chown не указывает данное имя пользователя, имя группы или комбинацию UID/GID для запроса конкретного владельца добавленного контента.

ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/

Правила для ADD:

  • Путь <src> должен находиться внутри контекста сборки; вы не можете ДОБАВИТЬ ../something/something, потому что первым шагом сборки docker является отправка каталога контекста (и подкаталогов) демону docker.
  • Если <src> является URL-адресом, а <dest> не заканчивается косой чертой, то файл загружается с URL-адреса и копируется в <dest>.
  • Если <src> является URL-адресом, а <dest> заканчивается косой чертой, то имя файла выводится из URL-адреса, и файл загружается в <dest>/<filename>. Например, ADD http://example.com/foobar/ создаст файл /foobar. URL-адрес должен иметь нетривиальный путь, чтобы в этом случае можно было обнаружить подходящее имя файла (http://example.com не будет работать).
  • Если <src> является каталогом, копируется все содержимое каталога, включая метаданные файловой системы. Сам каталог не копируется.
  • Если <src> является локальным tar-архивом в распознаваемом формате сжатия (identity, gzip, bzip2 или xz), то он распаковывается как каталог. Ресурсы с удаленных URL-адресов не распаковываются. Когда каталог копируется или распаковывается, он ведет себя так же, как tar -x.
  • Если <src> является файлом любого другого типа, он копируется отдельно вместе со своими метаданными. В этом случае, если <dest> заканчивается косой чертой /, он будет считаться каталогом, а содержимое <src> будет записано в <dest>/base(<src>).
  • Если указано несколько ресурсов <src>, либо напрямую, либо из-за использования подстановочного знака, то <dest> должен быть каталогом, и он должен заканчиваться косой чертой /.
  • Если <dest> не заканчивается косой чертой, он будет считаться обычным файлом, а содержимое <src> будет записано в <dest>.
  • Если <dest> не существует, он создается вместе со всеми отсутствующими каталогами на его пути.

COPY

Две формы

COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]

Последняя форма требуется для путей, содержащих пробелы. --chown поддерживается только для Linux.

Инструкция COPY копирует новые файлы или каталоги из <src> и добавляет их в файловую систему контейнера по пути <dest>.

При желании COPY принимает флаг --from=<name>, который можно использовать для установки исходного местоположения на предыдущую стадию сборки (созданную с помощью FROM .. AS <name>), которая будет использоваться вместо контекста сборки, отправленного пользователем. В случае, если этап сборки с указанным именем не может быть найден, вместо него делается попытка использовать образ с таким же именем.

Правила:

  • Путь <src> должен находиться внутри контекста сборки; вы не можете Ккопировать ../something/something, потому что первым шагом сборки docker является отправка каталога контекста (и подкаталогов) демону docker.
  • Если <src> является каталогом, копируется все содержимое каталога, включая метаданные файловой системы. Сам каталог не копируется, только его содержимое.
  • Если <src> является файлом любого другого типа, он копируется отдельно вместе со своими метаданными. В этом случае, если <dest> заканчивается косой чертой /, он будет считаться каталогом, а содержимое <src> будет записано в <dest>/base(<src>).
  • Если указано несколько ресурсов <src>, либо напрямую, либо из-за использования подстановочного знака, то <dest> должен быть каталогом, и он должен заканчиваться косой чертой /.
  • Если <dest> не заканчивается косой чертой, он будет считаться обычным файлом, а содержимое <src> будет записано в <dest>.
  • Если <dest> не существует, он создается вместе со всеми отсутствующими каталогами на его пути.

Разница между ADD и COPY в том, что ADD умеет скачивать удаленные файлы и умеет разворачимвать архивы.

ENTRYPOINT

Две формы:

# exec
ENTRYPOINT ["executable", "param1", "param2"]

# shell
ENTRYPOINT command param1 param2

ENTRYPOINT позволяет настроить контейнер, который будет работать как исполняемый файл. Например, следующий код запускает nginx с содержимым по умолчанию, прослушивая порт 80:

docker run -i -t --rm -p 80:80 nginx

Аргументы командной строки для запуска docker run <image> будут добавлены после всех элементов в форме exec ENTRYPOINT и переопределят все элементы, указанные с помощью CMD. Это позволяет передавать аргументы в точку входа, т.е. docker run <image> -d передаст аргумент -d в точку входа. Вы можете переопределить инструкцию ENTRYPOINT, используя флаг docker run --entrypoint.

Shell форма предотвращает использование любых аргументов командной строки CMD или команд запуска, но имеет тот недостаток, что ваша ENTRYPOINT будет запущена как подкоманда /bin/sh -c, которая не передает сигналы. Это означает, что исполняемый файл не будет иметь PID 1 контейнера и не будет получать сигналы Unix, поэтому ваш исполняемый файл не получит SIGTERM от docker stop <container>.

Всегда применяется только последняя инструкция ENTRYPOINT

Вы можете использовать форму exec ENTRYPOINT, чтобы установить довольно стабильные команды и аргументы по умолчанию, а затем использовать любую форму CMD, чтобы установить дополнительные значения по умолчанию, которые, скорее всего, будут изменены. Подробнее

Вы можете указать простую строку для ENTRYPOINT, и она будет выполняться в /bin/sh -c. Эта форма будет использовать обработку оболочки для замены переменных среды оболочки и игнорировать любые аргументы командной строки CMD или docker run. Чтобы гарантировать, что docker stop будет правильно сигнализировать о любом длительно работающем исполняемом файле ENTRYPOINT, вам нужно не забыть запустить его с помощью exec. Пример

И инструкции CMD, и ENTRYPOINT определяют, какая команда будет выполняться при запуске контейнера. Есть несколько правил, описывающих их взаимодействие:

  • Dockerfile должен указывать хотя бы одну из команд CMD или ENTRYPOINT.
  • ENTRYPOINT должен быть определен при использовании контейнера в качестве исполняемого файла.
  • CMD следует использовать как способ определения аргументов по умолчанию для команды ENTRYPOINT или для выполнения специальной команды в контейнере.
  • CMD будет переопределен при запуске контейнера с альтернативными аргументами.

В таблице ниже показано, какая команда выполняется для разных комбинаций ENTRYPOINT/CMD:

  No ENTRYPOINT ENTRYPOINT exec_entry p1_entry ENTRYPOINT [“exec_entry”, “p1_entry”]
No CMD error, not allowed /bin/sh -c exec_entry p1_entry exec_entry p1_entry
CMD [“exec_cmd”, “p1_cmd”] exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry exec_cmd p1_cmd
CMD [“p1_cmd”, “p2_cmd”] p1_cmd p2_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry p1_cmd p2_cmd
CMD exec_cmd p1_cmd /bin/sh -c exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd

VOLUME

VOLUME ["/data"]

Инструкция VOLUME создает точку монтирования с указанным именем и помечает ее как содержащую внешне смонтированные тома из собственного хоста или других контейнеров. Значение может быть массивом JSON, VOLUME ["/var/log/"] или простой строкой с несколькими аргументами, например VOLUME /var/log или VOLUME /var/log /var/db. Use volumes

Команда docker run инициализирует только что созданный том любыми данными, которые существуют в указанном месте в базовом образе.

  • Тома в контейнерах на базе Windows: при использовании контейнеров на базе Windows место назначения тома внутри контейнера должно быть одним из: несуществующий или пустой каталог или диск, отличный от C:
  • Изменение тома из Dockerfile: если какие-либо шаги сборки изменят данные внутри тома после его объявления, эти изменения будут отменены.
  • Форматирование JSON: список анализируется как массив JSON. Вы должны заключать слова в двойные кавычки (“”), а не в одинарные кавычки (‘).
  • Каталог хоста объявляется во время выполнения контейнера: каталог хоста (точка монтирования) по своей природе зависит от хоста. Это делается для сохранения переносимости образа, поскольку нельзя гарантировать, что данный каталог хоста будет доступен на всех хостах. По этой причине вы не можете смонтировать каталог хоста из Dockerfile. Инструкция VOLUME не поддерживает указание параметра host-dir. Вы должны указать точку монтирования при создании или запуске контейнера.

USER

USER <user>[:<group>]

# or
USER <UID>[:<GID>]

Инструкция USER устанавливает имя пользователя (или UID) и, при необходимости, группу пользователей (или GID) для использования при запуске образа и для любых инструкций RUN, CMD и ENTRYPOINT, которые следуют за ней в Dockerfile.

WORKDIR

WORKDIR /path/to/workdir

Инструкция WORKDIR устанавливает рабочий каталог для любых инструкций RUN, CMD, ENTRYPOINT, COPY и ADD, которые следуют за ней в Dockerfile. Если WORKDIR не существует, он будет создан, даже если он не используется ни в одной последующей инструкции Dockerfile.

Инструкцию WORKDIR можно использовать несколько раз в Dockerfile. Если указан относительный путь, он будет относиться к пути предыдущей инструкции WORKDIR.

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

WORKDIR поддерживает переменные

ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

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

ARG

ARG <name>[=<default value>]

Инструкция ARG определяет переменную, которую пользователи могут передать во время сборки сборщику с помощью команды сборки docker, используя флаг --build-arg <имя_переменной>=<значение>. Если пользователь указывает аргумент сборки, который не был определен в Dockerfile, сборка выводит предупреждение. Не рекомендуется передавать секретные даныне через аргументы.

FROM busybox
ARG user1
ARG buildno
# ...

Можно присваивать дефолтные значения

FROM busybox
ARG user1=someuser
ARG buildno=1
# ...

Определение переменной ARG вступает в силу из строки, в которой оно определено в Dockerfile, а не из-за использования аргумента в командной строке или где-либо еще. Инструкция ARG выходит из области видимости в конце этапа сборки, на котором она была определена. Чтобы использовать аргумент на нескольких этапах, каждый этап должен включать инструкцию ARG.

FROM busybox
ARG SETTINGS
RUN ./run/setup $SETTINGS

FROM busybox
ARG SETTINGS
RUN ./run/other $SETTINGS

Вы можете использовать инструкцию ARG или ENV для указания переменных, доступных для инструкции RUN. Переменные среды, определенные с помощью инструкции ENV, всегда переопределяют инструкцию ARG с тем же именем.

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=v1.0.0 # this used
RUN echo $CONT_IMG_VER

# solution
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=${CONT_IMG_VER:-v1.0.0}
RUN echo $CONT_IMG_VER

В отличие от инструкции ARG значения ENV всегда сохраняются в собраном образе.

В Docker есть набор предопределенных переменных ARG, которые можно использовать без соответствующей инструкции ARG в файле Dockerfile

Predefined ARGs:

  • HTTP_PROXY
  • http_proxy
  • HTTPS_PROXY
  • https_proxy
  • FTP_PROXY
  • ftp_proxy
  • NO_PROXY
  • no_proxy

Для buildkit бекенда доступны Automatic platform ARGs in the global scope

Переменные ARG не сохраняются в собранном образе, как переменные ENV. Однако переменные ARG аналогичным образом влияют на кеш сборки. Если Dockerfile определяет переменную ARG, значение которой отличается от предыдущей сборки, то «промах кеша» происходит при ее первом использовании, а не при ее определении. В частности, все инструкции RUN, следующие за инструкцией ARG, неявно используют переменную ARG (как переменную среды), что может привести к промаху кэша. Все предопределенные переменные ARG освобождаются от кэширования, если в Dockerfile нет соответствующего оператора ARG. Подробнее

ONBUILD

ONBUILD <INSTRUCTION>

Инструкция ONBUILD добавляет к образу триггерную инструкцию, которая будет выполняться, когда образ будет использоваться в качестве основы для другой сборки. Триггер будет выполняться в контексте нижестоящей сборки, как если бы он был вставлен сразу после инструкции FROM в нижестоящем Dockerfile. Это полезно, если вы создаете образ, который будет использоваться в качестве основы для создания других образов, например, среды сборки приложения или демона, который можно настроить с помощью пользовательской конфигурации.

STOPSIGNAL

STOPSIGNAL signal

Инструкция STOPSIGNAL устанавливает сигнал системного вызова, который будет отправлен контейнеру для выхода. Этот сигнал может быть именем сигнала в формате SIG<NAME>, например, SIGKILL, или числом без знака, которое соответствует положению в таблице системных вызовов ядра, например 9. По умолчанию используется значение SIGTERM, если оно не определено.

Сигнал остановки образа по умолчанию может быть переопределен для каждого контейнера с помощью флага --stop-signal при запуске и создании докера.

HEALTHCHECK

Инструкция HEALTHCHECK сообщает Docker, как протестировать контейнер, чтобы убедиться, что он все еще работает. Это может обнаружить такие случаи, как веб-сервер, который застрял в бесконечном цикле и не может обрабатывать новые подключения, даже если серверный процесс все еще работает.

Два формата:

  • HEALTHCHECK [OPTIONS] CMD command (проверить работоспособность контейнера, выполнив команду внутри контейнера)
  • HEALTHCHECK NONE (отключить любую проверку работоспособности, унаследованную от базового образа)

SHELL

Инструкция SHELL позволяет переопределить оболочку по умолчанию, используемую для формы оболочки команд.

Dockerfile best practices

Create ephemeral containers

Образ, определенный вашим Dockerfile, должен генерировать как можно более эфемерные контейнеры. Под «эфемерным» мы подразумеваем, что контейнер можно остановить и уничтожить, а затем перестроить и заменить с абсолютно минимальной настройкой и конфигурацией. См. раздел «Процессы»

Understand build context

Когда вы запускаете команду сборки docker, текущий рабочий каталог становится контекстом сборки. По умолчанию предполагается, что Dockerfile находится здесь, но вы можете указать другое местоположение с помощью флага файла (-f). Независимо от того, где на самом деле находится Dockerfile, все рекурсивное содержимое файлов и каталогов в текущем каталоге отправляется демону Docker в качестве контекста сборки.

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

Pipe Dockerfile through stdin

Docker может создавать образы, передавая Dockerfile через стандартный ввод с локальным или удаленным контекстом сборки. Передача файла Dockerfile через стандартный ввод может быть полезна для выполнения одноразовых сборок без записи файла Dockerfile на диск или в ситуациях, когда файл Dockerfile создается и не должен сохраняться впоследствии.

Build an image using a Dockerfile from stdin, without sending build context

Build from a local build context, using a Dockerfile from stdin

Build from a remote build context, using a Dockerfile from stdin

Exclude with .dockerignore

Чтобы исключить файлы, не относящиеся к сборке (без реструктуризации исходного репозитория), используйте файл .dockerignore. Этот файл поддерживает шаблоны исключения, аналогичные файлам .gitignore.

Use multi-stage builds

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

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

Например, если ваша сборка содержит несколько слоев, вы можете упорядочить их от менее часто изменяемых (чтобы обеспечить возможность повторного использования кэша сборки) к более часто изменяемым:

  • Установите инструменты, необходимые для создания вашего приложения
  • Установите или обновите зависимости библиотеки
  • Создайте свое приложение

Пример

# syntax=docker/dockerfile:1
FROM golang:1.16-alpine AS build

# Install tools required for project
# Run `docker build --no-cache .` to update dependencies
RUN apk add --no-cache git
RUN go get github.com/golang/dep/cmd/dep

# List project dependencies with Gopkg.toml and Gopkg.lock
# These layers are only re-built when Gopkg files are updated
COPY Gopkg.lock Gopkg.toml /go/src/project/
WORKDIR /go/src/project/
# Install library dependencies
RUN dep ensure -vendor-only

# Copy the entire project and build it
# This layer is rebuilt when a file changes in the project directory
COPY . /go/src/project/
RUN go build -o /bin/project

# This results in a single layer image
FROM scratch
COPY --from=build /bin/project /bin/project
ENTRYPOINT ["/bin/project"]
CMD ["--help"]

Одна из самых сложных вещей при создании образов — это уменьшение их размера. Каждая инструкция в Dockerfile добавляет слой к образу, и вам нужно не забыть убрать все ненужные артефакты, прежде чем переходить к следующему слою. Чтобы написать действительно эффективный файл Dockerfile, вам традиционно приходилось использовать приемы оболочки и другую логику, чтобы сделать слои как можно меньше и гарантировать, что каждый слой имеет нужные ему артефакты из предыдущего слоя и ничего больше.

На самом деле было очень распространено иметь один файл Dockerfile для разработки (который содержал все необходимое для создания вашего приложения) и урезанный файл для использования в производстве, который содержал только ваше приложение и именно то, что было необходимо для его запуска. Это называется «шаблон строителя». Поддерживать два файла Dockerfile не идеально.

Такой искусственный подход показан тут - команда RUN сжата с помощью && что делает ее подверженной ошибкам

# syntax=docker/dockerfile:1
FROM golang:1.16
WORKDIR /go/src/github.com/alexellis/href-counter/
COPY app.go ./
RUN go get -d -v golang.org/x/net/html \
  && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

При многоэтапных сборках используйте несколько операторов FROM в файле Dockerfile. Каждая инструкция FROM может использовать разные базы, и каждая из них начинает новый этап сборки. Вы можете выборочно копировать артефакты с одного этапа на другой, убирая из финального изображении все, что вам не нужно.

# syntax=docker/dockerfile:1
FROM golang:1.16
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go ./
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app ./
CMD ["./app"]

Теперь нужен только один Dockerfile, не нужно разделять build-скрипты.

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

Как это работает? Вторая инструкция FROM запускает новую стадию сборки с образцом alpine:latest в качестве основы. Строка COPY --from=0 копирует только созданный артефакт из предыдущего этапа в этот новый этап. Go SDK и любые промежуточные артефакты остаются позади и не сохраняются в конечном образе.

Хорошей практикой является создание именванных build-этапов

# syntax=docker/dockerfile:1
FROM golang:1.16 AS builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go    ./
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app ./
CMD ["./app"]

Кроме того, можно останавливаться на специфическом этапе сборки

docker build --target builder -t alexellis2/href-counter:latest .

Это открывает путь к следующим опциям:

  • Отладка определенного этапа сборки
  • Использование этапа отладки со всеми включенными символами или инструментами отладки, а также этапа “бережливой” стадии сборки.
  • Использование этапа тестирования, на котором ваше приложение заполняется тестовыми данными, но сборка для производства производится с использованием другого этапа, который использует реальные данные.

Использование внешнего образа в качесвте stage. При использовании многоэтапных сборок вы не ограничены копированием стадий, созданных ранее в вашем Dockerfile. Вы можете использовать инструкцию COPY --from для копирования из отдельного образа, используя имя локального образа, тег, доступный локально или в реестре Docker, или идентификатор тега. Клиент Docker при необходимости извлекает образ и копирует оттуда артефакт.

COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf

Наконец, предыдущзий этап можно использовать в качестве следующего

# syntax=docker/dockerfile:1
FROM alpine:latest AS builder
RUN apk --no-cache add build-base

FROM builder AS build1
COPY source1.cpp source.cpp
RUN g++ -o /binary source.cpp

FROM builder AS build2
COPY source2.cpp source.cpp
RUN g++ -o /binary source.cpp

Don’t install unnecessary packages

Чтобы уменьшить сложность, количество зависимостей, размеры файлов и время сборки, избегайте установки дополнительных или ненужных пакетов только потому, что они могут быть «приятными». Например, вам не нужно включать текстовый редактор в образ базы данных.

Decouple applications

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

Ограничение каждого контейнера одним процессом — хорошее эмпирическое правило, но это не жесткое правило. Например, не только контейнеры могут быть порождены процессом инициализации, некоторые программы могут порождать дополнительные процессы по собственному желанию. Например, Celery может порождать несколько рабочих процессов, а Apache может создавать по одному процессу на каждый запрос.

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

Minimize the number of layers

В более старых версиях Docker было важно свести к минимуму количество слоев в ваших образах, чтобы обеспечить их производительность. Следующие функции были добавлены, чтобы уменьшить это ограничение:

  • Только инструкции RUN, COPY, ADD создают слои. Другие инструкции создают временные промежуточные образы и не увеличивают размер сборки.
  • По возможности используйте многоэтапные сборки и копируйте в окончательный образ только те артефакты, которые вам нужны. Это позволяет включать инструменты и информацию об отладке на промежуточных этапах сборки без увеличения размера конечного образа.

Sort multi-line arguments

По возможности упрощайте последующие изменения, сортируя многострочные аргументы в алфавитно-цифровом порядке. Это помогает избежать дублирования пакетов и упрощает обновление списка. Также помогает добавление пробела перед обратной косой чертой \.

RUN
apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion \
  && rm -rf /var/lib/apt/lists/*

Leverage build cache

При создании образа Docker выполняет инструкции в вашем Dockerfile, выполняя каждую в указанном порядке. По мере проверки каждой инструкции Docker ищет существующий образ в своем кеше, который он может повторно использовать, а не создает новый (дубликат) образ.

Если вы вообще не хотите использовать кеш, вы можете использовать параметр --no-cache=true в команде сборки docker. Однако, если вы разрешаете Docker использовать свой кеш, важно понимать, когда он может и не может найти подходящее изображение. Основные правила, которым следует Docker, изложены ниже:

  • Начиная с родительского образа, который уже находится в кэше, следующая инструкция сравнивается со всеми дочерними образами, полученными из этого базового образа, чтобы определить, был ли один из них создан с использованием той же самой инструкции. В противном случае кеш становится недействительным.
  • В большинстве случаев достаточно просто сравнить инструкцию в Dockerfile с одним из дочерних образов. Однако некоторые инструкции требуют дополнительного изучения и пояснений.
  • Для инструкций ADD и COPY проверяется содержимое файла(ов) в образе и для каждого файла вычисляется контрольная сумма. В этих контрольных суммах время последнего изменения и последнего доступа к файлу (файлам) не учитывается. Во время поиска в кэше контрольная сумма сравнивается с контрольной суммой в существующих образах. Если что-то изменилось в файле (файлах), например, содержимое и метаданные, кэш становится недействительным.
  • Помимо команд ADD и COPY, проверка кеша не просматривает файлы в контейнере, чтобы определить совпадение с кешем. Например, при обработке команды RUN apt-get -y update файлы, обновленные в контейнере, не проверяются на наличие попадания в кэш. В этом случае для поиска соответствия используется только сама командная строка.
  • Как только кеш становится недействительным, все последующие команды Dockerfile генерируют новые изображения, а кеш не используется.

Dockerfile instructions

FROM

По возможности используйте текущие официальные изображения в качестве основы для ваших изображений. Мы рекомендуем образ Alpine, так как он строго контролируется и имеет небольшой размер (в настоящее время менее 6 МБ), но при этом является полноценным дистрибутивом Linux.

LABEL

Docker object labels

Вы можете добавить метки к своему изображению, чтобы упорядочить изображения по проектам, записать информацию о лицензии, упростить автоматизацию или по другим причинам. Для каждой метки добавьте строку, начинающуюся с LABEL и с одной или несколькими парами ключ-значение.

Начиная с Docker 1.10 рекомендуется комбинировать всю информацию в одну метку

# Set multiple labels on one line
LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12"

# or
# Set multiple labels at once, using line-continuation characters to break long lines
LABEL vendor=ACME\ Incorporated \
      com.example.is-beta= \
      com.example.is-production="" \
      com.example.version="0.0.1-beta" \
      com.example.release-date="2015-02-12"

RUN

Разделите длинные или сложные операторы RUN на несколько строк, разделенных обратными косыми чертами, чтобы сделать ваш файл Dockerfile более читабельным, понятным и удобным в сопровождении.

apt-get

Вероятно, наиболее распространенным вариантом использования RUN является apt-get. Поскольку он устанавливает пакеты, у команды RUN apt-get есть несколько ошибок, на которые следует обратить внимание.

Всегда комбинируйте RUN apt-get update с apt-get install в одном операторе RUN.

RUN apt-get update && apt-get install -y \
    package-bar \
    package-baz \
    package-foo  \
    && rm -rf /var/lib/apt/lists/*

Использование только apt-get update в операторе RUN вызывает проблемы с кэшированием, и последующие инструкции по установке apt-get не выполняются. Использование RUN apt-get update && apt-get install -y гарантирует, что ваш файл Dockerfile установит последние версии пакетов без дальнейшего кодирования или ручного вмешательства.

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

RUN apt-get update && apt-get install -y \
    aufs-tools \
    automake \
    build-essential \
    curl \
    dpkg-sig \
    libcap-dev \
    libsqlite3-dev \
    mercurial \
    reprepro \
    ruby1.9.1 \
    ruby1.9.1-dev \
    s3cmd=1.1.* \
 && rm -rf /var/lib/apt/lists/*

rm -rf /var/lib/apt/lists/* это уменьшает размер изображения, поскольку кеш apt не хранится в слое. Поскольку инструкция RUN начинается с обновления apt-get, кэш пакетов всегда обновляется перед установкой apt-get. Официальные образы Debian и Ubuntu автоматически запускают apt-get clean, поэтому явный вызов не требуется.

Using pipes

Некоторые команды RUN зависят от возможности передавать вывод одной команды в другую с помощью символа вертикальной черты |

Docker выполняет эти команды с помощью интерпретатора /bin/sh -c, который оценивает только код выхода последней операции в канале, чтобы определить успех.

Если вы хотите, чтобы команда завершилась ошибкой из-за ошибки на любом этапе конвейера, добавьте set -o pipefail &&, чтобы гарантировать, что непредвиденная ошибка предотвратит непреднамеренное успешное выполнение сборки.

# no pipe
RUN wget -O - https://some.site | wc -l > /number

# pipe
RUN set -o pipefail && wget -O - https://some.site | wc -l > /number

CMD

Инструкцию CMD следует использовать для запуска программного обеспечения, содержащегося в вашем образе, вместе с любыми аргументами. CMD почти всегда следует использовать в форме CMD ["исполняемый", "param1", "param2"…]. Таким образом, если образ предназначен для службы, такой как Apache и Rails, вы должны запустить что-то вроде CMD ["apache2","-DFOREGROUND"]. Действительно, такая форма инструкции рекомендуется для любого сервисного образа.

В большинстве других случаев CMD должна иметь интерактивную оболочку, такую как bash, python и perl. Например, CMD ["perl", "-de0"], CMD ["python"] или CMD ["php", "-a"]. Использование этой формы означает, что когда вы выполняете что-то вроде docker run -it python, вы попадаете в пригодную для использования оболочку, готовую к работе. CMD редко следует использовать в манере CMD ["param", "param"] в сочетании с ENTRYPOINT, если только вы и ваши предполагаемые пользователи уже не знакомы с тем, как работает ENTRYPOINT.

EXPOSE

Инструкция EXPOSE указывает порты, на которых контейнер прослушивает соединения. Следовательно, вы должны использовать общий, традиционный порт для вашего приложения. Например, изображение, содержащее веб-сервер Apache, будет использовать EXPOSE 80, а изображение, содержащее MongoDB, будет использовать EXPOSE 27017 и так далее.

Для внешнего доступа ваши пользователи могут выполнить docker run с флагом, указывающим, как сопоставить указанный порт с портом по своему выбору. Для связывания контейнеров Docker предоставляет переменные среды для пути от контейнера-получателя обратно к источнику.

ENV

Чтобы упростить запуск нового программного обеспечения, вы можете использовать ENV для обновления переменной среды PATH для программного обеспечения, которое устанавливает ваш контейнер. Например, ENV PATH=/usr/local/nginx/bin:$PATH гарантирует, что CMD ["nginx"] просто работает.

Инструкция ENV также полезна для предоставления необходимых переменных среды, специфичных для служб, которые вы хотите контейнеризовать, таких как PGDATA Postgres.

Наконец, ENV также можно использовать для установки часто используемых номеров версий, чтобы упростить поддержку обновлений версий, как показано в следующем примере:

ENV PG_MAJOR=9.3
ENV PG_VERSION=9.3.4
RUN curl -SL https://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgres && …
ENV PATH=/usr/local/postgres-$PG_MAJOR/bin:$PATH

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

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

ADD or COPY

Хотя ADD и COPY функционально схожи, обычно предпочтение отдается COPY. Это потому, что он более прозрачен, чем ADD. COPY поддерживает только базовое копирование локальных файлов в контейнер, в то время как ADD имеет некоторые функции (например, локальное извлечение tar и удаленную поддержку URL), которые не сразу очевидны. Следовательно, лучшим применением для ADD является автоматическое извлечение локального tar-файла в образ.

Если у вас есть несколько шагов Dockerfile, которые используют разные файлы из вашего контекста, копируйте их по отдельности, а не все сразу. Это гарантирует, что кеш сборки каждого шага становится недействительным (вынуждая повторный запуск шага) только в случае изменения специально необходимых файлов.

COPY requirements.txt /tmp/
RUN pip install --requirement /tmp/requirements.txt
COPY . /tmp/

В примере выше это приводит к меньшему количеству инвалидаций кеша для шага RUN, чем если бы вы поместили COPY . /tmp/` перед ним.

Поскольку размер изображения имеет значение, использование ADD для получения пакетов с удаленных URL-адресов настоятельно не рекомендуется; вместо этого вы должны использовать curl или wget. Таким образом, вы можете удалить файлы, которые вам больше не нужны, после того, как они были извлечены, и вам не нужно добавлять еще один слой в ваше изображение. Например, вам следует избегать таких действий, как:

# wrong
ADD https://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all

# right
RUN mkdir -p /usr/src/things \
    && curl -SL https://example.com/big.tar.xz \
    | tar -xJC /usr/src/things \
    && make -C /usr/src/things all

ENTRYPOINT

Лучшее использование для ENTRYPOINT — установить основную команду образа, позволяя этому образу запускаться, как если бы это была эта команда (и затем использовать CMD в качестве флагов по умолчанию).

Инструкцию ENTRYPOINT также можно использовать в сочетании со вспомогательным сценарием, что позволяет ей работать даже если для запуска инструмента может потребоваться более одного шага.

Пример:

#!/bin/bash
set -e

if [ "$1" = 'postgres' ]; then
    chown -R postgres "$PGDATA"

    if [ -z "$(ls -A "$PGDATA")" ]; then
        gosu postgres initdb
    fi

    exec gosu postgres "$@"
fi

exec "$@"
COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["postgres"]

VOLUME

Инструкцию VOLUME следует использовать для предоставления доступа к любой области хранения базы данных, хранилищу конфигурации или файлам/папкам, созданным вашим док-контейнером. Настоятельно рекомендуется использовать VOLUME для любых изменяемых и/или обслуживаемых пользователем частей образа.

USER

Если служба может работать без привилегий, используйте USER, чтобы изменить пользователя без полномочий root. Избегайте установки или использования sudo, так как он имеет непредсказуемое поведение TTY и переадресации сигналов, что может вызвать проблемы. Если вам абсолютно необходима функциональность, аналогичная sudo, например инициализация демона с правами root, но запуск его без полномочий root, рассмотрите возможность использования «gosu».

Наконец, чтобы уменьшить уровни и сложность, избегайте частого переключения USER туда и обратно.

WORKDIR

Для ясности и надежности вы всегда должны использовать абсолютные пути для вашего WORKDIR. Кроме того, вы должны использовать WORKDIR вместо множащихся инструкций, таких как RUN cd … && do-something, которые трудно читать, устранять неполадки и поддерживать.

ONBUILD

Команда ONBUILD выполняется после завершения текущей сборки Dockerfile. ONBUILD выполняется в любом дочернем образе, производном от текущего образа. Воспринимайте команду ONBUILD как инструкцию, которую родительский файл Dockerfile дает дочернему файлу Dockerfile.

Сборка Docker выполняет команды ONBUILD перед любой командой в дочернем файле Docker.

ONBUILD полезен для образов, которые собираются ИЗ заданного образа. Например, вы могли бы использовать ONBUILD для образа языкового стека, который создает произвольное пользовательское программное обеспечение, написанное на этом языке.

Образы, созданные с помощью ONBUILD, должны иметь отдельный тег, например: ruby:1.9-onbuild или ruby:2.0-onbuild.

Будьте осторожны, добавляя ADD или COPY в ONBUILD. Образ «onbuild» приводит к катастрофическому сбою, если в контексте новой сборки отсутствует добавляемый ресурс. Добавление отдельного тега, как рекомендовано выше, помогает смягчить это, позволяя автору Dockerfile сделать выбор.

Смотри еще: