Лучше всего подходит для создания эффективных образов

В этом документе рассматриваются рекомендуемые передовые практики и методы создания эффективных образов.

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

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

# syntax=docker/dockerfile:1
FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py

Каждая оператор создаёт один слой:

  • FROM создаёт слой из образа ubuntu:18.04 Docker.

  • COPY добавляет файлы из текущего каталога вашего клиента Docker.

  • RUN создаёт ваше приложение с помощью make.

  • CMD указывает, какую команду запускать в контейнере.

Когда вы запускаете образ и создаёте контейнер, вы добавляете новый доступный для записи слой («слой-контейнер») поверх нижележащих слоев. Все изменения, внесенные в работающий контейнер, такие как запись новых файлов, изменение существующих файлов и удаление файлов, записываются в данный доступный для записи уровень контейнера.

Дополнительные сведения о слоях образов (и о том, как Docker создаёт и хранит образы), см. в статье О драйверах хранилища.

Общие указания и рекомендации

Создание эфемерных контейнеров

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

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

Понимание контекста сборки

См. страницу Создание контекста для получения дополнительной информации.

Передать Dockerfile через stdin

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

В примерах в этом разделе для удобства используется вот документы, но можно использовать любой способ предоставления Dockerfile на stdin.

Например, следующие команды эквивалентны:

echo -e 'FROM busybox\nRUN echo "hello world"' | docker build -
docker build -<<EOF
FROM busybox
RUN echo "hello world"
EOF

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

Создаёт образ с помощью Dockerfile из стандартного ввода без отправки контекста сборки

Используйте данный синтаксис для создания образа с использованием Dockerfile из stdin без отправки дополнительных файлов в качестве контекста сборки. Дефис (-) занимает позицию PATH и указывает Docker читать контекст сборки (который содержит только Dockerfile) из stdin вместо каталога:

docker build [OPTIONS] -

В следующем примере создаётся образ с использованием Dockerfile, который передаётся через stdin. Никакие файлы не отправляются демону в качестве контекста сборки.

docker build -t myimage:latest -<<EOF
FROM busybox
RUN echo "hello world"
EOF

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

Если вы хотите повысить скорость сборки, исключив некоторые файлы из контекста сборки, обратитесь к исключить с помощью .dockerignore.

Примечание

Попытка создать Dockerfile с использованием COPY или ADD завершится ошибкой, если используется данный синтаксис. Следующий пример иллюстрирует это:

# create a directory to work in
mkdir example
cd example

# create an example file
touch somefile.txt

docker build -t myimage:latest -<<EOF
FROM busybox
COPY somefile.txt ./
RUN cat /somefile.txt
EOF

# observe that the build fails
...
Step 2/3 : COPY somefile.txt ./
COPY failed: stat /var/lib/docker/tmp/docker-builder249218248/somefile.txt: no such file or directory

Сборка из локального контекста сборки с использованием файла Dockerfile из стандартного ввода

Используйте данный синтаксис для создания образа с использованием файлов в вашей локальной файловой системе, но с использованием Dockerfile из stdin. Синтаксис использует параметр -f (или --file), чтобы указывает Dockerfile для использования, используя дефис (-) в качестве имени файла, чтобы указывает Docker читать Dockerfile из stdin:

docker build [OPTIONS] -f- PATH

В приведённом далее примере используется текущий каталог (.) в качестве контекста сборки и создаётся образ с использованием Dockerfile, который передаётся через stdin с использованием здесь документ.

# create a directory to work in
mkdir example
cd example

# create an example file
touch somefile.txt

# build an image using the current directory as context, and a Dockerfile passed through stdin
docker build -t myimage:latest -f- . <<EOF
FROM busybox
COPY somefile.txt ./
RUN cat /somefile.txt
EOF

Сборка из контекста удаленной сборки с использованием файла Dockerfile из стандартного ввода

Используйте данный синтаксис для создания образа с использованием файлов из удаленного репозитория git с использованием Dockerfile из stdin. Синтаксис использует параметр -f (или --file), чтобы указывает Dockerfile для использования, используя дефис (-) в качестве имени файла, чтобы указывает Docker читать Dockerfile из stdin:

docker build [OPTIONS] -f- PATH

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

В приведённом далее примере создаётся образ с использованием Dockerfile из stdin и добавляется файл hello.c из Git-репозитория «hello-world» на GitHub.

docker build -t myimage:latest -f- https://github.com/docker-library/hello-world.git <<EOF
FROM busybox
COPY hello.c ./
EOF

Под капотом

При сборке образа с использованием удаленного репозитория Git в качестве контекста сборки Docker выполняет git clone репозитория на локальном компьютере и отправляет данные файлы демону в качестве контекста сборки. Для этой функции требуется, чтобы git был установлен на хосте, где вы запускаете команду docker build.

Исключить с помощью .dockerignore

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

Используйте многоэтапные сборки

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

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

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

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

  • Устанавливает или обновляет зависимости библиотеки

  • Создаёт свое приложение

Dockerfile для приложения Go может выглядеть так:

# 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"]

Не устанавливайте ненужные пакеты

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

Разделяйте приложения

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

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

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

Минимизируйте количество слоев

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

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

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

Сортировка многострочных аргументов

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

Вот пример из образа buildpack-deps:

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

Используйте кеш сборки

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

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

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

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

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

  • Помимо команд ADD и COPY, проверка кеша не просматривает файлы в контейнере, чтобы определить соответствие кеша. Например, при обработке команды RUN apt-get -y update файлы, обновленные в контейнере, не проверяются на наличие попадания в кэш. В этом случае для поиска соответствия используется только сама командная строка.

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

Инструкции по dockerfile

Данные рекомендации призваны помочь вам создать эффективную и удобную в сопровождении Dockerfile.

FROM

Справочник по dockerfile для инструкции FROM

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

LABEL

Знакомство с метками объектов

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

Примечание

Строки с пробелами должны быть заключены в кавычки или пробелы должны быть экранированы. Внутренние кавычки (") также должны быть экранированы.

# Set one or more individual labels
LABEL com.example.version="0.0.1-beta"
LABEL vendor1="ACME Incorporated"
LABEL vendor2=ZENITH\ Incorporated
LABEL com.example.release-date="2015-02-12"
LABEL com.example.version.is-production=""

Образ может иметь более одной метки. До Docker 1.10 рекомендовалось объединять все метки в одну инструкцию LABEL, чтобы предотвратить создание дополнительных слоев. В этом больше нет необходимости, но объединение меток по-прежнему поддерживается.

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

Вышеупомянутое также может быть записано как:

# 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"

См. Знакомство с метками объектов для ознакомления с допустимыми ключами и значениями меток. Сведения о запросе меток см. в разделах, связанных с фильтрацией, в статье Управление метками на объектах. См. также ЭТИКЕТКА в справочнике по Dockerfile.

RUN

Справочник по dockerfile для инструкции 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 install завершаются ошибкой. Например, предположим, что у вас есть Dockerfile:

# syntax=docker/dockerfile:1
FROM ubuntu:18.04
RUN apt-get update
RUN apt-get install -y curl

После построения образа все слои находятся в кеше Docker. Предположим, вы позже изменили apt-get install, добавив дополнительный пакет:

# syntax=docker/dockerfile:1
FROM ubuntu:18.04
RUN apt-get update
RUN apt-get install -y curl nginx

Docker считает исходные и измененные инструкции идентичными и повторно использует кэш из предыдущих шагов. В результате apt-get update не выполняется, поскольку сборка использует кешированную версию. Поскольку apt-get update не запущен, ваша сборка потенциально может получает устаревшую версию пакетов curl и nginx.

Использование RUN apt-get update && apt-get install -y гарантирует, что ваш Dockerfile установит последние версии пакетов без дополнительного кодирования или ручного вмешательства. Данный метод известен как «очистка кэша». Вы также можете добиться очистки кеша, указав версию пакета. Например, это называется закреплением версии:

RUN apt-get update && apt-get install -y \
    package-bar \
    package-baz \
    package-foo=1.3.*

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

Далее приведена правильно сформированная оператор RUN, демонстрирующая все рекомендации apt-get.

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/*

Аргумент s3cmd указывает версию 1.1.*. Если в образе ранее использовалась более старая версия, указание новой версии вызывает сброс кэша apt-get update и гарантирует установку новой версии. Перечисление пакетов в каждой строке также может предотвратить ошибки при дублировании пакетов.

Кроме того, когда вы очищаете кеш apt, удаляя /var/lib/apt/lists, размер образа уменьшается, поскольку кеш apt не хранится в слое. Поскольку оператор RUN начинается с apt-get update, кэш пакетов всегда обновляется до apt-get install.

Примечание

Официальные образы Debian и Ubuntu автоматически запускать apt-get clean, поэтому явный вызов не требуется.

Использование труб

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

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

Docker выполняет данные команды с помощью интерпретатора /bin/sh -c, который вычисляет только код выхода последней операции в канале, чтобы определить успех. В приведённом выше примере данный шаг сборки завершается успешно и создаёт новый образ до тех пор, пока команда wc -l выполняется успешно, даже если команда wget не выполняется.

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

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

Не все оболочки поддерживают параметр -o pipefail.

В таких случаях, как оболочка dash в образах на основе Debian, рассмотрите возможность использования формы exec RUN, чтобы явно выбрать оболочку, которая поддерживает параметр pipefail. Например:

RUN ["/bin/bash", "-c", "set -o pipefail && wget -O - https://some.site | wc -l > /number"]

CMD

Справочник по dockerfile для инструкции CMD

Инструкцию CMD следует использовать для запуска программного обеспечения, содержащегося в вашем образе, вместе с любыми аргументами. CMD почти всегда следует использовать в форме CMD ["executable", "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.

EXPOSE

Справочник по dockerfile для инструкции EXPOSE

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

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

ENV

Справочник по dockerfile для инструкции 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. Это означает, что даже если вы сбросите переменную среды в будущем слое, она все равно сохранится в этом слое, и её значение может быть сброшено. Вы можете проверяет это, создав Dockerfile, как показано далее, а затем собрав его.

# syntax=docker/dockerfile:1
FROM alpine
ENV ADMIN_USER="mark"
RUN echo $ADMIN_USER > ./mark
RUN unset ADMIN_USER
$ docker run --rm test sh -c 'echo $ADMIN_USER'

mark

Чтобы предотвратить это и действительно сбросить переменную среды, используйте команду RUN с командами оболочки, чтобы устанавливать, использовать и сбрасывать переменную на одном уровне. Вы можете разделить свои команды с помощью ; или &&. Если вы используете второй метод, и одна из команд завершается ошибкой, docker build также завершается ошибкой. Обычно это хорошая идея. Использование \ в качестве символа продолжения строки для Linux Dockerfiles улучшает читаемость. Вы также можете поместить все команды в сценарий оболочки и заставить команду RUN просто запускать данный сценарий оболочки.

# syntax=docker/dockerfile:1
FROM alpine
RUN export ADMIN_USER="mark" \
    && echo $ADMIN_USER > ./mark \
    && unset ADMIN_USER
CMD sh
$ docker run --rm test sh -c 'echo $ADMIN_USER'

ADD или COPY

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

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

Например:

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

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

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

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

И вместо этого выполняет что-то вроде:

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

Для других элементов (файлов, каталогов), которые не требуют возможности автоматического извлечения tar ADD, вы всегда должны использовать COPY.

ENTRYPOINT

Справочник по dockerfile для инструкции ENTRYPOINT

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

Начнем с примера образа для инструмента командной строки s3cmd:

ENTRYPOINT ["s3cmd"]
CMD ["--help"]

Теперь образ можно выполнить таким образом, чтобы показать справку команды:

$ docker run s3cmd

Или используя правильные параметры для выполнения команды:

$ docker run s3cmd ls s3://mybucket

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

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

Например, Официальное образ Postgres использует следующий сценарий в качестве своего 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 "$@"

Configure app as PID 1

Примечание

Данный сценарий использует команду исполнитель Bash, так что окончательное запущенное приложение становится PID контейнера 1. Это позволяет приложению получать любые сигналы Unix, отправленные в контейнер. Для получения дополнительной информации см. справочник по ENTRYPOINT.

Вспомогательный скрипт копируется в контейнер и запускается через ENTRYPOINT при запуске контейнера:

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

Данный сценарий позволяет пользователю взаимодействовать с Postgres несколькими способами.

Он может просто выполнить Postgres:

$ docker run postgres

Или его можно использовать для запуска Postgres и передачи параметров на сервер:

$ docker run postgres postgres --help

Наконец, его также можно использовать для запуска совершенно другого инструмента, такого как Bash:

$ docker run --rm -it postgres bash

VOLUME

Справочник по dockerfile для инструкции VOLUME

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

USER

Справочник по dockerfile для инструкции USER

Если служба может работать без привилегий, используйте USER, чтобы перейти на пользователя без полномочий root. Начать с создания пользователя и группы в Dockerfile с чем-то вроде:

RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres

Рассмотрим явный UID/GID

Пользователям и группам в образе назначается недетерминированный UID/GID, поскольку «следующий» UID/GID назначается независимо от перестроения образа. Итак, если это критично, вы должны назначить явный UID/GID.

Из-за нерешенная ошибка в пакете Go archive/tar, обрабатывающем разреженные файлы, попытка создать пользователя со значительно большим UID внутри контейнера Docker может привести к нехватке места на диске, поскольку /var/log/faillog на уровне контейнера заполнен символами NULL (). Обходной путь — передать флаг --no-log-init в useradd. Оболочка Debian/Ubuntu adduser не поддерживает данный флаг.

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

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

WORKDIR

Справочник по dockerfile для инструкции WORKDIR

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

ONBUILD

Справочник по dockerfile для инструкции ONBUILD

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

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

ONBUILD полезен для образов, которые будут построены FROM для данного образа. Например, вы должны использовать ONBUILD для образа языкового стека, который создаёт произвольное пользовательское программное обеспечение, написанное на этом языке, в пределах Dockerfile, как вы можете видеть в вариантах ONBUILD для Ruby.

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

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

Примеры Docker Официальных образов

Данные официальные образы имеют образцовый Dockerfile:

Дополнительные ресурсы: