Entrypoint docker: назад к основам / Блог компании Southbridge / Хабр
назад к основам / Блог компании Southbridge / Хабр
Название ENTRYPOINT
всегда меня смущало. Это название подразумевает, что каждый контейнер должен иметь определенную инструкцию ENTRYPOINT
. Но после прочтения официальной документации я понял, что это не соответствует действительности.
Факт 1: Требуется определить хотя бы одну инструкцию (ENTRYPOINT
или CMD
) (для запуска).
Если вы не определите ни одной из них, то получите сообщение об ошибке. Давайте попробуем запустить образ Alpine Linux, для которого не определены ни ENTRYPOINT
, ни CMD
.
$ docker run alpine
docker: Error response from daemon: No command specified.
See 'docker run --help'.
Факт 2: Если во время выполнения определена только одна из инструкций, то и CMD
и ENTRYPOINT
будут иметь одинаковый эффект.
$ cat Dockerfile
FROM alpine
ENTRYPOINT ls /usr
$ docker build -t test .
$ docker run test
bin
lib
local
sbin
share
Мы получим те же результаты, если будем использовать CMD
вместо ENTRYPOINT
.
$ cat Dockerfile
FROM alpine
CMD ls /usr # Using CMD instead
$ docker build -t test .
$ docker run test
bin
lib
local
sbin
share
Хотя этот пример и показывает, что между ENTRYPOINT
и CMD
нет никакой разницы, её можно увидеть, сравнив метаданные контейнеров.
Например, первый файл Dockerfile (с определенной ENTRYPOINT
):
$ docker inspect b52 | jq .[0].Config
{
...
"Cmd": null,
...
"Entrypoint": [
"/bin/sh",
"-c",
"ls /"
],
...
}
Факт 3: И для CMD
, и для ENTRYPOINT
существуют режимы shell и exec.
Из руководства:
ENTRYPOINT
имеет два режима выполнения:
ENTRYPOINT ["executable", "param1", "param2"]
(исполняемая форма, предпочтительно)
ENTRYPOINT command param1 param2
(форма оболочки)
До сих пор мы использовали режим shell, или оболочки. Это означает, что наша команда ls -l
запускается внутри /bin/sh -c
. Давайте попробуем оба режима и изучим запущенные процессы.
Режим «shell»:
$ cat Dockerfile
FROM alpine
ENTRYPOINT ping www.google.com # "shell" format
$ docker build -t test .
$ docker run -d test
11718250a9a24331fda9a782788ba315322fa879db311e7f8fbbd9905068f701
Затем изучим процессы:
$ docker exec 117 ps
PID USER TIME COMMAND
1 root 0:00 /bin/sh -c ping www.google.com
7 root 0:00 ping www.google.com
8 root 0:00 ps
Обратите внимание, что процесс sh -c
имеет PID, равный 1. Теперь то же самое, используя режим «exec»:
$ cat Dockerfile
FROM alpine
ENTRYPOINT ["ping", "www.google.com"] # "exec" format
$ docker build -t test .
$ docker run -d test
1398bb37bb533f690402e47f84e43938897cbc69253ed86f0eadb6aee76db20d
$ docker exec 139 ps
PID USER TIME COMMAND
1 root 0:00 ping www.google.com
7 root 0:00 ps
Мы видим, что при использовании режима exec команда ping www.google.com
работает с идентификатором процесса PID, равным 1, а процесс sh -c
отсутствует. Имейте в виду, что приведенный выше пример работает точно так же, если использовать CMD
вместо ENTRYPOINT
.
Факт 4: Режим exec является рекомендуемым.
Это связано с тем, что контейнеры задуманы так, чтобы содержать один процесс. Например, отправленные в контейнер сигналы перенаправляются процессу, запущенному внутри контейнера с идентификатором PID, равным 1. Очень познавательный опыт: чтобы проверить факт перенаправления, полезно запустить контейнер ping и попытаться нажать ctrl + c для остановки контейнера.
Контейнер, определенный с помощью режима exec, успешно завершает работу:
$ cat Dockerfile
FROM alpine
ENTRYPOINT ["ping", "www.google.com"]
$ docker build -t test .C64 bytes from 172.217.7.164: seq=4 ttl=37 time=0.334 ms
64 bytes from 172.217.7.164: seq=5 ttl=37 time=0.400 ms
Помогите, я не могу выйти! Сигнал SIGINT
, который был направлен процессу sh
, не будет перенаправлен в подпроцесс ping
, и оболочка не завершит работу. Если по какой-то причине вы действительно хотите использовать режим shell, выходом из ситуации будет использовать exec
для замены процесса оболочки процессом ping
.
$ cat Dockerfile
FROM alpine
ENTRYPOINT exec ping www.google.com
Факт 5: Нет оболочки? Нет переменных окружения.
Проблема запуска НЕ в режиме оболочки заключается в том, что вы не можете воспользоваться преимуществами переменных среды (таких как $PATH
) и прочими возможностями, которые предоставляет использование оболочки. В приведенном ниже файле Dockerfile присутствуют две проблемы:
$ cat Dockerfile
FROM openjdk:8-jdk-alpine
WORKDIR /data
COPY *.jar /data
CMD ["java", "-jar", "*.jar"] # "exec" format
Первая проблема: поскольку вы не можете воспользоваться переменной среды $PATH
, нужно указать точное расположение исполняемого java-файла. Вторая проблема: символы подстановки интерпретируются самой оболочкой, поэтому строка *.jar
не будет корректно обработана. После исправления этих проблем итоговый файл Dockerfile выглядит следующим образом:
FROM openjdk:8-jdk-alpine
WORKDIR /data
COPY *.jar /data
CMD ["/usr/bin/java", "-jar", "spring.jar"]
Факт 6: Аргументы CMD присоединяются к концу инструкции ENTRYPOINT
… иногда.
Вот тут-то и начинается путаница. В руководстве есть таблица, цель которой – внести ясность в этот вопрос.
Попытаюсь объяснить на пальцах.
Факт 6a: Если вы используете режим shell для ENTRYPOINT
, CMD
игнорируется.
$ cat Dockerfile
FROM alpine
ENTRYPOINT ls /usr
CMD blah blah blah blah
$ docker build -t test .
$ docker run test
bin
lib
local
sbin
share
Строка blah blah blah blah
была проигнорирована.
FACT 6b: При использовании режима exec для ENTRYPOINT
аргументы CMD
добавляются в конце.
$ cat Dockerfile
FROM alpine
ENTRYPOINT ["ls", "/usr"]
CMD ["/var"]
$ docker build -t test .
$ docker run test
/usr:
bin
lib
local
sbin
share
/var:
cache
empty
lib
local
lock
log
opt
run
spool
tmp
Аргумент /var
был добавлен к нашей инструкции ENTRYPOINT
, что позволило эффективно запустить команду ls/usr/var
.
Факт 6c: При использовании режима exec для инструкции ENTRYPOINT
необходимо использовать режим exec и для инструкции CMD
. Если этого не сделать, Docker попытается добавить sh -c
в уже добавленные аргументы, что может привести к некоторым непредсказуемым результатам.
Факт 7: Инструкции ENTRYPOINT
и CMD
могут быть переопределены с помощью флагов командной строки.
Флаг --entrypoint
может быть использован, чтобы переопределить инструкцию ENTRYPOINT
:
docker run --entrypoint [my_entrypoint] test
Все, что следует после названия образа в команде docker run
, переопределяет инструкцию CMD
:
docker run test [command 1] [arg1] [arg2]
Все вышеперечисленные факты справедливы, но имейте в виду, что разработчики имеют возможность переопределять флаги в команде docker run
. Из этого следует, что …
Достаточно фактов… Что же делать мне?
Ok, если вы дочитали статью до этого места, то вот информация, в каких случаях использовать ENTRYPOINT
, а в каких CMD
.
Это решение я собираюсь оставить на усмотрение человека, создающего Dockerfile, который может быть использован другими разработчиками.
Используйте ENTRYPOINT
, если вы не хотите, чтобы разработчики изменяли исполняемый файл, который запускается при запуске контейнера. Вы можете представлять, что ваш контейнер – исполняемая оболочка. Хорошей стратегией будет определить стабильную комбинацию параметров и исполняемого файла как ENTRYPOINT
. Для нее вы можете (не обязательно) указать аргументы CMD
по умолчанию, доступные другим разработчикам для переопределения.
$ cat Dockerfile
FROM alpine
ENTRYPOINT ["ping"]
CMD ["www.google.com"]
$ docker build -t test .
Запуск с параметрами по умолчанию:
$ docker run test
PING www.google.com (172.217.7.164): 56 data bytes
64 bytes from 172.217.7.164: seq=0 ttl=37 time=0.306 ms
Переопределение CMD собственными параметрами:
$ docker run test www.yahoo.com
PING www.yahoo.com (98.139.183.24): 56 data bytes
64 bytes from 98.139.183.24: seq=0 ttl=37 time=0.590 ms
Используйте только CMD
(без определения ENTRYPOINT
), если требуется, чтобы разработчики могли легко переопределять исполняемый файл. Если точка входа определена, исполняемый файл все равно можно переопределить, используя флаг --entrypoint
. Но для разработчиков будет гораздо удобнее добавлять желаемую команду в конце строки docker run
.
$ cat Dockerfile
FROM alpine
CMD ["ping", "www.google.com"]
$ docker build -t test .
Ping – это хорошо, но давайте попробуем запустить контейнер с оболочкой вместо команды ping
.
$ docker run -it test sh
/ # ps
PID USER TIME COMMAND
1 root 0:00 sh
7 root 0:00 ps
/ #
Я предпочитаю по большей части этот метод, потому что он дает разработчикам свободу легко переопределять исполняемый файл оболочкой или другим исполняемым файлом.
Очистка
После запуска команд на хосте осталась куча остановленных контейнеров. Очистите их следующей командой:
$ docker system prune
Обратная связь
Буду рад услышать ваши мысли об этой статье ниже в комментариях. Кроме того, если вам известен более простой способ поиска в выдаче докера с помощью jq, чтобы можно было сделать что-то вроде docker inspect [id] | jq * .config
, тоже напишите в комментариях.
Джон Закконе
Капитан Докера и инженер по облачным технологиям в IBM. Специализируется на Agile, микросервисах, контейнерах, автоматизации, REST, DevOps.
Ссылки:
- ENTRYPOINT vs CMD: Back to Basics
Dockerfile и коммуникация между контейнерами / Блог компании Infobox / Хабр
В прошлой статье мы рассказали, что такое Docker и как с его помощью можно обойти Vendor–lock. В этой статье мы поговорим о Dockerfile как о правильном способе подготовки образов для Docker. Также мы рассмотрим ситуацию, когда контейнерам нужно взаимодействовать друг с другом.
В InfoboxCloud мы сделали готовый образ Ubuntu 14.04 с Docker. Не забудьте поставить галочку «Разрешить управление ядром ОС» при создании сервера, это требуется для работы Docker.
Dockerfile
Подход docker commit, описанный в предыдущей статье, не является рекомендованным для Docker. Его плюс состоит в том, что мы настраиваем контейнер практически так, как привыкли настраивать стандартный сервер.
Вместо этого подхода мы рекомендуем использовать подход Dockerfile и команду docker build. Dockerfile использует обычный DSL с инструкциями для построения образов Docker. После этого выполняется команда docker build для построения нового образа с инструкциями в Dockerfile.
Написание Dockerfile
Давайте создадим простой образ с веб-сервером с помощью Dockerfile. Для начала создадим директорию и сам Dockerfile.
mkdir static_web
cd static_web
touch Dockerfile
Созданная директория — билд-окружение, в которой Docker вызывает контекст или строит контекст. Docker загрузит контекст в папке в процессе работы Docker–демона, когда будет запущена сборка образа. Таким образом будет возможно для Docker–демона получить доступ к любому коду, файлам или другим данным, которые вы захотите включить в образ.
Добавим в Dockerfile информацию по построению образа:
# Version: 0.0.1
FROM ubuntu:14.04
MAINTAINER Yuri Trukhin <[email protected]>
RUN apt-get update
RUN apt-get install -y nginx
RUN echo 'Hi, I am in your container' \
>/usr/share/nginx/html/index.html
EXPOSE 80
Dockerfile содержит набор инструкций с аргументами. Каждая инструкция пишется заглавными буквами (например FROM). Инструкции обрабатываются сверху вниз. Каждая инструкция добавляет новый слой в образ и коммитит изменения. Docker исполняет инструкции, следуя процессу:
- Запуск контейнера из образа
- Исполнение инструкции и внесение изменений в контейнер
- Запуск эквивалента docker commit для записи изменений в новый слой образа
- Запуск нового контейнера из нового образа
- Исполнение следующей инструкции в файле и повторение шагов процесса.
Это означает, что если исполнение Dockerfile остановится по какой-то причине (например инструкция не сможет завершиться), вы сможете использовать образ до этой стадии. Это очень полезно при отладке: вы можете запустить контейнер из образа интерактивно и узнать, почему инструкция не выполнилась, используя последний созданный образ.
Также Dockerfile поддерживает комментарии. Любая строчка, начинающаяся с # означает комментарий.
Первая инструкция в Dockerfile всегда должна быть FROM, указывающая, из какого образа нужно построить образ. В нашем примере мы строим образ из базового образа ubuntu версии 14:04.
Далее мы указываем инструкцию MAINTAINER, сообщающую Docker автора образа и его email. Это полезно, чтобы пользователи образа могли связаться с автором при необходимости.
Инструкция RUN исполняет команду в конкретном образе. В нашем примере с помощью ее мы обновляем APT репозитории и устанавливаем пакет с NGINX, затем создаем файл /usr/share/nginx/html/index.html.
По-умолчанию инструкция RUN исполняется внутри оболочки с использованием обертки команд /bin/sh -c. Если вы запускаете инструкцию на платформе без оболочки или просто хотите выполнить инструкцию без оболочки, вы можете указать формат исполнения:
RUN ["apt-get", "install", "-y", "nginx"]
Мы используем этот формат для указания массива, содержащего команду для исполнения и параметры команды.
Далее мы указываем инструкцию EXPOSE, которая говорит Docker, что приложение в контейнере должно использовать определенный порт в контейнере. Это не означает, что вы можете автоматически получать доступ к сервису, запущенному на порту контейнера (в нашем примере порт 80). По соображениям безопасности Docker не открывает порт автоматически, но ожидает, когда это сделает пользователь в команде docker run. Вы можете указать множество инструкций EXPOSE для указания, какие порты должны быть открыты. Также инструкция EXPOSE полезна для проброса портов между контейнерами.
Строим образ из нашего файла
docker build -t trukhinyuri/nginx ~/static_web
, где trukhinyuri – название репозитория, где будет храниться образ, nginx – имя образа. Последний параметр — путь к папке с Dockerfile. Если вы не укажете название образа, он автоматически получит название latest. Также вы можете указать git репозиторий, где находится Dockerfile.
docker build -t trukhinyuri/nginx \ [email protected]:trukhinyuri/docker-static_web
В данном примере мы строим образ из Dockerfile, расположенном в корневой директории Docker.
Если в корне билд контекста есть файл .dockerignore – он интерпретируется как список паттернов исключений.
Что произойдет, если инструкция не исполнится?
Давайте переименуем в Dockerfile nginx в ngin и посмотрим.
Мы можем создать контейнер из предпоследнего шага с ID образа 066b799ea548
docker run -i -t 066b799ea548 /bin/bash
и отладить исполнение.
По-умолчанию Docker кеширует каждый шаг и формируя кеш сборок. Чтобы отключить кеш, например для использования последнего apt-get update, используйте флаг —no-cache.
docker build --no-cache -t trukhinyuri/nginx
Использования кеша сборок для шаблонизации
Используя кеш сборок можно строить образы из Dockerfile в форме простых шаблонов. Например шаблон для обновления APT-кеша в Ubuntu:
FROM ubuntu:14.04
MAINTAINER Yuri Trukhin <[email protected]>
ENV REFRESHED_AT 2014–10–16
RUN apt-get -qq update
Инструкция ENV устанавливает переменные окружения в образе. В данном случае мы указываем, когда шаблон был обновлен. Когда необходимо обновить построенный образ, просто нужно изменить дату в ENV. Docker сбросит кеш и версии пакетов в образе будут последними.
Инструкции Dockerfile
Давайте рассмотрим и другие инструкции Dockerfile. Полный список можно посмотреть тут.
CMD
Инструкция CMD указывает, какую команду необходимо запустить, когда контейнер запущен. В отличие от команды RUN указанная команда исполняется не во время построения образа, а во время запуска контейнера.
CMD ["/bin/bash", "-l"]
В данном случае мы запускаем bash и передаем ему параметр в виде массива. Если мы задаем команду не в виде массива — она будет исполняться в /bin/sh -c. Важно помнить, что вы можете перегрузить команду CMD, используя docker run.
ENTRYPOINT
Часто команду CMD путают с ENTRYPOINT. Разница в том, что вы не можете перегружать ENTRYPOINT при запуске контейнера.
ENTRYPOINT ["/usr/sbin/nginx"]
При запуске контейнера параметры передаются команде, указанной в ENTRYPOINT.
docker run -d trukhinyuri/static_web -g "daemon off"
Можно комбинировать ENTRYPOINT и CMD.
ENTRYPOINT ["/usr/sbin/nginx"]
CMD ["-h"]
В этом случае команда в ENTRYPOINT выполнится в любом случае, а команда в CMD выполнится, если не передано другой команды при запуске контейнера. Если требуется, вы все-таки можете перегрузить команду ENTRYPOINT с помощью флага —entrypoint.
WORKDIR
С помощью WORKDIR можно установить рабочую директорию, откуда будут запускаться команды ENTRYPOINT и CMD.
WORKDIR /opt/webapp/db
RUN bundle install
WORKDIR /opt/webapp
ENTRYPOINT ["rackup"]
Вы можете перегрузить рабочую директорию контейнера в рантайме с помощью флага -w.
USER
Специфицирует пользователя, под которым должен быть запущен образ. Мы можем указать имя пользователя или UID и группу или GID.
USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group
Вы можете перегрузить эту команду, используя глаг -u при запуске контейнера. Если пользователь не указан, используется root по-умолчанию.
VOLUME
Инструкция VOLUME добавляет тома в образ. Том — папка в одном или более контейнерах или папка хоста, проброшенная через Union File System (UFS).
Тома могут быть расшарены или повторно использованы между контейнерами. Это позволяет добавлять и изменять данные без коммита в образ.
VOLUME ["/opt/project"]
В примере выше создается точка монтирования /opt/project для любого контейнера, созданного из образа. Таким образом вы можете указывать и несколько томов в массиве.
ADD
Инструкция ADD добавляет файлы или папки из нашего билд-окружения в образ, что полезно например при установке приложения.
ADD software.lic /opt/application/software.lic
Источником может быть URL, имя файла или директория.
ADD http://wordpress.org/latest.zip /root/wordpress.zip
ADD latest.tar.gz /var/www/wordpress/
В последнем примере архив tar.gz будет распакован в /var/www/wordpress. Если путь назначения не указан — будет использован полный путь включая директории.
COPY
Инструкция COPY отличается от ADD тем, что предназначена для копирования локальных файлов из билд-контекста и не поддерживает распаковки файлов:
COPY conf.d/ /etc/apache2/
ONBUILD
Инструкция ONBUILD добавляет триггеры в образы. Триггер исполняется, когда образ используется как базовый для другого образа, например, когда исходный код, нужный для образа еще не доступен, но требует для работы конкретного окружения.
ONBUILD ADD . /app/src
ONBUILD RUN cd /app/src && make
Коммуникация между контейнерами
В предыдущей статье было показано, как запускать изолированные контейнеры Docker и как пробрасывать файловую систему в них. Но что, если приложениям нужно связываться друг с другом. Есть 2 способа: связь через проброс портов и линковку контейнеров.
Проброс портов
Такой способ связи уже был показан ранее. Посмотрим на варианты проброса портов чуть шире.
Когда мы используем EXPOSE в Dockerfile или параметр -p номер_порта – порт контейнера привязывается к произвольному порту хоста. Посмотреть этот порт можно командой docker ps или docker port имя_контейнера номер_порта_в_контейнере. В момент создания образа мы можем не знать, какой порт будет свободен на машине в момент запуска контейнера.
Указать, на какой конкретный порт хоста мы привяжем порт контейнера можно параметром docker run -p порт_хоста: порт_контейнера
По-умолчанию порт используется на всех интерфейсах машины. Можно, например, привязать к localhost явно:
docker run -p 127.0.0.1:80:80
Можно привязать UDP порты, указав /udp:
docker run -p 80:80/udp
Линковка контейнеров
Связь через сетевые порты — лишь один способ коммуникации. Docker предоставляет систему линковки, позволяющую связать множество контейнеров вместе и отправлять информацию о соединении от одного контейнера другому.
Для установки связи нужно использовать имена контейнеров. Как было показано ранее, вы можете дать имя контейнеру при создании с помощью флага —name.
Допустим у вас есть 2 контейнера: web и db. Чтобы создать связь, удалите контейнер web и пересоздайте с использованием команды —link name:alias.
docker run -d -P --name web --link db:db trukhinyuri/webapp python app.py
Используя docker -ps можно увидеть связанные контейнеры.
Что на самом деле происходит при линковке? Создается контейнер, который предоставляет информацию о себе контейнеру-получателю. Это происходит двумя способами:
- Через переменные окружения
- Через /etc/hosts
Переменные окружения можно посмотреть, выполнив команду env:
$ sudo docker run --rm --name web2 --link db:db training/webapp env
. . .
DB_NAME=/web2/db
DB_PORT=tcp://172.17.0.5:5432
DB_PORT_5432_TCP=tcp://172.17.0.5:5432
DB_PORT_5432_TCP_PROTO=tcp
DB_PORT_5432_TCP_PORT=5432
DB_PORT_5432_TCP_ADDR=172.17.0.5
Префикс DB_ был взят из alias контейнера.
Можно просто использовать информацию из hosts, например команда ping db (где db – alias) будет работать.
Заключение
В этой статье мы научились использовать Dockerfile и организовывать связь между контейнерами. Это только вершина айсберга, очень многое осталось за кадром и будет рассмотрено в будущем. Для дополнительного чтения рекомендуем книгу The Docker Book.
Готовый образ с Docker доступен в облаке InfoboxCloud.
В случае, если вы не можете задавать вопросы на Хабре, можно задать в Сообществе InfoboxCloud.
Если вы обнаружили ошибку в статье, автор ее с удовольствием исправит. Пожалуйста напишите в ЛС или на почту о ней.
Успешного использования Docker!
Docker — Разница между ENTRYPOINT и CMD
пример
Существует две директивы Dockerfile
для указания, какую команду запускать по умолчанию в построенных изображениях. Если вы укажете только CMD
то докер выполнит эту команду, используя стандартную ENTRYPOINT
, которая является /bin/sh -c
. При запуске встроенного образа вы можете переопределить любую или оба точки входа и / или команды. Если вы укажете оба, то ENTRYPOINT
указывает исполняемый файл вашего контейнерного процесса, а CMD
будет предоставлен в качестве параметров этого исполняемого файла.
Например, если ваш Dockerfile
содержит
FROM ubuntu:16.04
CMD ["/bin/date"]
Затем вы используете директиву ENTRYPOINT
по умолчанию /bin/sh -c
и run /bin/date
с этой точкой входа по умолчанию. Команда вашего процесса контейнера будет /bin/sh -c /bin/date
. После запуска этого изображения оно будет по умолчанию распечатывать текущую дату
$ docker build -t test .
$ docker run test
Tue Jul 19 10:37:43 UTC 2016
Вы можете переопределить CMD
в командной строке, и в этом случае он выполнит указанную вами команду.
$ docker run test /bin/hostname
bf0274ec8820
Если вы укажете директиву ENTRYPOINT
, Docker будет использовать этот исполняемый файл, а директива CMD
задает параметры (параметры) по умолчанию. Поэтому, если ваш Dockerfile
содержит:
FROM ubuntu:16.04
ENTRYPOINT ["/bin/echo"]
CMD ["Hello"]
Тогда запуск будет производить
$ docker build -t test .
$ docker run test
Hello
Вы можете предоставить различные параметры, если хотите, но все они будут запускать /bin/echo
$ docker run test Hi
Hi
Если вы хотите переопределить точку входа, указанную в вашем файле Docker (т. Е. Если вы хотите запустить другую команду, чем echo
в этом контейнере), вам нужно указать параметр --entrypoint
в командной строке:
$ docker run --entrypoint=/bin/hostname test
b2c70e74df18
Обычно вы используете директиву ENTRYPOINT
чтобы указать на основное приложение, которое вы хотите запустить, и CMD
на параметры по умолчанию.
Начало работы с Docker. Часть вторая. Категория: ОС Linux • Разное
Создание Dockerfile
Dockerfile
— конфигурационный файл, описывающий пошаговое создание среды для работы приложения. В этом файле подробно описывается какие образы задействованы, какие команды будут выполнены и какие настройки будут применены. А движок Docker-а при запуске уже прочитает этот файл и создаст соответствующий образ.
Создадим директорию app
в домашнем каталоге и разместим в нем два файла — script.php
и Dockerfile
:
$ cd ~ $ mkdir app $ cd app
$ nano script.php
<?php $n = $i = 5; while ($i--) { echo str_repeat(' ', $i).str_repeat('* ', $n - $i)."\n"; }
$ nano Dockerfile
FROM php:7.2-cli COPY script.php /script.php RUN chmod +x /script.php CMD php /script.php
Эти инструкции означают:
FROM
— использовать образ с предустановленнымphp:7.2-cli
(имя:тег
)COPY
— копировать файлscript.php
из основной системы внутрь контейнераRUN
— выполнить shell-команду в терминале контейнера (сделать скрипт исполняемым)CMD
— выполнять эту shell-команду каждый раз при запуске контейнера
Теперь создадим образ из Dockerfile
:
$ docker build . --tag php-cli-script
Эта команда создает образы из Dockerfile
и «контекста». Контекст сборки — это набор файлов, расположенных по указанному пути или URL. Процесс создания образа может ссылаться на любой из файлов в контексте. Например, инструкция COPY
будет искать файл по указанному пути. Мы используем точку, чтобы сообщить, что все файлы расположены в текущей директории. А опция --tag
позволяет задать имя образа (можно задать в виде имя:тег
).
Посмотрим список образов, сохраненных локально:
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE -------------------------------------------------------------------------------------------- php-cli-script latest 5e72190ee0b4 10 minutes ago 398MB tokmakov/apache2_ubuntu latest 18073a80c28b 23 hours ago 191MB php 7.2-cli 555ec78042a1 3 weeks ago 398MB ubuntu latest 72300a873c2c 3 weeks ago 64.2MB hello-world latest fce289e99eb9 14 months ago 1.84kB
Теперь все готово к запуску нового контейнера:
$ docker run php-cli-script * * * * * * * * * * * * * * *
При запуске контейнера можно указать дополнительные опции, например
$ docker run --rm ИмяОбраза # удалить контейнер при завершении $ docker run --name ИмяКонтейнера ИмяОбраза # присвоить контенеру имя
Наш php-скрипт был успешно скопирован, и выполнен благодаря указанной в Dockerfile
инструкции CMD
. Давайте проверим, что скрипт действительно был скопирован внутрь контейнера:
$ docker run php-cli-script ls bin ..... sbin script.php srv ..... var
Однако, сейчас этот контейнер недостаточно гибкий. Хотелось бы иметь возможность передать нашему скрипту аргументы, чтобы можно было изменять размер пирамиды.
$ nano script.php
<?php $n = $i = empty($argv[1]) ? 5 : $argv[1]; while ($i--) { echo str_repeat(' ', $i).str_repeat('* ', $n - $i)."\n"; }
$ docker build . --tag php-cli-script
$ docker run php-cli-script php /script.php 7 # переопределяем инструкцию CMD * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Но, было бы неплохо передавать этот аргумент самому контейнеру, вместо переписывания всей команды.
$ nano Dockerfile
FROM php:7.2-cli COPY script.php /script.php RUN chmod +x /script.php ENTRYPOINT ["php", "/script.php"] CMD ["10"]
Теперь аргументы инструкции CMD
будут добавлены к аргументам инструкции ENTRYPOINT
и получится
$ php /script.php 10 # интерпретатор php выполняет php-скрипт script.php, которому передается аргумент 10
Соберем образ заново, запустим контейнер и предадим ему аргумент:
$ docker build . --tag php-cli-script
$ docker run php-cli-script 8 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Основные инструкции Dockerfile
Для начала посмотрим на список инструкций Dockerfile
с краткими комментариями, а потом разберем их подробнее:
FROM
— задаёт базовый (родительский) образLABEL
— описывает метаданные (например, кто создал образ)ENV
— устанавливает постоянные переменные средыRUN
— выполняет команду и создаёт слой образаCOPY
— копирует в контейнер файлы и директорииADD
— копирует файлы и папки в контейнер, может распаковывать .tar-файлыCMD
— описывает команду с аргументами, которая выполняется при запуске контейнераWORKDIR
— задаёт рабочую директорию для следующей инструкцииARG
— задаёт переменные для передачи Docker во время сборки образаENTRYPOINT
— задает команду с аргументами для вызова во время выполнения контейнераEXPOSE
— указывает на необходимость открыть портVOLUME
— создаёт точку монтирования для работы с постоянным хранилищем
1. Инструкция FROM
сообщает Docker о том, чтобы при сборке образа использовался базовый образ, который соответствует предоставленному имени и тегу. Базовый образ, кроме того, ещё называют родительским образом.
FROM ubuntu:18.04
2. Инструкция LABEL
(метка) позволяет добавлять в образ метаданные. Как правило, она включает в себя контактные сведения создателя образа.
LABEL maintainer="[email protected]"
3. Инструкция ENV
позволяет задавать постоянные переменные среды, которые будут доступны в контейнере во время его выполнения. Хорошо подходит для задания констант. Если некое значение используется несколько раз, имеет смысл записать его в константу.
ENV ADMIN="somebody"
В файлах Dockerfile
часто существуют разные способы решения одних и тех же задач. Например, инструкции RUN
, CMD
и ENTRYPOINT
служат разным целям, но все они используются для выполнения команд.
4. Инструкция RUN
позволяет создать слой во время сборки образа. После её выполнения в образ добавляется новый слой, его состояние фиксируется. Инструкция RUN
часто используется для установки в образы дополнительных пакетов.
# обновить пакеты RUN apt-get update && apt-get -y upgrade # создать директорию RUN ["mkdir", "/app"]
Инструкция RUN
и схожие с ней инструкции (CMD
и ENTRYPOINT
), могут быть использованы либо в exec-форме, либо в shell-форме. Exec-форма использует синтаксис, напоминающий описание JSON-массива:
# запустить exec-file с аргументами first и second RUN ["exec-file", "first", "second"]
5. Инструкция COPY
позволяет копировать в образ файлы и директории. Если целевая директория не существует, она будет создана. Например, сообщим Docker о том, что нужно взять файлы и папки из локального контекста сборки и добавить их в рабочую директорию образа.
COPY . ./bin
6. Инструкция ADD
позволяет решать те же задачи, что и COPY
, но с ней связана ещё пара вариантов использования. Так, с помощью этой инструкции можно добавлять в контейнер файлы, загруженные из удалённых источников, а также распаковывать локальные tar-файлы.
7. Инструкция CMD
предоставляет Docker команду, которую нужно выполнить при запуске контейнера. Результаты выполнения этой команды не добавляются в образ во время его сборки. В одном файле Dockerfile
может присутствовать лишь одна инструкция CMD
.
Инструкция CMD
может иметь exec-форму. Если в эту инструкцию не входит упоминание исполняемого файла, тогда в файле должна присутствовать инструкция ENTRYPOINT
. В таком случае обе эти инструкции должны быть представлены в формате JSON.
Аргументы командной строки, передаваемые docker run
, переопределяют аргументы, предоставленные инструкции CMD
в Dockerfile.
8. Инструкция WORKDIR
позволяет установить рабочую директорию, откуда будут запускаться команды ENTRYPOINT
и CMD
. Инструкция автоматически создаёт директорию в том случае, если она не существует. Кроме того, с помощью инструкции WORDDIR
можно создать контекст для инструкции COPY
:
WORKDIR /app COPY . .
9. Инструкция ARG
позволяет задать переменную, значение которой можно передать из командной строки в образ во время его сборки. Значение для переменной по умолчанию можно представить в Dockerfile
.
В отличие от ENV
-переменных, ARG
-переменные недоступны во время выполнения контейнера. Однако ARG
-переменные можно использовать для задания значений по умолчанию для ENV
-переменных из командной строки в процессе сборки образа. А ENV
-переменные уже будут доступны в контейнере во время его выполнения.
10. Инструкция ENTRYPOINT
позволяет задавать команду с аргументами, которая должна выполняться при запуске контейнера. Она похожа на команду CMD
, но параметры, задаваемые в ENTRYPOINT
, не перезаписываются в том случае, если контейнер запускают с параметрами командной строки. Вместо этого аргументы командной строки, передаваемые в конструкции вида docker run image param
, добавляются к аргументам, задаваемым инструкцией ENTRYPOINT
.
# Задаём значение по умолчанию для переменной ARG data="some value" # Команда будет запущена при запуске контейнера ENTRYPOINT ["php", "./app/script.php", "data"]
11. Инструкция EXPOSE
указывает на то, какие порты нужно открыть для того, чтобы можно было связаться с работающим контейнером. Обратите внимание, что эта инструкция не открывает порты. Для того, чтобы открыть порт (или порты) и настроить перенаправление, нужно выполнить команду docker run
с опцией -p
. Если использовать ключ в виде -P
(с заглавной буквой P
), то открыты будут все порты, указанные в инструкции EXPOSE
.
12. Инструкция VOLUME
устарела и использовать ее не рекомендуется.
Хранение данных в Docker
Данные в Docker могут храниться либо временно, либо постоянно.
1. Временное хранение данных
По умолчанию файлы, создаваемые приложением, работающим в контейнере, сохраняются в верхнем слое контейнера, поддерживающем запись. Для того чтобы этот механизм работал, ничего специально настраивать не нужно. Получается дёшево и сердито — приложению достаточно просто сохранить данные и продолжить заниматься своими делами. Однако после того как контейнер перестанет существовать, исчезнут и данные, сохранённые таким вот нехитрым способом.
2. Постоянное хранение данных
Самый простой способ — монтирование директории основной системы в Docker контейнер прямо при запуске:
$ docker run -v /path/to/outer-dir:/path/to/inner-dir image-name
Давайте запустим в работу контейнер с Ubuntu и смонтируем директорию ~/share
в директорию контейнера /mnt/share
:
$ mkdir ~/share $ docker run -it -v ~/share:/mnt/share ubuntu:18.10 /bin/bash # cd /mnt/share # переходим в директорию # touch test.txt # и создаем в ней файл # exit
Теперь в основной системе, в директории ~/share
мы увидим файл test.txt
. При удалении контейнера, все данные, которые он записывал в директорию /mnt/share
останутся в директории ~/share
основной системы.
Второй способ постоянного хранения данных — использование Docker Volumes.
Поиск:
CLI • Linux • Ubuntu • Виртуализация • Команда • Настройка • Процесс • Установка • Контейнер • Образ • Docker
Docker для разработчиков Java: сборка на Docker
Эта статья является частью нашего курса Академии под названием Docker Tutorial для разработчиков Java .
В этом курсе мы предлагаем серию руководств, чтобы вы могли разрабатывать свои собственные приложения на основе Docker. Мы охватываем широкий спектр тем, от Docker через командную строку, до разработки, тестирования, развертывания и непрерывной интеграции. С нашими простыми учебными пособиями вы сможете запустить и запустить собственные проекты за минимальное время. Проверьте это здесь !
1. Введение
В течение первых нескольких частей руководства мы рассмотрели основы Docker и множество способов взаимодействия с ним. Настало время применить полученные знания в реальных проектах Java, начав обсуждение с темы о том, как Docker влияет на хорошо отлаженные процессы и практики сборки.
Честно говоря, цель этого раздела двоякая. Сначала мы рассмотрим, как существующие инструменты сборки, а именно Apache Maven и Gradle , помогают упаковывать приложения Java в контейнеры Docker . Во-вторых, мы будем продвигать эту идею еще дальше и узнаем, как мы могли бы использовать Docker для полной инкапсуляции конвейера сборки наших Java-приложений и создания окончательных образов Docker в конце.
2. Под увеличительным стеклом
Чтобы поэкспериментировать, мы собираемся разработать два простых (но, тем не менее, значимых) веб-приложения на Java, которые бы реализовывали и представляли API REST (ful) для управления задачами.
Первое приложение будет разработано поверх Spring Boot и Spring Webflux с использованием Gradle в качестве инструмента управления сборкой и зависимостями. Что касается версий, мы будем использовать последнюю 2.0.0.M6
Spring Boot 2.0.0.M6
, последнюю 2.0.0.M6
Spring Webflux 5.0.1
и последнюю версию Gradle 4.3
.
Второе приложение, хотя и функционально эквивалентное первому, будет разработано поверх другой популярной платформы Java, Dropwizard , на этот раз с использованием Apache Maven для управления сборкой и зависимостями. Что касается версий, мы собираемся представить последний выпуск 1.2.0
Dropwizard и последний выпуск 3.5.2
Apache Maven .
Как мы уже упоминали, оба приложения будут реализовывать и предоставлять API-интерфейсы REST (ful) для управления задачами, по сути, заключая в себе операции CRUD (создание, чтение, обновление и удаление).
1 2 3 4 |
|
Сама задача моделируется как постоянный объект, который будет управляться Hibernate ORM и храниться в базе данных отношений MySQL .
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 |
|
На этом сходство обоих приложений заканчивается, и каждое из них будет следовать своему идиоматическому пути развития.
3. Градл и Докер
Итак, все готово, давайте начнем путешествие с изучения того, что нужно для интеграции Docker в типичную сборку Gradle . Для этого подраздела вам понадобится установить Gradle 4.3
на ваш компьютер для разработки. Если у вас его еще нет, следуйте инструкциям по установке , выбрав любой предложенный метод, который вы предпочитаете.
Чтобы упаковать типичное приложение Spring Boot как образ Docker с помощью Gradle, нам просто нужно включить два дополнительных плагина в файл build.gradle
:
Конвейер сборки в основном полагался бы на плагин Spring Boot Gradle для создания uber-jar (термин, который часто используется для описания техники создания единственного исполняемого JAR-архива приложения), который позже будет использоваться Palantir Docker Gradle для сборки образа Docker . Вот как выглядит определение сборки, файл build.gradle
.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
|
Это на самом деле довольно просто, все мясо в основном находится в разделе build.gradle
файла build.gradle
. Вы также можете заметить, что мы используем наш собственный Dockerfile , src/main/docker/Dockerfile
, чтобы предоставить Docker инструкции по созданию образа.
01 02 03 04 05 06 07 08 09 10 11 |
|
Действительно, так просто, как только можно. Обратите внимание, как мы используем инструкцию ARG (и настройку build.gradle
файле build.gradle
) для передачи аргументов в изображение. В этом случае мы передаем версию проекта, чтобы найти окончательные артефакты сборки. Еще одна интересная деталь, на которую стоит обратить внимание, — это использование инструкций ENV для соединения хоста и порта экземпляра MySQL для подключения. И, как вы уже догадались, инструкция EXPOSE информирует Docker о том, что контейнер прослушивает порт 19900
во время выполнения.
Круто, а что дальше? Ну, нам просто нужно запустить нашу сборку Gradle , вот так:
1 2 3 4 |
|
Задача dockerTag
самом деле не нужна, но из-за этой проблемы, о которой сообщалось в отношении плагина Palantir Docker Gradle, мы должны явно вызвать его, чтобы правильно dockerTag
наше изображение. Давайте проверим, есть ли у нас изображение, доступное локально.
1 2 3 4 5 |
|
Хорошо, новый образ есть прямо из духовки. Мы можем запустить его немедленно, используя инструмент командной строки docker , но сначала нам нужно, чтобы где-то был доступен контейнер MySQL . К счастью, мы делали это уже столько раз, что это нас не озадачит.
1 2 3 4 5 6 |
|
Теперь мы готовы запустить наше приложение в виде контейнера Docker . Есть несколько способов, которыми мы могли бы ссылаться на контейнер MySQL , при этом определяемая пользователем сеть является предпочтительным вариантом. Для простых случаев, подобных нашему, мы можем просто обратиться к нему, назначив DB_HOST
среды DB_HOST
IP-адрес работающего контейнера MySQL , например:
1 2 3 4 5 |
|
Сопоставив порт 19900
от контейнера к хосту, мы могли общаться с нашим приложением, получая доступ к его REST (ful) API из curl, используя localhost
качестве имени хоста. Давайте сделаем это прямо сейчас.
01 02 03 04 05 06 07 08 09 10 11 |
|
1 2 3 4 5 6 7 8 9 |
|
1 2 3 4 5 6 7 |
|
Под капотом много движущихся частей, например, автоматическая миграция базы данных с использованием Flyway и встроенная поддержка проверки работоспособности с помощью Spring Boot Actuator . Некоторые из них появятся в следующих разделах руководства, но посмотрите, насколько просто и естественно создать и упаковать свои приложения Spring Boot в виде образов Docker с помощью Gradle .
4. Gradle на докере
Оказывается, что создание образов Docker с помощью Gradle совсем не больно. Но все же, для того, чтобы Gradle был установлен в целевой системе вместе с JDK / JRE, требуется некоторая предварительная работа. Это может быть не проблема, скажем, для разработки, так как очень вероятно, что вы все равно (и многие другие) были бы установлены.
Однако в случае облачных развертываний или конвейеров CI / CD это может быть проблемой, что может повлечь за собой дополнительные затраты с точки зрения работы и / или обслуживания. Можем ли мы найти способ избавиться от таких издержек и полностью положиться на Docker ? Да, фактически мы можем, приняв многоэтапные сборки , одно из недавних дополнений в наборе функций Docker .
Если вам интересно, как это может помочь нам, вот идея. По сути, мы собираемся следовать обычной процедуре создания образа из Dockerfile . Но Dockerfile на самом деле будет содержать два определения изображений. Первый (основанный на одном из официальных образов Gradle ) инструктирует Docker запустить сборку Gradle нашего приложения Spring Boot . Второй выбрал бы двоичные файлы, созданные первым изображением, и создал окончательный образ Docker с нашим приложением Spring Boot, запечатанным внутри (так же, как мы делали раньше).
Возможно, лучше один раз увидеть, чем пытаться объяснить. Файл Dockerfile.build
ниже иллюстрирует эту идею в действии, используя инструкции многоступенчатой сборки .
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 |
|
Первая часть определения Dockerfile описывает изображение на основе gradle:4.3.0-jdk8-alpine
. Поскольку наш проект довольно мал, мы просто копируем все источники внутри образа и запускаем gradle build
(эта команда будет выполняться Docker во время gradle build
образа). Результатом сборки будет uber-jar, который мы openjdk:8-jdk-alpine
в другое определение изображения, на этот раз на основе openjdk:8-jdk-alpine
. Это составило бы наше окончательное изображение, которое мы могли бы создать, используя инструмент командной строки docker .
1 2 3 4 5 |
|
После командного соревнования мы должны увидеть наше недавно испеченное изображение в списке доступных изображений Docker .
1 2 3 4 5 |
|
Многоуровневые сборки имеют большой потенциал, но даже для такого простого приложения, как наше, они заслуживают внимания.
5. Мавен и Докер
Давайте немного переключимся и посмотрим, как Apache Maven использует управление сборкой для приложений Dropwizard . Для этого подраздела вам потребуется установить Apache Maven 3.2.5
на компьютере разработчика (однако, если у вас уже установлен Apache Maven 3.2.1
или более поздней версии, вы можете просто придерживаться его).
Шаги, которые мы должны выполнить, в основном идентичны тем, что мы обсуждали для сборок Gradle , изменения в основном только в плагинах:
Плагин Maven Shade создает uber-jar, который позже будет использоваться плагином Spotify Docker Maven для создания образа Docker . Без суеты, давайте посмотрим на файл pom.xml
.
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
|
Честно говоря, это выглядит значительно более многословно, чем сборка Gradle , но если мы на секунду представим, что все теги XML пропали, мы получим в основном идентичное определение, по крайней мере, в случае плагинов Docker . Dockerfile немного отличается, хотя:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 |
|
Из-за особенностей приложения Dropwizard , мы должны связать файл конфигурации, в нашем случае application.yml
, вместе с приложением. Вместо того, чтобы выставлять только один порт 19900
, мы должны выставить другой, 19901
, для административных задач. И последнее, но не менее важное: мы предоставляем скрипт для инструкции ENTRYPOINT , docker-entrypoint.sh
.
01 02 03 04 05 06 07 08 09 10 |
|
Причиной добавления некоторой сложности здесь является то, что по умолчанию пакет дополнений Dropwizard Flyway не выполняет автоматическую миграцию схемы базы данных. Мы могли бы обойти это, но самый простой способ — запустить команду db migrate
перед запуском приложения Dropwizard . Это именно то, что мы делаем внутри сценария оболочки выше. Теперь пришло время запустить сборку!
01 02 03 04 05 06 07 08 09 10 11 12 |
|
Давайте посмотрим, будет ли наше изображение доступно локально на этот раз.
1 2 3 4 5 |
|
Отлично, предполагая, что контейнер MySQL запущен и работает (эта часть не меняется вообще, мы могли бы использовать ту же команду из предыдущего раздела ), мы могли бы просто запустить наш контейнер приложения Dropwizard .
1 2 3 4 5 6 |
|
Мы также отображаем порты 19900
и 19901
из контейнера на хост, чтобы мы могли использовать localhost
качестве имени хоста в curl .
01 02 03 04 05 06 07 08 09 10 11 |
|
1 2 3 4 5 6 7 8 9 |
|
1 2 3 4 5 6 7 |
|
Обратите внимание, что при сопоставлении портов хоста мы можем запустить либо jcg/dropwizard-webapp:0.0.1-SNAPSHOT
либо контейнер jcg/spring-boot-webapp:0.0.1-SNAPSHOT
, но не оба одновременно, так как к неизбежным конфликтам портов. Мы просто используем один и тот же порт для удобства, но в большинстве случаев вы будете использовать динамические привязки портов и не увидите, что эта проблема происходит.
6. Maven на докере
Та же техника использования многоэтапных сборок в равной степени применима к проектам, которые используют Apache Maven для сборки и управления зависимостями (к счастью, существуют официальные изображения Apache Maven, опубликованные на Docker Hub ).
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Немного добавить сюда, когда мы взломали работу многоступенчатых сборок , поэтому давайте создадим окончательный образ с помощью инструмента командной строки docker .
1 2 3 4 5 |
|
И убедитесь, что изображение появляется в списке доступных изображений Docker .
1 2 3 4 5 |
|
Это довольно круто, если честно. Прежде чем закончить обсуждение многоэтапных сборок , давайте коснемся варианта использования, с которым вы наверняка столкнетесь: проверка проекта из системы контроля версий. Примеры, которые мы видели до сих пор, предполагают, что проект доступен локально, но мы могли бы клонировать его из удаленного репозитория как часть определения многоступенчатых сборок .
7. Выводы
В этом разделе руководства мы увидели несколько примеров того, как популярные инструменты управления сборкой и зависимостями, а именно Apache Maven и Gradle , поддерживают упаковку приложений Java в виде образов Docker . Мы также потратили некоторое время на обсуждение многоэтапных сборок и возможностей, которые они открывают для реализации переносимых конвейеров сборки с нулевой зависимостью (буквально!).
8. Что дальше
В следующем разделе руководства мы рассмотрим, как Docker может упростить процессы и практики разработки, особенно в отношении работы с хранилищами данных и внешними (или даже внутренними) сервисами.
Полные исходные коды проекта доступны для скачивания .
Справочник по Dockerfile
Docker может автоматически создавать образы читая инструкции из Dockerfile
. Файл Dockerfile
представляет из себя текстовый документ содержащий все команды для сборки образа. С помощью команды docker build
пользователи могут производить автоматизированную сборку которая выполняет последовательность инструкций в командной строке.
На этой странице описаны команды, которые вы можете использовать в Dockerfile
. Когда вы ознакомитесь с этой главой, рекомендуем также прочесть Dockerfile лучшая практика.
Применение
Команда docker build
создает образ из Dockerfile
и контекста. Контекст сборки это файлы с заданным местоположением на локальном компьютере (PATH
) или URL
указывающим на Git репозиторий.
Контекст обрабатывается рекурсивно. Итак PATH
включает все поддиректории и URL
включает в себя репозиторий и его подмодули. Простая команда создания образа включающая в себя текущую директорию и ее контекст:
$ docker build .
Sending build context to Docker daemon 6.51 MB
...
Создание образа запускается Docker демоном, а не интерфейсом командной строки (CLI). Первым делом процесс сборки рекурсивно отправляет контекст демону. В большинстве случаев, лучше начать с пустого каталога в качестве контекста и сохранить в него Dockerfile. Добавляйте в него только те файлы которые будут использоваться в Dockerfile.
Предупреждение: Не используйте вашу корневую директорию,
/
, поскольку это вызовет передачу всего содержимого жесткого диска демону Docker в качестве контекста.
Для использования файла в контексте сборки образа, в Dockerfile
используют специальную инструкцию, к примеру COPY
. Для улучшения производительности при сборке образа, можно исключить ненужные файлы из контекста добавив в ненужный каталог файл .dockerignore
. Для подробной информации о том как создать файл .dockerignore
перейдите по ссылке.
Традиционно Dockerfile
размещается к корне контекста. Используйте флаг -f
с командой docker build
что бы задать другое местоположение Dockerfile
в файловой системе.
$ docker build -f /path/to/a/Dockerfile .
Вы можете указать репозиторий и тег что бы сохранить образ при успешной сборке:
$ docker build -t shykes/myapp .
Для того что бы пометить образ в нескольких репозиториях после сборки,
добавьте несколько параметров -t
когда вы запускаете команду сборки build
:
$ docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
Docker демон выполняет инструкции в Dockerfile
одну за другой, производя коммит результата каждой инструкции в новый образ если это необходимо, перед тем как вывести финальный ID вашего нового образа. Демон Docker автоматически очищает отправленный вами контекст.
Обратите внимание что каждая инструкция выполняется независимо и создает новый образ, по этому инструкция RUN cd /tmp
не будет оказывать эффект на следующие инструкции.
Всякий раз когда возможно, Docker будет использовать промежуточные образы (кэш), что существенно ускоряет процесс выполнения docker build
. При этом в консоли выводится сообщение Using cache
. (Для более подробной информации, читайте раздел кэш сборки) в разделе лучших практик по Dockerfile:
$ docker build -t svendowideit/ambassador .
Sending build context to Docker daemon 15.36 kB
Step 1 : FROM alpine:3.2
---> 31f630c65071
Step 2 : MAINTAINER [email protected]
---> Using cache
---> 2a1c91448f5f
Step 3 : RUN apk update && apk add socat && rm -r /var/cache/
---> Using cache
---> 21ed6e7fbb73
Step 4 : CMD env | grep _TCP= | (sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat -t 100000000 TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/' && echo wait) | sh
---> Using cache
---> 7ea8aef582cc
Successfully built 7ea8aef582cc
Формат
Рассмотрим формат Dockerfile
:
# Comment
INSTRUCTION arguments
Инструкция не чувствительна к регистру. Тем не менее, рекомендуется использовать ВЕРХНИЙ РЕГИСТР что бы было проще отличить инструкцию от ее аргументов.
Docker выполняет инструкции из Dockerfile
по порядку. Первой инструкцией должна быть `FROM` что бы задать базовый образ на основе которого будет происходить сборка.
Docker обрабатывает строки начинающиеся с #
как комментарии, если только строка не является директивой парсера. В любых других случаях маркер #
считается аргументом. Благодаря этому мы можем использовать следующие выражения:
# Comment
RUN echo 'we are running some # of cool things'
Соединение строк не поддерживается в комментариях.
Директивы парсера
Директивы парсера не являются обязательными, и влияют на способ которым обрабатываются следующие инструкции в Dockerfile
. Директивы не добавляют слои в сборку и не отображаются как отдельные шаги. Директивы парсера пишутся как особый вид комментариев # directive=value
. Одна директива может быть использована только один раз.
Как только комментарий, пустая строка или инструкция сборки были обработаны, Docker перестает искать директивы парсера. Вместо этого Докер рассматривает любую директиву как обычный комментарий. По этому, все директивы парсера должны быть в самом верху Dockerfile
.
Директивы парсера не чувствительны к регистру. Тем не менее мы рекомендуем использовать нижний регистр. Так же будет правильным отделять директивы от инструкций пустой строкой. Склеивание строк не поддерживается в директивах парсера.
Исходя из вышеизложенного следующие примеры будут не правильными:
Неправильно из-за переноса строк:
Не верно, т.к. одна директива используется дважды:
# directive=value1
# directive=value2
FROM ImageName
Считается комментарием поскольку используется после инструкции:
FROM ImageName
# directive=value
Считается комментарием поскольку используется после комментария который не является директивой:
# About my dockerfile
FROM ImageName
# directive=value
Неизвестная директива считается комментарием и не выполняется. Кроме того, известная директива рассматривается как комментарий если используется после комментария.
# unknowndirective=value
# knowndirective=value
Использования пробелов без переноса строк разрешается в директивах парсера. Таким образом следующие строки обрабатываются одинаково:
#directive=value
# directive =value
# directive= value
# directive = value
# dIrEcTiVe=value
Поддерживаются следующие директивы:
escape
Директива escape
устанавливает символ для экранирования в
Dockerfile
. Если не задан, то по умолчанию используется \
.
Экранирующий символ используется как для управляющих символов в строке, так и для разбиения длинных инструкций на несколько строк. Обратите внимание на то что в не зависимости от того задана ли директива escape
в Dockerfile
или нет, экранирование в команде RUN
поддерживается только в конце строки.
Установка экранирующего символа на `
особенно полезна в
Windows, где \
используется в путях файловой системы. `
совместим с Windows PowerShell.
Рассмотрим следующий пример который не очевидным образом вызовет ошибку в Windows
. Второй символ \
в конце второй строки будет интерпретирован как перенос на другую строку, вместо того чтобы экранироваться первым \
.
Точно также, \
в конце третьей строки, будет обрабатываться как инструкция, по тому что он будет воспринят как продолжение строки. В результате этого вторая и третья строки dockerfile будут рассматриваться как одна инструкция:
FROM windowsservercore
COPY testfile.txt c:\\
RUN dir c:\
PS C:\John> docker build -t cmd .
Sending build context to Docker daemon 3.072 kB
Step 1 : FROM windowsservercore
---> dbfee88ee9fd
Step 2 : COPY testfile.txt c:RUN dir c:
GetFileAttributesEx c:RUN: The system cannot find the file specified.
PS C:\John>
Одним из решений будет использование /
для инструкций COPY
и dir
.Тем не менее данный синтаксис, в лучшем случае путает, т.к. он не естественен для путей Windows
, а в худшем приведет к ошибкам, поскольку не все команды Windows
поддерживают /
в качестве разделителя путей.
При добавлении директивы парсера escape
, следующий Dockerfile
завершается успешно, как ожидалось с использованием естественной семантики платформы для путей к файлам Windows
:
# escape=`
FROM windowsservercore
COPY testfile.txt c:\
RUN dir c:\
PS C:\John> docker build -t succeeds --no-cache=true .
Sending build context to Docker daemon 3.072 kB
Step 1 : FROM windowsservercore
---> dbfee88ee9fd
Step 2 : COPY testfile.txt c:\
---> 99ceb62e90df
Removing intermediate container 62afbe726221
Step 3 : RUN dir c:\
---> Running in a5ff53ad6323
Volume in drive C has no label.
Volume Serial Number is 1440-27FA
Directory of c:\
03/25/2016 05:28 AM <DIR> inetpub
03/25/2016 04:22 AM <DIR> PerfLogs
04/22/2016 10:59 PM <DIR> Program Files
03/25/2016 04:22 AM <DIR> Program Files (x86)
04/18/2016 09:26 AM 4 testfile.txt
04/22/2016 10:59 PM <DIR> Users
04/22/2016 10:59 PM <DIR> Windows
1 File(s) 4 bytes
6 Dir(s) 21,252,689,920 bytes free
---> 2569aa19abef
Removing intermediate container a5ff53ad6323
Successfully built 2569aa19abef
PS C:\John>
Замена переменных окружения
Переменные окружения (объявляются с помощью инструкции ENV
) могут быть также использованы в других инструкциях как переменные интерпретируемые в Dockerfile
. Экранирование также обрабатывается для подстанавливаемых переменных.
Environment variables are notated in the Dockerfile
either with
$variable_name
or ${variable_name}
. They are treated equivalently and the
brace syntax is typically used to address issues with variable names with no
whitespace, like ${foo}_bar
.
Синтаксис ${variable_name}
поддерживает несколько стандартных bash модификаторов:
${variable:-word}
означает что если переменнаяvariable
не задана, то будет возвращено значениеword
.${variable:+word}
означает что если переменнаяvariable
задана, то будет возвращено значениеword
в противном случае будет возвращена пустая строка.
В любом случае, вместо word
может быть любая строка, в том числе другая переменная окружения.
Экранирование осуществляется добавлением \
перед переменной, к примеру: \$foo
или \${foo}
, будет переведено в $foo
и ${foo}
соответственно.
Пример (результат показан после #
):
FROM busybox
ENV foo /bar
WORKDIR ${foo} # WORKDIR /bar
ADD . $foo # ADD . /bar
COPY \$foo /quux # COPY $foo /quux
Переменные окружения поддерживаются в следующих инструкциях Dockerfile
:
ADD
COPY
ENV
EXPOSE
LABEL
USER
WORKDIR
VOLUME
STOPSIGNAL
ONBUILD
(когда комбинируется с одной из поддерживаемых инструкций)
Примечание:
до версии 1.4,ONBUILD
не поддерживала переменные окружения, даже в сочетании с какой-либо из инструкций, перечисленных выше.
Переменные окружения при замещении будут использовать то же значение для каждой переменной на протяжении всей команды. Другими словами, в этом примере:
ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc
в результате def
будет равно hello
, но не bye
. Однако,
ghi
будет равно bye
по тому что выражение не является частью команды в которой abc
было присвоено значение bye
.
Файл .dockerignore
Перед тем как Докер CLI отправляет контекст демону, он проверяет наличие файла .dockerignore
в корневой папке контекста.
Если этот файл существует, CLI модифицирует контекст удаляя из него файлы и папки перечисленные в .dockerignore
. Это помогает избежать
отправки лишних или конфиденциальных файлов и каталогов демону и их добавления в образ командой ADD
или COPY
.
Интерфейс командной строки интерпретирует .dockerignore
файл как список паттернов. Для целей сопоставления, корень контекста считается как рабочий и корневой каталог. К примеру, паттерны
/foo/bar
и foo/bar
исключают файл или каталог с именем bar
в подкаталоге foo
контекста или корне репозитория git
расположенного по заданному URL
.
Если строка в файле .dockerignore
начинается с #
, то строка считается комментарием.
Вот пример .dockerignore
файла:
# comment
*/temp*
*/*/temp*
temp?
Этот файл обрабатывается следующим образом при сборке:
Правило | Поведение |
---|---|
# comment | Игнорируется. |
*/temp* | Исключаются файлы и каталоги имена которых начинаются с temp в любой поддиректории корня. К примеру, данный файл /somedir/temporary.txt будет исключен, как и этот каталог /somedir/temp . |
*/*/temp* | Исключает файлы и каталоги чье название начинается с temp во всех каталогах второго уровня. К примеру, /somedir/subdir/temporary.txt . |
temp? | Исключает файлы и папки в корневой директории чьи имена отличаются на один символ от temp . К примеру, /tempa и /tempb будут исключены. |
Сравнение происзводится с помощью Go
filepath.Match правил. Перед обработкой удаляются лишние пробелы и элементы .
, ..
с помощью Go
filepath.Clean. Строки оставшиеся пустыми после предобработки игнорируются.
Помимо правил Go filepath.Match, Docker также поддерживает строку **
которая соответствует любому количеству каталогов (включая ноль). К примеру, **/*.go
исключит все файлы заканчивающиеся на .go
найденные во всех каталогах, включая корень контекста сборки.
Строки начинающиеся с !
(восклицательный знак) могут быть использованы для создания исключений. Вот пример с использованием данного механизма в .dockerignore
файле:
Все файлы с расширением .md
за исключением README.md
не будут добавлены в контекст.
Использование правила исключения !
влияет на поведение: последняя строка .dockerignore
которая соответствует конкретному файлу определяет будет ли он исключен. Рассмотрим следующий пример:
*.md
!README*.md
README-secret.md
Файлы с названием README.md
будут включены в контекст, за исключением README-secret.md
.
Теперь рассмотрим немного другой пример:
*.md
README-secret.md
!README*.md
Все файлы README будут включены. Средняя строка не оказывает никакого эффекта, по тому что шаблон !README*.md
отменяет действие README-secret.md
и используется в самом конце.
Вы даже можете использовать .dockerignore
файл для исключения Dockerfile
и .dockerignore
файлов. Эти файлы будут по прежнему отправляться демону по тому что необходимы для его работы. Но команды ADD
и COPY
не смогут скопировать их в образ.
И наконец, вы можете указать, какие файлы включить в контекст, а не исключать. Для достижения этой цели, укажите *
в качестве первого паттерна, за которым следуют один или более !
исключающих паттернов.
Примечание: По историческим причинам, паттерн .
игнорируется.
FROM
Инструкция FROM
задает базовый образ
для последующих инструкций. Dockerfile
обязательно должен иметь инструкцию FROM
. Можно использовать любой работающий образ, проще всего начать с загрузки образа из публичного репозитория.
FROM
должен быть первой инструкцией вDockerfile
(не считая комментариев и директив парсера).FROM
может использоваться несколько раз в пределах одногоDockerfile
для создания нескольких образов. Просто отслеживайте последний ID образа перед каждой новой командойFROM
.Значения
tag
илиdigest
не обязательны. Если любая из этих опций не задана Докер по умолчанию использует значениеlatest
. Сборщик Docker возвращает ошибку если значениеtag
не найдено.
MAINTAINER
Инструкция MAINTAINER
позволяет указать автора образа.
RUN
RUN имеет две формы:
RUN <command>
(shell форма, команда выполняется в шеле, по умолчанию/bin/sh -c
для Linux илиcmd /S /C
для Windows)RUN ["executable", "param1", "param2"]
(exec форма)
Инструкция RUN
выполняет любые команды в новом слое поверх текущего образа и делает коммит результата. Полученный после коммита образ будет использован для следующего шага в
Dockerfile
.
Создание слоев инструкцией RUN
и последующий их коммит является базовой концепцией Docker, которая позволяет создать контейнер из любой точки истории образа, по аналогии с системами контроля версий.
exec форма выполнения команд позволяет разбивать строку команды и выполнять команды используя базовый образ который не имеет исполняемого файла оболочки.
Для shell формы оболочка по-умолчанию может быть изменена с помощью команды SHELL
.
В shell форме вы можете использовать \
(обратный слеш) в инструкциях
RUN для переноса команды на следующую строку. К примеру рассмотрим две следующих строки:
RUN /bin/bash -c 'source $HOME/.bashrc ;\
echo $HOME'
Вместе они эквивалентны строке:
RUN /bin/bash -c 'source $HOME/.bashrc ; echo $HOME'
Примечание:
Для использование другой оболочки отличной от /bin/sh, используйте exec форму выполнения командыRUN ["/bin/bash", "-c", "echo hello"]
Примечание:
Exec форма обрабатывается как JSON массив, это означает что вы должны разделять слова используя двойные кавычки (“) и ни в коем случае одинарные (‘).
Примечание:
В отличие от shell формы, exec форма не вызывает командную оболочку.
Это означает что обработки в нормальной оболочке не происходит. К примеру,RUN [ "echo", "$HOME" ]
не будет осуществлять подстановку переменной$HOME
.
Если вам нужна обработка в оболочке, используйте shell форму или запускайте оболочку напрямую, к примеру:RUN [ "sh", "-c", "echo $HOME" ]
.
При использовании exec формы и запуска оболочки напрямую, как в случае с
shell формой, подстановка переменных осуществляется оболочкой а не docker.
Примечание:
При формате команды в виде JSON, необходимо экранировать обратные слеши. Это особенно актуально в Windows, где обратный слеш является разделителем пути.
Следующая строка будет рассматриваться как shell форма а не JSON как это могло бы ожидаться:
RUN ["c:\windows\system32\tasklist.exe"]
Правильным синтаксисом в данном случае будет:
RUN ["c:\\windows\\system32\\tasklist.exe"]
Кэш для инструкций RUN
остается нетронутым до следующей сборки. Например кэш для инструкции RUN apt-get dist-upgrade -y
будет повторно использован при следующей сборке. Кэш инструкции RUN
может быть сброшен флагом --no-cache
, к примеру docker build --no-cache
.
Кэш для инструкций RUN
может быть отменен добавлением инструкций. Что бы узнать подробности читайте дальше.
Известные проблемы (RUN)
- Issue 783 о проблемах с правами доступа, которые могут возникнуть при использовании AUFS файловой системы. Вы можете заметить это при попытке переименовать файл.
Для систем имеющих последнюю версию aufs (может быть установлена опция монтирования dirperm1
), docker попытается автоматически исправить ошибку смонтировав слои с опцией dirperm1
. Больше информации об опции dirperm1
вы можете найти на странице мануала по aufs
Если ваша система не поддерживает dirperm1
, в Issue 783 также указано альтернативное решение.
CMD
Инструкция CMD
имеет три формы:
CMD ["executable","param1","param2"]
(exec форма, является предпочтительной)CMD ["param1","param2"]
(в качестве параметров по умолчанию для ENTRYPOINT)CMD command param1 param2
(shell форма)
Инструкция CMD
может быть использована только один раз в Dockerfile
. Если вы используете больше одной CMD
, то только последняя инструкция будет работать.
Основное предназначение CMD
передача параметров по-умолчанию для запуска контейнера. Эти значения по умолчанию могут включать в себя исполняемый файл, или же они могут опустить исполняемый файл, но в этом случае вы должны использовать инструкцию ENTRYPOINT
.
Примечание:
ЕслиCMD
используется для передачи аргументов по-умолчанию для инструкцииENTRYPOINT
, обе инструкцииCMD
иENTRYPOINT
должны быть в JSON формате.
Примечание:
exec форма обрабатывается как JSON массив, что подразумевает
использование двойных кавычек («) для слов и ни в коем случае одинарных (‘).
Примечание:
В отличие от shell формы, exec форма не вызывает командную оболочку. Это означает что стандартной для оболочки обработки не происходит. К примеру,CMD [ "echo", "$HOME" ]
не производит подстановки переменной$HOME
.
Если вам нужна обработка оболочкой, используйте shell форму или запускайте оболочку напрямую, например так:CMD [ "sh", "-c", "echo $HOME" ]
.
При использовании exec формы и запуске оболочки напрямую, в случае с shell формой, именно оболочка осуществляет обработку переменных окружения, а не docker.
При использовании shell или exec форматов, инструкция CMD
задает команду которая будет выполнена при запуске образа.
Если вы используете shell форму инструкции CMD
, то команда <command>
будет выполнена в
/bin/sh -c
:
FROM ubuntu
CMD echo "This is a test." | wc -
Если вы хотите запустить команду <command>
без оболочки, то вы должны написать команду в формате JSON массива и указать полный путь к исполняемому файлу. Этот формат является предпочтительным для CMD
. Любые дополнительные параметры должны быть отдельно перечислены в массиве:
FROM ubuntu
CMD ["/usr/bin/wc","--help"]
Если вы хотите что бы ваш контейнер запускал один и тот же исполняемый файл каждый раз, то вам стоит использовать инструкцию ENTRYPOINT
в комбинации с CMD
. Читайте подробнее о инструкции ENTRYPOINT.
Если пользователь задает аргументы для docker run
то они переопределяют аргументы по-умолчанию из CMD
.
Примечание:
Не путайте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."
Образ может иметь несколько лейблов. Для этого,
Docker рекомендует комбинировать лейблы в одной инструкции LABEL
. Каждая инструкция LABEL
создает новый слой, а большое количество слоев может негативно сказаться на скорости запуска образа. В этом примере мы покажем как сделать все в одном слое:
LABEL multi.label1="value1" multi.label2="value2" other="value3"
Так же можно использовать такую форму записи:
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
Лейблы являются аддитивными, в том числе и те которые использованы в базовом образе (FROM
). Если Docker встречает лейбл который уже существует, то старое значение будет заменено новым.
Для просмотра лейблов образа используйте команду docker inspect
.
"Labels": {
"com.example.vendor": "ACME Incorporated"
"com.example.label-with-value": "foo",
"version": "1.0",
"description": "This text illustrates that label-values can span multiple lines.",
"multi.label1": "value1",
"multi.label2": "value2",
"other": "value3"
},
EXPOSE
EXPOSE <port> [<port>...]
Инструкция EXPOSE
указывает Docker что контейнер слушает определенные порты после запуска. EXPOSE
не делает порты контейнера доступными для хоста. Для этого, вы должны использовать флаг -p
(что бы открыть диапазон портов) или флаг -P
что бы открыть все порты из EXPOSE. Можно задать один номер порта и пробросить его на другой внешний порт.
ENV
ENV <key> <value>
ENV <key>=<value> ...
Инструкция ENV
задает переменные окружения с именем <key>
и значением <value>
. Это значение будет находиться в окружении всех команд потомков Dockerfile
и могут быть использованы как обычные переменные окружения.
Инструкция ENV
имеет две формы. Первая форма, ENV <key> <value>
,
устанавливает значение одной переменной. Вся строка после первого пробела будет рассматриваться как <value>
— включая пробелы и кавычки.
Вторая форма, ENV <key>=<value> ...
, позволяет задать сразу несколько переменных. Обратите внимание что вторая форма использует в синтаксисе знак равенства (=), в то время как для первой формы это не нужно. Как и в случае разбора командной строки, ковычки и обратные слеши могут быть использованы для включения пробелов в значениях.
ENV myName="John Doe" myDog=Rex\ The\ Dog \
myCat=fluffy
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy
Оба примера приведут к одному результату в контейнере, но первый вариант предпочтительней, поскольку он создаст только один слой.
Переменные окружения заданные с помощью ENV
будут доступны даже когда из образа будет запущен контейнер. Вы можете посмотреть переменные с помощью команды docker inspect
, и
изменить их командой docker run --env <key>=<value>
.
Примечание:
Доступность окружения может вызывать неожиданные побочные эффекты. К примеру инструкцияENV DEBIAN_FRONTEND noninteractive
может вызвать сбой apt-get в образах собранных из Debian. Для задания одноразовой переменной используйтеRUN <key>=<value> <command>
.
ADD
ADD имеет две формы:
ADD <src>... <dest>
ADD ["<src>",... "<dest>"]
(эта форма обязательна для путей с пробелами)
Инструкция ADD
копирует новые файлы, папки или или удаленные файлы по URLs из <src>
и добавляет их в файловую систему контейнера в <dest>
.
Возможно множественное задание <src>
, но
пути к файлам и папкам должны быть относительными для контекста сборки (папки с Dockerfile).
Каждый <src>
может содержать групповые символы (wildcards) обработка которых осуществляется с использованием правил Go
filepath.Match. К примеру:
ADD hom* /mydir/ # adds all files starting with "hom"
ADD hom?.txt /mydir/ # ? is replaced with any single character, e.g., "home.txt"
<dest>
абсолютный, или относительный путь для WORKDIR
, куда будет произведено копирование в файловую систему контейнера.
ADD test relativeDir/ # adds "test" to `WORKDIR`/relativeDir/
ADD test /absoluteDir/ # adds "test" to /absoluteDir/
Все файлы и папки создаются с UID и GID равными 0.
В случае когда <src>
представляет из себя URL удаленного файла, на копируемый файл ставятся права 600. Если вместе с получаемым файлам есть HTTP заголовок
Last-Modified
, дата и время из заголовка будут использованы для установки mtime
. Однако, как и для любого другого файла обрабатываемого инструкцией ADD
, mtime
не используется для определения изменений в файле при которых происходит очищение кэша.
Примечание:
Если вы собираете образ переда
Ссылка на файл Dockerfile
| Документация Docker
Расчетное время чтения: 81 минута
Docker может автоматически создавать образы, читая инструкции из
Dockerfile
. Dockerfile
— это текстовый документ, содержащий все команды
пользователь может вызвать командную строку для сборки изображения. Использование docker build
пользователи могут создать автоматизированную сборку, которая выполняет несколько командных строк
инструкции по порядку.
На этой странице описаны команды, которые можно использовать в файле Dockerfile
.Когда вы
прочитав эту страницу, обратитесь к Dockerfile
Best
Практики для ориентированного на чаевые гида.
Использование
Команда docker build создает образ из
файл Dockerfile
и контекст . Контекст сборки — это набор файлов в
указанное местоположение PATH
или URL
. PATH
— это каталог на вашем локальном
файловая система. URL-адрес
— это расположение репозитория Git.
Контекст обрабатывается рекурсивно.Итак, PATH
включает все подкаталоги и
URL-адрес
включает репозиторий и его подмодули. Этот пример показывает
команда сборки, которая использует текущий каталог в качестве контекста:
$ сборка докеров.
Отправка контекста сборки демону Docker 6.51 МБ
...
Сборка выполняется демоном Docker, а не интерфейсом командной строки. Первым делом сборка
процесс отправляет весь контекст (рекурсивно) демону. В большинстве
случаев лучше всего начать с пустого каталога в качестве контекста и сохранить
Dockerfile в этом каталоге.Добавьте только файлы, необходимые для создания
Dockerfile.
Предупреждение
Не используйте корневой каталог
/
в качествеПУТЬ
, так как это вызывает сборку
передать все содержимое вашего жесткого диска демону Docker.
Чтобы использовать файл в контексте сборки, Dockerfile
ссылается на указанный файл
в инструкции, например, COPY
. Для увеличения сборки
производительности, исключите файлы и каталоги, добавив .dockerignore
в
каталог контекста. Для получения информации о том, как создать .dockerignore
файл см. документацию на этой странице.
Традиционно Dockerfile
называется Dockerfile
и находится в корне
контекста. Вы используете флаг -f
с docker build
, чтобы указать на Dockerfile
в любом месте вашей файловой системы.
$ docker build -f / путь / к / a / Dockerfile.
Вы можете указать репозиторий и тег для сохранения нового изображения, если
сборка выполнена успешно:
$ docker build -t shykes / myapp.
Чтобы пометить изображение в нескольких репозиториях после сборки,
добавить несколько параметров -t
при запуске команды build
:
$ docker build -t shykes / myapp: 1.0.2 -t shykes / myapp: latest.
Перед тем, как демон Docker выполнит инструкции из файла Dockerfile
, он выполняет
предварительная проверка Dockerfile
и возвращает ошибку, если синтаксис неверен:
$ docker build -t test / myapp.Отправка контекста сборки демону Docker 2.048 kB
Ответ демона об ошибке: Неизвестная инструкция: RUNCMD
Демон Docker последовательно запускает инструкции из файла Dockerfile
,
фиксация результата каждой инструкции
к новому изображению, если необходимо, прежде чем, наконец, вывести идентификатор вашего
новое изображение. Демон Docker автоматически очистит контекст, который вы
послал.
Обратите внимание, что каждая инструкция выполняется независимо и вызывает новый образ.
будет создан — поэтому RUN cd / tmp
не повлияет на следующий
инструкции.
Когда это возможно, Docker будет повторно использовать промежуточные образы (кеш),
для значительного ускорения процесса сборки докеров
. На это указывает
сообщение Using cache
в выводе консоли.
(Дополнительные сведения см. В руководстве по передовым методам Dockerfile
:
$ docker build -t svendowideit / ambassador.
Отправка контекста сборки демону Docker 15,36 КБ
Шаг 1/4: ИЗ альпийского: 3.2
---> 31f630c65071
Шаг 2/4: ОБСЛУЖИВАНИЕ SvenDowideit @ home.org.au
---> Использование кеша
---> 2a1cf5f
Шаг 3/4: ЗАПУСТИТЕ apk update && apk add socat && rm -r / var / cache /
---> Использование кеша
---> 21ed6e7fbb73
Шаг 4/4: CMD env | grep _TCP = | (sed 's /.*_ ПОРТ _ \ ([0-9] * \) _ TCP = tcp: \ / \ / \ (. * \): \ (. * \) / socat -t 100000000 TCP4-LISTEN: \ 1 , fork, reuseaddr TCP4: \ 2: \ 3 \ & / '&& echo wait) | ш
---> Использование кеша
---> 7ea8aef582cc
Успешно построенный 7ea8aef582cc
Кэш сборки используется только для образов, имеющих локальную родительскую цепочку.Это означает
что эти образы были созданы предыдущими сборками или всей цепочкой образов
был загружен docker load
. Если вы хотите использовать кеш сборки определенного
image вы можете указать его с помощью опции --cache-from
. Изображения, указанные с
--cache-from
не обязательно иметь родительскую цепочку и может быть извлечен из другой
реестры.
Когда вы закончите сборку, вы готовы изучить Pushing a
репозиторий в свой реестр .
BuildKit
Начиная с версии 18.09, Docker поддерживает новый бэкэнд для выполнения ваших
сборки, предоставляемые moby / buildkit
проект. Бэкэнд BuildKit предоставляет множество преимуществ по сравнению со старым
реализация. Например, BuildKit может:
- Обнаружение и пропуск неиспользуемых этапов сборки
- Распараллелить независимые этапы сборки
- Постепенно переносите только измененные файлы в контексте сборки между сборками
- Обнаружение и пропуск передачи неиспользуемых файлов в контексте сборки
- Используйте внешние реализации Dockerfile со многими новыми функциями
- Избегайте побочных эффектов с остальной частью API (промежуточные изображения и контейнеры)
- Установите приоритет кэша сборки для автоматического удаления
Чтобы использовать бэкэнд BuildKit, вам необходимо установить переменную среды
DOCKER_BUILDKIT = 1
в интерфейсе командной строки перед вызовом сборки докера
.
Чтобы узнать об экспериментальном синтаксисе Dockerfile, доступном для BuildKit-based
сборки относятся к документации в репозитории BuildKit.
Формат
Вот формат файла Dockerfile
:
# Комментарий
ИНСТРУКЦИЯ аргументы
В инструкции регистр не учитывается. Однако по соглашению они
быть ЗАГЛАВНЫМ, чтобы легче отличать их от аргументов.
Docker запускает инструкции в файле Dockerfile
по порядку. Dockerfile
должен
Начните с инструкции ОТ
. Это может быть после парсера
директивы, комментарии и глобальная область видимости
ARG. Инструкция FROM
указывает Parent
Изображение , с которого вы находитесь
здание. ИЗ
может предшествовать только одна или несколько инструкций ARG
, которые
объявить аргументы, которые используются в из строк
в файле Dockerfile
.
Docker обрабатывает строки, в которых начинается с с #
, как комментарий, если только строка не
допустимая директива парсера.Маркер #
где угодно
else в строке рассматривается как аргумент. Это позволяет использовать такие утверждения, как:
# Комментарий
RUN echo "мы запускаем несколько интересных вещей"
Строки комментариев удаляются перед выполнением инструкций Dockerfile, которые
означает, что комментарий в следующем примере не обрабатывается оболочкой
выполнение команды echo
, и оба приведенных ниже примера эквивалентны:
RUN echo hello \
# комментарий
Мир
Символы продолжения строки в комментариях не поддерживаются.
Примечание о пробеле
Для обратной совместимости начальные пробелы перед комментариями (
#
) и
инструкции (такие какRUN
) игнорируются, но не рекомендуется. Ведущие пробелы
не сохраняется в этих случаях, поэтому следующие примеры
эквивалент:# это строка комментария RUN echo привет Беги эхо мир
# это строка комментария RUN echo привет Беги эхо мир
Обратите внимание, однако, что пробел в команде аргументов , таких как команды
следующиеRUN
, сохраняются, поэтому в следующем примере печатается `hello world`
с ведущими пробелами, как указано:RUN echo "\ Здравствуйте\ Мир"
Директивы парсера
Директивы парсера необязательны и влияют на способ, которым последующие строки
в файле Dockerfile обрабатываются
.Директивы парсера не добавляют слои в сборку,
и не будет отображаться как этап сборки. Директивы парсера записываются как
специальный тип комментария в виде # директива = значение
. Единая директива
можно использовать только один раз.
После обработки комментария, пустой строки или инструкции конструктора Docker
больше не ищет директивы парсера. Вместо этого он обрабатывает все отформатированное
как директива парсера в качестве комментария и не пытается проверить, может ли он
быть директивой парсера.Следовательно, все директивы парсера должны быть в самом
верхняя часть Dockerfile
.
Директивы парсера не чувствительны к регистру. Однако по соглашению они
быть строчными. Соглашение также должно включать пустую строку после любого
директивы парсера. Символы продолжения строки не поддерживаются парсером
директивы.
Из-за этих правил все следующие примеры недействительны:
Недействителен из-за продолжения строки:
Недействителен из-за двукратного появления:
# директива = значение1
# директива = значение2
ОТ ImageName
Считается комментарием из-за появления после инструкции строителя:
ИЗ ImageName
# директива = значение
Считается комментарием из-за появления после комментария, не являющегося синтаксическим анализатором
директива:
# О моем dockerfile
# директива = значение
ОТ ImageName
Директива unknown обрабатывается как комментарий, так как не распознается.В
кроме того, известная директива обрабатывается как комментарий из-за появления после
комментарий, который не является директивой парсера.
# unknowndirective = значение
# knowndirective = значение
В директиве синтаксического анализатора разрешены пробелы, не прерывающие строку. Следовательно
следующие строки обрабатываются одинаково:
# директива = значение
# директива = значение
# директива = значение
# директива = значение
# dIrEcTiVe = значение
Поддерживаются следующие директивы парсера:
синтаксис
# syntax = [ссылка на удаленное изображение]
Например:
# синтаксис = docker / dockerfile
# синтаксис = docker / dockerfile: 1.0
# синтаксис = docker.io / docker / dockerfile: 1
# syntax = docker / dockerfile: 1.0.0-экспериментальный
# синтаксис = example.com / user / repo: tag @ sha256: abcdef ...
Эта функция доступна, только если используется серверная часть BuildKit.
Синтаксическая директива определяет расположение построителя Dockerfile, который используется для
создание текущего Dockerfile. Бэкэнд BuildKit позволяет легко использовать
внешние реализации построителей, которые распространяются как образы Docker и
выполнить внутри среды песочницы контейнера.
Реализация Custom Dockerfile позволяет:
- Автоматически получать исправления без обновления демона
- Убедитесь, что все пользователи используют одну и ту же реализацию для создания вашего файла Docker.
- Используйте новейшие функции без обновления демона
- Попробуйте новые экспериментальные или сторонние функции
Официальные релизы
Docker распространяет официальные версии образов, которые можно использовать для сборки
Dockerfiles в репозитории docker / dockerfile
в Docker Hub.Есть два
каналы, на которых выпускаются новые изображения: стабильные и экспериментальные.
Стабильный канал следует за семантическим управлением версиями. Например:
-
docker / dockerfile: 1.0.0
— разрешить только неизменяемую версию1.0.0
-
docker / dockerfile: 1.0
— разрешить версии1.0. *
-
docker / dockerfile: 1
— разрешить версии1. *. *
-
docker / dockerfile: latest
— последний выпуск на стабильном канале
Экспериментальный канал использует инкрементное управление версиями с основным и второстепенным
компонент из стабильного канала на момент релиза.Например:
-
docker / dockerfile: 1.0.1-экспериментальный
— разрешить только неизменяемую версию1.0.1-экспериментальный
-
docker / dockerfile: 1.0-experimental
— последние экспериментальные выпуски после1.0
-
docker / dockerfile: экспериментальный
— последний выпуск на экспериментальном канале
Вам следует выбрать канал, который лучше всего соответствует вашим потребностям. Если ты только хочешь
исправления ошибок, вы должны использовать docker / dockerfile: 1.0
. Если вы хотите извлечь выгоду из
экспериментальные функции, вам следует использовать экспериментальный канал. Если вы используете
экспериментальный канал, более новые выпуски могут не иметь обратной совместимости, поэтому
рекомендуется использовать неизменяемый вариант полной версии.
Основные сборки и ночные выпуски функций см. В описании в
исходный репозиторий.
побег
или
Директива escape
устанавливает символ, используемый для escape-символов в
Dockerfile
.Если не указан, escape-символ по умолчанию — \
.
Управляющий символ используется как для экранирования символов в строке, так и для
избежать новой строки. Это позволяет инструкции Dockerfile
охватывают несколько строк. Обратите внимание, что независимо от того, экранирует ли парсер
директива включена в Dockerfile
, экранирование не выполняется в
команда RUN
, кроме конца строки.
Установка escape-символа на `
особенно полезна на
Windows
, где \
— разделитель путей к каталогам. `
согласован
с Windows PowerShell.
Рассмотрим следующий пример, который неочевидным образом дает сбой на
Windows
. Второй \
в конце второй строки будет интерпретироваться как
escape для новой строки вместо цели escape из первых \
.
Точно так же \
в конце третьей строки будет, если предположить, что это действительно
обрабатывается как инструкция, потому что она рассматривается как продолжение строки.Результат
этого файла докеров заключается в том, что вторая и третья строки считаются одним
инструкция:
С microsoft / nanoserver
КОПИРОВАТЬ testfile.txt c: \\
RUN dir c: \
Результатов в:
PS C: \ John> сборка докеров -t cmd.
Отправка контекста сборки демону Docker 3.072 kB
Шаг 1/2: С microsoft / nanoserver
---> 22738ff49c6d
Шаг 2/2: КОПИРОВАТЬ testfile.txt c: \ RUN dir c:
GetFileAttributesEx c: RUN: системе не удается найти указанный файл.
PS C: \ Джон>
Одним из решений вышеперечисленного было бы использование /
в качестве цели как для COPY
инструкция и директория
.Однако этот синтаксис в лучшем случае сбивает с толку, поскольку это не так.
естественно для путей на Windows
и в худшем случае подвержен ошибкам, так как не все команды на
Windows
поддерживает /
в качестве разделителя пути.
При добавлении директивы парсера escape
следующий Dockerfile
преуспеет как
ожидается с использованием естественной семантики платформы для путей к файлам в Windows
:
# escape = `
С microsoft / nanoserver
КОПИРОВАТЬ тестовый файл.txt c: \
RUN dir c: \
Результатов в:
PS C: \ John> docker build -t завершается успешно --no-cache = true.
Отправка контекста сборки демону Docker 3.072 kB
Шаг 1/3: С microsoft / nanoserver
---> 22738ff49c6d
Шаг 2/3: КОПИРОВАТЬ testfile.txt c: \
---> 96655de338de
Снятие промежуточного контейнера 4db9acbb1682
Шаг 3/3: ЗАПУСК dir c: \
---> Запуск в a2c157f842f5
Том на диске C не имеет метки.
Серийный номер тома 7E6D-E0F7.
Каталог c: \
05.10.2016 17:04 1,894 Лицензия.текст
05.10.2016 14:22 Программные файлы
05.10.2016 14:14 Программные файлы (x86)
28.10.2016 11:18 62 testfile.txt
28.10.2016 11:20 Пользователи
28.10.2016 11:20 Windows
2 Файл (ы) 1,956 байт
4 Dir (s) 21,259,096,064 байта свободно
---> 01c7f3bef04f
Снятие промежуточного контейнера a2c157f842f5
Успешно построен 01c7f3bef04f
PS C: \ Джон>
Замена окружающей среды
Переменные среды (объявленные с помощью оператора ENV
) также могут быть
используется в определенных инструкциях как переменные, которые должны интерпретироваться
Dockerfile
.Экраны также обрабатываются для включения синтаксиса, подобного переменному
в заявление буквально.
Переменные среды обозначены в Dockerfile
либо с
$ имя_переменной
или $ {имя_переменной}
. К ним относятся одинаково, и
синтаксис скобок обычно используется для решения проблем с именами переменных без
пробел, например $ {foo} _bar
.
Синтаксис $ {variable_name}
также поддерживает некоторые из стандартных bash
модификаторы, указанные ниже:
-
$ {variable: -word}
указывает, что еслипеременная
установлена, то результат
будет это значение.Еслипеременная
не установлена, результатом будетслово
. -
$ {переменная: + слово}
указывает, что еслипеременная
установлена, тослово
будет
результат, иначе результатом будет пустая строка.
Во всех случаях слово
может быть любой строкой, включая дополнительную среду
переменные.
Экранирование возможно путем добавления \
перед переменной: \ $ foo
или \ $ {foo}
,
например, будет преобразовано в литералы $ foo
и $ {foo}
соответственно.
Пример (проанализированное представление отображается после #
):
ОТ busybox
ENV FOO = / бар
WORKDIR $ {FOO} # WORKDIR / бар
ДОБАВИТЬ . $ FOO # ДОБАВИТЬ. /бар
COPY \ $ FOO / quux # COPY $ FOO / quux
Переменные среды поддерживаются следующим списком инструкций в
файл Dockerfile
:
-
ДОБАВИТЬ
-
КОПИЯ
-
ENV
-
EXPOSE
-
ИЗ
-
ТАБЛИЧКА
-
СИГНАЛ ОСТАНОВА
-
ПОЛЬЗОВАТЕЛЬ
-
ОБЪЕМ
-
WORKDIR
-
ONBUILD
(в сочетании с одной из поддерживаемых инструкций выше)
При замене переменной среды будет использоваться одно и то же значение для каждой переменной
на протяжении всей инструкции.Другими словами, в этом примере:
ENV abc = привет
ENV abc = пока def = $ abc
ENV ghi = $ abc
приведет к def
, имеющему значение hello
, а не bye
. Однако,
ghi
будет иметь значение bye
, потому что он не является частью той же инструкции
которые устанавливают abc
на bye
.
.dockerignore файл
Прежде чем интерфейс командной строки докера отправит контекст демону докера, он выглядит
для файла с именем .dockerignore
в корневом каталоге контекста.
Если этот файл существует, CLI изменяет контекст, чтобы исключить файлы и
каталоги, соответствующие шаблонам в нем. Это помогает избежать
без необходимости отправлять большие или конфиденциальные файлы и каталоги на
daemon и потенциально добавляя их к изображениям, используя ADD
или COPY
.
CLI интерпретирует файл .dockerignore
как разделенный новой строкой
список шаблонов, похожих на файловые глобусы оболочек Unix.Для
в целях сопоставления корень контекста считается как
рабочий и корневой каталог. Например, выкройки
/ foo / bar
и foo / bar
оба исключают файл или каталог с именем bar
в подкаталоге foo
PATH
или в корне git
репозиторий URL
. Ни то, ни другое не исключает ничего.
Если строка в файле .dockerignore
начинается с #
в столбце 1, то эта строка
считается комментарием и игнорируется перед интерпретацией CLI.
Вот пример файла .dockerignore
:
# comment
* / темп *
* / * / темп *
темп?
Этот файл вызывает следующее поведение сборки:
Правило | Поведение |
---|---|
# комментарий | Игнорируется. |
* / темп * | Исключить файлы и каталоги, имена которых начинаются с temp , из любого непосредственного подкаталога корня.Например, простой файл /somedir/ Contemporary.txt исключается, как и каталог / somedir / temp . |
* / * / темп * | Исключить файлы и каталоги, начинающиеся с temp , из любого подкаталога, находящегося на два уровня ниже корня. Например, /somedir/subdir/ Contemporary.txt исключен. |
темп? | Исключить из корневого каталога файлы и каталоги, имена которых имеют односимвольное расширение temp .Например, / tempa и / tempb исключаются. |
Сопоставление выполняется с помощью Go
путь к файлу. правила соответствия. А
шаг предварительной обработки удаляет начальные и конечные пробелы и
устраняет .
и ..
элементов с использованием Go
filepath.Clean. Линии
пустые после предварительной обработки игнорируются.
Путь к файлу
Beyond Go. Соответствие правилам, Docker также поддерживает специальный
строка подстановочного знака **
, которая соответствует любому количеству каталогов (включая
нуль).Например, ** / *. Go
исключит все файлы, заканчивающиеся на .go
.
которые находятся во всех каталогах, включая корень контекста сборки.
Строки начинающиеся с !
(восклицательный знак) можно использовать для исключения
к исключениям. Ниже приведен пример файла .dockerignore
, который
использует этот механизм:
* .md
! README.md
Все файлы уценки , кроме README.md
, исключаются из контекста.
Размещение !
правил исключения влияет на поведение: последний
строка .dockerignore
, которая соответствует конкретному файлу, определяет
включен он или исключен. Рассмотрим следующий пример:
* .md
! README * .md
README-secret.md
В контекст не включены файлы уценки, кроме файлов README, кроме
README-secret.md
.
Теперь рассмотрим этот пример:
*.мкр
README-secret.md
! README * .md
Включены все файлы README. Средняя линия не действует, потому что
! README * .md
соответствует README-secret.md
и идет последним.
Вы даже можете использовать файл .dockerignore
, чтобы исключить файл Dockerfile
и файлов .dockerignore
. Эти файлы по-прежнему отправляются демону
потому что они нужны ему для работы. Но инструкции ADD
и COPY
не копируйте их на изображение.
Наконец, вы можете указать, какие файлы включать в
контекст, а не исключаемый. Для этого укажите *
как
первый образец, за которым следует один или несколько !
шаблонов исключений.
Примечание
По историческим причинам паттерн
.
игнорируется.
ИЗ
ОТ [--platform = ] [AS ]
или
ОТ [--platform = ] [: ] [AS ]
или
ОТ [--platform = ] [@ ] [AS ]
Инструкция FROM
инициализирует новую стадию сборки и устанавливает
Базовый образ для последующих инструкций.Таким образом,
действительный Dockerfile
должен начинаться с инструкции FROM
. Изображение может быть
любое действительное изображение — особенно легко начать с , вытащив образ из
Публичные репозитории .
-
ARG
— единственная инструкция, которая может предшествоватьFROM
вDockerfile
.
См. Раздел «Понять, как взаимодействуют ARG и FROM». -
ИЗ
может появляться несколько раз в одном файлеDockerfile от
до
создавать несколько образов или использовать один этап сборки как зависимость для другого.Просто запишите последний идентификатор изображения, выводимый коммитом перед каждым новым
ИЗ
инструкция. Каждая инструкцияИЗ
очищает любое состояние, созданное предыдущим
инструкции. - При желании можно дать имя новому этапу сборки, добавив
Имя AS
к
ИЗ
инструкция. Имя может использоваться в последующихОТ
и
COPY --from =
инструкций для обращения к образу, созданному на этом этапе. - Тег
дайджест Значения
необязательны.Если вы опустите любой из них,
Builder по умолчанию принимаетпоследних тегов
. Строитель возвращает ошибку, если
не может найти значение тега.
Необязательный флаг --platform
можно использовать для указания платформы образа.
в случае, если ОТ
ссылается на многоплатформенный образ. Например, linux / amd64
,
linux / arm64
или windows / amd64
. По умолчанию целевая платформа сборки
запрос используется. В значении этого флага можно использовать глобальные аргументы сборки,
например автоматические платформенные ARG
позволяют принудительно перейти на платформу собственной сборки ( --platform = $ BUILDPLATFORM
),
и использовать его для кросс-компиляции на целевую платформу внутри сцены.
Понять, как взаимодействуют ARG и FROM
FROM
инструкции поддерживают переменные, которые объявлены любыми ARG
инструкции, которые появляются до первых ИЗ
.
ARG CODE_VERSION = последний
ИЗ базы: $ {CODE_VERSION}
CMD / код / запуск приложения
ИЗ дополнительных услуг: $ {CODE_VERSION}
CMD / код / дополнительные функции
ARG
, объявленный перед FROM
, находится вне стадии сборки, поэтому он
не может использоваться ни в одной инструкции после ОТ
.Чтобы использовать значение по умолчанию
ARG
, объявленный перед первым FROM
, использует инструкцию ARG
без
значение внутри стадии сборки:
ARG VERSION = последняя
ОТ busybox: $ VERSION
ВЕРСИЯ ARG
ВЫПОЛНИТЬ echo $ VERSION> image_version
ЗАПУСК
RUN имеет 2 формы:
-
RUN <команда>
(форма оболочки , команда запускается в оболочке, которая
по умолчанию/ bin / sh -c
в Linux илиcmd / S / C
в Windows) -
RUN ["исполняемый файл", "param1", "param2"]
( exec form)
Команда RUN
будет выполнять любые команды на новом уровне поверх
текущее изображение и зафиксируйте результаты.Полученное зафиксированное изображение будет
используется для следующего шага в файле Dockerfile
.
Уровни RUN
инструкций и генерация коммитов соответствуют ядру
концепции Docker, где коммиты дешевы, а контейнеры могут быть созданы из
любой момент в истории изображения, как в системе управления версиями.
Форма exec позволяет избежать перестановки строк оболочки и RUN
команды с использованием базового образа, не содержащего указанного исполняемого файла оболочки.
Оболочка по умолчанию для формы оболочки может быть изменена с помощью ОБОЛОЧКА
команда.
В форме оболочки вы можете использовать \
(обратная косая черта), чтобы продолжить одиночный
Инструкцию RUN на следующую строку. Например, рассмотрим эти две строки:
RUN / bin / bash -c 'источник $ HOME / .bashrc; \
эхо $ HOME '
Вместе они эквивалентны одной строке:
RUN / bin / bash -c 'source $ HOME /.bashrc; эхо $ HOME '
Чтобы использовать другую оболочку, кроме ‘/ bin / sh’, используйте форму exec , передаваемую в
желаемый снаряд. Например:
RUN ["/ bin / bash", "-c", "echo hello"]
Примечание
Форма exec анализируется как массив JSON, что означает, что
вы должны заключать слова в двойные кавычки («), а не в одинарные кавычки («).
В отличие от формы оболочки , форма exec не вызывает командную оболочку.Это означает, что нормальной обработки оболочки не происходит. Например,
RUN ["echo", "$ HOME"]
не будет выполнять подстановку переменных в $ HOME
.
Если вам нужна обработка оболочки, используйте форму оболочки или выполните
непосредственно оболочку, например: RUN ["sh", "-c", "echo $ HOME"]
.
При использовании формы exec и непосредственном выполнении оболочки, как в случае с
форма оболочки, это оболочка, которая выполняет переменную среды
расширение, а не докер.
Примечание
В форме JSON необходимо избегать обратной косой черты.Это
особенно актуально в Windows, где обратная косая черта является разделителем пути.
В противном случае следующая строка будет рассматриваться как форма оболочки из-за того, что
действительный JSON, и неожиданный сбой:RUN ["c: \ windows \ system32 \ tasklist.exe"]
Правильный синтаксис для этого примера:
RUN ["c: \\ windows \\ system32 \\ tasklist.exe"]
Кэш для команд RUN
не становится недействительным автоматически во время
следующая сборка.Кеш для инструкции типа
RUN apt-get dist-upgrade -y
будет повторно использован во время следующей сборки. В
кэш для RUN
инструкций можно сделать недействительным с помощью --no-cache
флаг, например docker build --no-cache
.
См. Dockerfile
Best Practices
руководство для получения дополнительной информации.
Кэш для инструкций RUN
может быть аннулирован инструкциями ADD
и COPY
.
Известные проблемы (RUN)
Проблема 783 связана с файлом.
проблемы с разрешениями, которые могут возникнуть при использовании файловой системы AUFS. Вы
может заметить это при попытке, например,rm
файла.Для систем с последней версией aufs (например,
dirperm1
опция крепления может
быть установленным), докер попытается исправить проблему автоматически, установив
слои с опциейdirperm1
. Более подробно по вариантуdirperm1
можно
найдено по адресуaufs
, справочная страницаЕсли ваша система не поддерживает
dirperm1
, проблема описывает обходной путь.
CMD
Инструкция CMD
имеет три формы:
-
CMD ["исполняемый", "param1", "param2"]
( exec form, это предпочтительная форма) -
CMD ["param1", "param2"]
(как параметров по умолчанию для ENTRYPOINT ) -
CMD command param1 param2
(форма оболочки )
В файле Dockerfile
может быть только одна инструкция CMD
.Если вы укажете более одного CMD
тогда только последний CMD
вступит в силу.
Основная цель CMD
- предоставить значения по умолчанию для выполняющейся
контейнер. Эти значения по умолчанию могут включать исполняемый файл, или они могут не указывать
исполняемый файл, и в этом случае вы должны указать ENTRYPOINT
инструкция тоже.
Если CMD
используется для предоставления аргументов по умолчанию для инструкции ENTRYPOINT
,
обе инструкции CMD
и ENTRYPOINT
должны быть указаны с JSON
формат массива.
Примечание
Форма exec анализируется как массив JSON, что означает, что вы должны использовать
двойные кавычки («) вокруг слов, а не одинарные кавычки (‘).
В отличие от формы оболочки , форма exec не вызывает командную оболочку.
Это означает, что нормальной обработки оболочки не происходит. Например,
CMD ["echo", "$ HOME"]
не будет выполнять подстановку переменных в $ HOME
.
Если вам нужна обработка оболочки, используйте форму оболочки или выполните
оболочка напрямую, например: CMD ["sh", "-c", "echo $ HOME"]
.При использовании формы exec и непосредственном выполнении оболочки, как в случае с
форма оболочки, это оболочка, которая выполняет переменную среды
расширение, а не докер.
При использовании в форматах оболочки или exec инструкция CMD
устанавливает команду
для выполнения при запуске образа.
Если вы используете форму оболочки из CMD
, то <команда>
будет выполняться в
/ bin / sh -c
:
ОТ ubuntu
CMD echo "Это тест."| туалет -
Если вы хотите, чтобы запускал ваш
без оболочки , вы должны
выразите команду в виде массива JSON и укажите полный путь к исполняемому файлу.
Эта форма массива является предпочтительным форматом CMD
. Любые дополнительные параметры
должны быть индивидуально выражены в виде строк в массиве:
ОТ ubuntu
CMD ["/ usr / bin / wc", "- help"]
Если вы хотите, чтобы ваш контейнер запускал каждый раз один и тот же исполняемый файл, тогда
вам следует рассмотреть возможность использования ENTRYPOINT
в сочетании с CMD
.Увидеть
ВХОД .
Если пользователь указывает аргументы для docker run
, то они переопределят
по умолчанию указано в CMD
.
Примечание
Не путайте
RUN
сCMD
.RUN
фактически запускает команду и фиксирует
результат;CMD
ничего не выполняет во время сборки, но указывает
предполагаемая команда для изображения.
ТАБЛИЧКА
LABEL <ключ> = <значение> <ключ> = <значение> <ключ> = <значение>...
Инструкция LABEL
добавляет метаданные к изображению. LABEL
- это
пара ключ-значение. Чтобы включить пробелы в значение LABEL
, используйте кавычки и
обратная косая черта, как при синтаксическом анализе командной строки. Несколько примеров использования:
LABEL "com.example.vendor" = "ACME Incorporated"
LABEL com.example.label-with-value = "foo"
LABEL version = "1.0"
LABEL description = "Этот текст иллюстрирует \
что значения меток могут занимать несколько строк ".
Изображение может иметь более одной метки.Вы можете указать несколько меток на
одна линия. До Docker 1.10 это уменьшало размер окончательного образа,
но это уже не так. Вы по-прежнему можете указать несколько ярлыков
в одной инструкции одним из следующих двух способов:
LABEL multi.label1 = "value1" multi.label2 = "value2" other = "value3"
LABEL multi.label1 = "значение1" \
multi.label2 = "значение2" \
другое = "значение3"
Метки, включенные в базовые или родительские изображения (изображения в строке FROM
), являются
унаследовано вашим изображением.Если метка уже существует, но с другим значением,
последнее примененное значение имеет приоритет над любым ранее установленным значением.
Для просмотра меток изображения используйте команду docker image inspect
. Ты можешь использовать
опция --format
для отображения только меток;
образ докера проверить --format = '' myimage
{
"com.example.vendor": "ACME Incorporated",
"com.example.label-with-value": "foo",
"версия": "1.0",
"description": "Этот текст показывает, что значения меток могут занимать несколько строк.",
"multi.label1": "значение1",
"multi.label2": "значение2",
"другое": "значение3"
}
ТЕХНИЧЕСКОЕ ОБСЛУЖИВАНИЕ (устарело)
Инструкция MAINTAINER
устанавливает поле Author сгенерированных изображений.
Инструкция LABEL
- гораздо более гибкая версия этого, и вы должны использовать
вместо этого, поскольку он позволяет устанавливать любые требуемые метаданные, и их можно просмотреть
легко, например с докером осмотрите
. Чтобы установить метку, соответствующую
MAINTAINER
поле, которое вы можете использовать:
LABEL keeper = "SvenDowideit @ home.org.au "
Это будет видно из docker inspect
с другими метками.
ЭКСПОЗИЦИЯ
EXPOSE <порт> [<порт> / <протокол> ...]
Инструкция EXPOSE
сообщает Docker, что контейнер прослушивает
указанные сетевые порты во время выполнения. Вы можете указать, прослушивает ли порт
TCP или UDP, по умолчанию - TCP, если протокол не указан.
Инструкция EXPOSE
фактически не публикует порт.Он функционирует как
тип документации между человеком, который создает изображение, и человеком, который
запускает контейнер, о том, какие порты предназначены для публикации. На самом деле
опубликуйте порт при запуске контейнера, используйте флаг -p
на docker run
для публикации и сопоставления одного или нескольких портов или флага -P
для публикации всех открытых
порты и сопоставьте их с портами высокого порядка.
По умолчанию EXPOSE
предполагает TCP. Вы также можете указать UDP:
Чтобы открыть как TCP, так и UDP, включите две строки:
EXPOSE 80 / tcp
EXPOSE 80 / udp
В этом случае, если вы используете -P
с docker run
, порт будет открыт один раз
для TCP и один раз для UDP.Помните, что -P
использует эфемерный хост высокого порядка
порт на хосте, поэтому порт не будет одинаковым для TCP и UDP.
Независимо от настроек EXPOSE
, вы можете переопределить их во время выполнения, используя
флаг -p
. Например
docker run -p 80: 80 / tcp -p 80: 80 / udp ...
Чтобы настроить перенаправление портов в хост-системе, см. Использование флага -P.
Команда docker network
поддерживает создание сетей для связи между
контейнеров без необходимости предоставлять или публиковать определенные порты, потому что
подключенные к сети контейнеры могут связываться друг с другом через любые
порт.Для получения подробной информации см.
обзор этой функции.
ENV
Инструкция ENV
устанавливает для переменной среды <ключ>
значение
<значение>
. Это значение будет в окружении для всех последующих инструкций
на этапе сборки и может быть заменен встроенным в
многие тоже. Значение будет интерпретировано для других переменных среды, поэтому
символы кавычек будут удалены, если они не экранированы. Подобно синтаксическому анализу командной строки,
кавычки и обратные косые черты могут использоваться для включения пробелов в значения.
Пример:
ENV MY_NAME = "Джон Доу"
ENV MY_DOG = Рекс \ Собака
ENV MY_CAT = пушистый
Команда ENV
позволяет установить несколько переменных <ключ> = <значение> ...
за один раз, и приведенный ниже пример даст такие же чистые результаты в финальном
изображение:
ENV MY_NAME = "Джон Доу" MY_DOG = Рекс \ Собака \
MY_CAT = пушистый
Переменные среды, установленные с помощью ENV
, сохранятся при запуске контейнера.
из полученного изображения.Вы можете просмотреть значения с помощью docker inspect
и
измените их, используя docker run --env
.
Сохранение переменной среды может вызвать непредвиденные побочные эффекты. Например,
установка ENV DEBIAN_FRONTEND = noninteractive
изменяет поведение apt-get
,
и может запутать пользователей вашего изображения.
Если переменная среды нужна только во время сборки, а не в финале
изображение, рассмотрите возможность установки значения для одной команды:
RUN DEBIAN_FRONTEND = неинтерактивный apt-get update && apt-get install -y...
Или используя ARG
, который не сохраняется в окончательном образе:
ARG DEBIAN_FRONTEND = не интерактивный
ЗАПУСТИТЬ apt-get update && apt-get install -y ...
Альтернативный синтаксис
Инструкция
ENV
также допускает альтернативный синтаксисENV <ключ> <значение>
,
исключая=
. Например:Этот синтаксис не позволяет устанавливать несколько переменных среды в
одиночная инструкцияENV
и может сбивать с толку.Например, следующие
устанавливает единственную переменную среды (ONE
) со значением"TWO = THREE = world"
:Альтернативный синтаксис поддерживается для обратной совместимости, но не рекомендуется
по причинам, указанным выше, и может быть удален в будущем выпуске.
ДОБАВИТЬ
ADD имеет две формы:
ДОБАВИТЬ [--chown = <пользователь>: <группа>] ... <самый лучший>
ДОБАВИТЬ [--chown = <пользователь>: <группа>] ["",... "<самый лучший>"]
Последняя форма требуется для путей, содержащих пробелы.
Примечание
Функция
--chown
поддерживается только в файлах Dockerfiles, используемых для сборки контейнеров Linux,
и не будет работать с контейнерами Windows. Поскольку концепции владения пользователями и группами
не переводить между Linux и Windows, использование/ etc / passwd
и/ etc / group
для
преобразование имен пользователей и групп в идентификаторы ограничивает возможность использования этой функции
для контейнеров на базе ОС Linux.
Инструкция ADD
копирует новые файлы, каталоги или URL-адреса удаленных файлов из
и добавляет их в файловую систему образа по пути
.
Можно указать несколько ресурсов
, но если они являются файлами или
каталоги, их пути интерпретируются относительно источника
контекст сборки.
Каждый
может содержать подстановочные знаки, и сопоставление будет выполняться с использованием Go
Путь файла.Правила матча. Например:
Чтобы добавить все файлы, начинающиеся с «hom»:
В примере ниже ?
заменяется любым одиночным символом, например, «home.txt».
- это абсолютный путь или путь относительно WORKDIR
, в который
источник будет скопирован в целевой контейнер.
В приведенном ниже примере используется относительный путь и добавляется «test.txt» к
:
.
ADD test.txt relativeDir /
В этом примере используется абсолютный путь и добавляется «test.txt» к / absoluteDir /
ДОБАВИТЬ test.txt / absoluteDir /
При добавлении файлов или каталогов, содержащих специальные символы (например, [
и ]
), вам нужно избегать этих путей, следуя правилам Голанга, чтобы предотвратить
их не рассматривать как соответствующий шаблон. Например, чтобы добавить файл
с именем arr [0] .txt
, используйте следующее;
Все новые файлы и каталоги создаются с UID и GID, равными 0, если только
необязательный флаг --chown
указывает имя пользователя, имя группы или UID / GID
комбинация, чтобы запросить конкретное право собственности на добавленный контент.В
формат флага --chown
позволяет использовать строки имени пользователя и группы
или прямые целочисленные UID и GID в любой комбинации. Предоставление имени пользователя без
groupname или UID без GID будут использовать тот же числовой UID, что и GID. Если
указано имя пользователя или группы, корневая файловая система контейнера
Для выполнения перевода будут использоваться файлы / etc / passwd
и / etc / group
.
от имени до целого UID или GID соответственно. Следующие примеры показывают
допустимые определения для флага --chown
:
ADD --chown = 55: файлы mygroup * / somedir /
ДОБАВИТЬ --chown = bin файлы * / somedir /
ДОБАВИТЬ --chown = 1 файл * / somedir /
ДОБАВИТЬ --chown = 10: 11 файлов * / somedir /
Если корневая файловая система контейнера не содержит / etc / passwd
или
/ etc / group
файлов и имена пользователей или групп используются в --chown
флаг, сборка завершится ошибкой при операции ADD
.Использование числовых идентификаторов требует
без поиска и не будет зависеть от содержимого корневой файловой системы контейнера.
В случае, когда
- это URL-адрес удаленного файла, адресат будет
имеют разрешения 600. Если удаленный файл, который извлекается, имеет HTTP
Last-Modified
header, будет использоваться метка времени из этого заголовка
для установки mtime
в конечном файле. Однако, как и любой другой файл
обработано во время ADD
, mtime
не будет включено в определение
от того, был ли изменен файл и должен ли обновляться кеш.
Примечание
Если вы собираете, передавая файл Dockerfile
через STDIN (
docker
build -), контекста сборки нет, поэтому файл Dockerfile
может содержать только инструкциюADD
на основе URL. Вы также можете пройти
сжатый архив через STDIN: (сборка докера-
),
файлDockerfile
в корне архива и остальная часть
Архив будет использоваться в качестве контекста сборки.
Если ваши файлы URL защищены с помощью аутентификации, вам необходимо использовать RUN wget
,
RUN curl
или используйте другой инструмент из контейнера в качестве инструкции ADD
не поддерживает аутентификацию.
Примечание
Первая обнаруженная инструкция
ADD
сделает кеш недействительным для всех
следуя инструкциям из Dockerfile, если содержимоеимеет
изменилось.Это включает аннулирование кеша дляRUN
инструкций.
См.Dockerfile
Best Practices.
руководство - Использование кеша сборки
для дополнительной информации.
ADD
подчиняется следующим правилам:
Путь
вы не можетеADD ../something / something
, потому что первый шаг
docker build
- отправить контекстный каталог (и подкаталоги) в
демон докера.Если
файл загружается с URL-адреса и копируется вЕсли
имя файла выводится из URL-адреса, и файл загружается в
<самый старый> / <имя файла>
. Например,ADD http: // example.com / foobar /
будет
создайте файл/ foobar
. URL-адрес должен иметь нетривиальный путь, чтобы
в этом случае можно найти соответствующее имя файла (http://example.com
не будет работать).Если
включая метаданные файловой системы.
Примечание
Сам каталог не копируется, только его содержимое.
Если
(identity, gzip, bzip2 или xz), затем он распаковывается как каталог. Ресурсы
из удаленного URL-адреса не распакованы . Когда каталог копируется или
распакованный, он имеет то же поведение, что иtar -x
, результатом является объединение:- Все, что было на пути назначения и
- Содержимое исходного дерева, конфликты разрешены в пользу
из «2.”По каждому файлу.
Примечание
Идентифицирован ли файл как распознанный формат сжатия или нет
выполняется исключительно на основе содержимого файла, а не имени файла.
Например, если пустой файл заканчивается на.tar.gz
, это не будет
будет распознан как сжатый файл, и не будет генерировать какие-либо
сообщение об ошибке декомпрессии, скорее файл будет просто скопирован в
место назначения.Если
его метаданные. В этом случае, если/
, это
будет считаться каталогом и будет записано содержимое
по адресу/ base ( ) Если указано несколько ресурсов
использование подстановочного знака, тогда
косая черта/
.Если
обычный файл, а содержимоеЕсли
на своем пути.
КОПИЯ
КОПИЯ имеет две формы:
КОПИРОВАТЬ [--chown = <пользователь>: <группа>] ... <самый>
КОПИРОВАТЬ [--chown = <пользователь>: <группа>] ["", ... "<самый лучший>"]
Последняя форма требуется для путей, содержащих пробелы
Примечание
Функция
--chown
поддерживается только в файлах Dockerfiles, используемых для сборки контейнеров Linux,
и не будет работать с контейнерами Windows. Поскольку концепции владения пользователями и группами
не переводить между Linux и Windows, использование/ etc / passwd
и/ etc / group
для
преобразование имен пользователей и групп в идентификаторы ограничивает возможность использования этой функции только для
Контейнеры на базе ОС Linux.
Команда COPY
копирует новые файлы или каталоги из
и добавляет их в файловую систему контейнера по пути
.
Можно указать несколько ресурсов
, но пути к файлам и
каталоги будут интерпретироваться как относящиеся к источнику контекста
сборки.
Каждый
может содержать подстановочные знаки, и сопоставление будет выполняться с использованием Go
Путь файла.Правила матча. Например:
Чтобы добавить все файлы, начинающиеся с «hom»:
В примере ниже ?
заменяется любым одиночным символом, например, «home.txt».
- это абсолютный путь или путь относительно WORKDIR
, в который
источник будет скопирован в целевой контейнер.
В приведенном ниже примере используется относительный путь и добавляется «test.txt» к
:
.
COPY test.txt relativeDir /
В этом примере используется абсолютный путь и добавляется «test.txt» к / absoluteDir /
КОПИРОВАТЬ test.txt / absoluteDir /
При копировании файлов или каталогов, содержащих специальные символы (например, [
и ]
), вам нужно избегать этих путей, следуя правилам Голанга, чтобы предотвратить
их не рассматривать как соответствующий шаблон. Например, чтобы скопировать файл
с именем arr [0] .txt
, используйте следующее;
КОПИЯ обр. [[] 0].txt / mydir /
Все новые файлы и каталоги создаются с UID и GID, равными 0, если только
необязательный флаг --chown
указывает имя пользователя, имя группы или UID / GID
комбинация, чтобы запросить конкретное право собственности на скопированный контент. В
формат флага --chown
позволяет использовать строки имени пользователя и группы
или прямые целочисленные UID и GID в любой комбинации. Предоставление имени пользователя без
groupname или UID без GID будут использовать тот же числовой UID, что и GID.Если
указано имя пользователя или группы, корневая файловая система контейнера
Для выполнения перевода будут использоваться файлы / etc / passwd
и / etc / group
.
от имени до целого UID или GID соответственно. Следующие примеры показывают
допустимые определения для флага --chown
:
КОПИЯ --chown = 55: файлы mygroup * / somedir /
КОПИРОВАТЬ --chown = bin файлы * / somedir /
КОПИРОВАТЬ --chown = 1 файл * / somedir /
КОПИРОВАТЬ --chown = 10: 11 файлов * / somedir /
Если корневая файловая система контейнера не содержит / etc / passwd
или
/ etc / group
файлов и имена пользователей или групп используются в --chown
флаг, сборка завершится ошибкой при операции COPY
.Использование числовых идентификаторов требует
нет поиска и не зависит от содержимого корневой файловой системы контейнера.
Примечание
Если вы собираете с использованием STDIN (сборка докеров
-
), нет
контекст сборки, поэтомуCOPY
использовать нельзя.
Необязательно КОПИЯ
принимает флаг --from =
, который можно использовать для установки
исходное местоположение на предыдущем этапе сборки (созданное с помощью FROM .. AS
)
который будет использоваться вместо контекста сборки, отправленного пользователем.В случае сборки
этап с указанным именем не может быть найден изображение с таким же именем
попытался использовать вместо этого.
COPY
подчиняется следующим правилам:
Путь
вы не можетеCOPY ../something / something
, потому что первый шаг
docker build
- отправить контекстный каталог (и подкаталоги) в
демон докера.Если
включая метаданные файловой системы.
Примечание
Сам каталог не копируется, только его содержимое.
Если
его метаданные. В этом случае, если/
, это
будет считаться каталогом и будет записано содержимое
по адресу/ base ( ) Если указано несколько ресурсов
использование подстановочного знака, тогда
косая черта/
.Если
обычный файл, а содержимоеЕсли
на своем пути.
Примечание
Первая обнаруженная инструкция
COPY
сделает кеш недействительным для всех
следуя инструкциям из Dockerfile, если содержимоеимеет
изменилось. Это включает аннулирование кеша дляRUN
инструкций.
См.Dockerfile
Best Practices.
руководство - Использование кеша сборки
для дополнительной информации.
ВХОД
ENTRYPOINT имеет две формы:
Форма exec , которая является предпочтительной формой:
ENTRYPOINT ["исполняемый файл", "параметр1", "параметр2"]
Оболочка форма:
ENTRYPOINT команда param1 param2
ENTRYPOINT
позволяет вам настроить контейнер, который будет работать как исполняемый файл.
Например, следующий запускает nginx с содержимым по умолчанию, прослушивая
на порту 80:
$ docker run -i -t --rm -p 80:80 nginx
Аргументы командной строки для docker run
будут добавлены в конце концов
элементы в exec формируют ENTRYPOINT
, и переопределят все указанные элементы
используя CMD
.
Это позволяет передавать аргументы в точку входа, то есть docker run
передаст аргумент -d
точке входа.Вы можете переопределить инструкцию ENTRYPOINT
, используя команду docker run --entrypoint
флаг.
Форма оболочки предотвращает использование аргументов командной строки CMD
или run
.
используется, но имеет тот недостаток, что ваш ENTRYPOINT
будет запускаться как
подкоманда / bin / sh -c
, которая не передает сигналы.
Это означает, что исполняемый файл не будет PID 1
контейнера - и
будет не получать сигналы Unix - поэтому ваш исполняемый файл не получит
SIGTERM
из docker stop
.
Только последняя инструкция ENTRYPOINT
в Dockerfile
будет иметь эффект.
Exec form Пример ENTRYPOINT
Вы можете использовать форму exec из ENTRYPOINT
для установки довольно стабильных команд по умолчанию.
и аргументы, а затем используйте любую форму CMD
для установки дополнительных значений по умолчанию, которые
с большей вероятностью будут изменены.
ОТ ubuntu
ENTRYPOINT ["верх", "-b"]
CMD ["-c"]
Когда вы запустите контейнер, вы увидите, что top
- единственный процесс:
$ docker run -it --rm --name test top -H
наверх - 08:25:00 до 7:27, пользователей 0, средняя загрузка: 0.00, 0,01, 0,05
Темы: всего 1, 1 запущен, 0 спит, 0 остановлен, 0 зомби
% ЦП: 0,1 мкс, 0,1 синг, 0,0 нi, 99,7 ид, 0,0 ват, 0,0 hi, 0,0 си, 0,0 ст
KiB Mem: всего 2056668, использовано 1616832, свободно 439836, буферов 99352
KiB Swap: всего 1441840, 0 используется, 1441840 бесплатно. 1324440 кэшированных Mem
PID ПОЛЬЗОВАТЕЛЬ PR NI VIRT RES SHR S% CPU% MEM TIME + COMMAND
1 корень 20 0 19744 2336 2080 R 0,0 0,1 0: 00,04 верх
Для дальнейшего изучения результата вы можете использовать docker exec
:
$ docker exec -it test ps aux
USER PID% CPU% MEM VSZ RSS TTY STAT ВРЕМЯ НАЧАЛА КОМАНДА
корень 1 2.6 0,1 19752 2352? Сс + 08:24 0:00 вверх -b -H
корень 7 0,0 0,1 15572 2164? R + 08:25 0:00 пс доп.
И вы можете изящно запросить завершение работы top
с помощью docker stop test
.
В следующем файле Dockerfile
показано использование ENTRYPOINT
для запуска Apache в
передний план (т.е. как PID 1
):
ОТ debian: стабильный
ЗАПУСТИТЬ apt-get update && apt-get install -y --force-yes apache2
ВЫБРАТЬ 80 443
VOLUME ["/ var / www", "/ var / log / apache2", "/ etc / apache2"]
ENTRYPOINT ["/ usr / sbin / apache2ctl", "-D", "FOREGROUND"]
Если вам нужно написать стартовый сценарий для одного исполняемого файла, вы можете убедиться, что
последний исполняемый файл получает сигналы Unix, используя exec
и gosu
команды:
#! / Usr / bin / env bash
set -e
если ["$ 1" = 'postgres']; тогда
chown -R postgres "$ PGDATA"
если [-z "$ (ls -A" $ PGDATA ")"]; тогда
gosu postgres initdb
фи
exec gosu postgres "$ @"
фи
exec "$ @"
Наконец, если вам нужно выполнить дополнительную очистку (или связаться с другими контейнерами)
при завершении работы или координируете более одного исполняемого файла, вам может потребоваться
что сценарий ENTRYPOINT
получает сигналы Unix, передает их, а затем
работает еще:
#! / Bin / sh
# Примечание: я написал это с помощью sh, поэтому он работает и в контейнере busybox
# ИСПОЛЬЗУЙТЕ ловушку, если вам нужно также выполнить ручную очистку после остановки службы,
# или нужно запустить несколько сервисов в одном контейнере
trap "эхо TRAPed signal" HUP INT QUIT TERM
# запустить службу в фоновом режиме здесь
/ usr / sbin / apachectl начало
echo "[нажмите клавишу ввода для выхода] или запустите 'docker stop '"
читать
# остановить обслуживание и очистить здесь
эхо "остановка Apache"
/ usr / sbin / apachectl стоп
echo "вышел из $ 0"
Если вы запустите этот образ с docker run -it --rm -p 80:80 --name test apache
,
затем вы можете проверить процессы контейнера с помощью docker exec
или docker top
,
а затем попросите скрипт остановить Apache:
$ docker exec -it test ps aux
USER PID% CPU% MEM VSZ RSS TTY STAT ВРЕМЯ НАЧАЛА КОМАНДА
корень 1 0.1 0,0 4448 692? Сс + 00:42 0:00 / bin / sh /run.sh 123 cmd cmd2
корень 19 0,0 0,2 71304 4440? Сс 00:42 0:00 / usr / sbin / apache2 -k start
www-data 20 0,2 0,2 360468 6004? Сл 00:42 0:00 / usr / sbin / apache2 -k start
www-data 21 0,2 0,2 360468 6000? Сл 00:42 0:00 / usr / sbin / apache2 -k start
корень 81 0,0 0,1 15572 2140? R + 00:44 0:00 пс доп.
$ docker top test
КОМАНДА ПОЛЬЗОВАТЕЛЯ PID
10035 root {run.ш} / bin / sh /run.sh 123 cmd cmd2
10054 корень / usr / sbin / apache2 -k start
10055 33 / usr / sbin / apache2 -k начало
10056 33 / usr / sbin / apache2 -k начало
$ / usr / bin / time docker stop test
контрольная работа
реальный 0 м 0,27 с
пользователь 0 м 0,03 с
sys 0m 0,03 с
Примечание
Вы можете отменить настройку
ENTRYPOINT
, используя--entrypoint
,
но это может установить только двоичный файл exec (sh -c
использоваться не будет).
Примечание
Форма exec анализируется как массив JSON, что означает, что
вы должны заключать слова в двойные кавычки («), а не в одинарные кавычки («).
В отличие от формы оболочки , форма exec не вызывает командную оболочку.
Это означает, что нормальной обработки оболочки не происходит. Например,
ENTRYPOINT ["echo", "$ HOME"]
не будет выполнять подстановку переменных в $ HOME
.Если вам нужна обработка оболочки, используйте форму оболочки или выполните
оболочку напрямую, например: ENTRYPOINT ["sh", "-c", "echo $ HOME"]
.
При использовании формы exec и непосредственном выполнении оболочки, как в случае с
форма оболочки, это оболочка, которая выполняет переменную среды
расширение, а не докер.
Форма оболочки Пример ENTRYPOINT
Вы можете указать простую строку для ENTRYPOINT
, и она будет выполняться в / bin / sh -c
.Эта форма будет использовать обработку оболочки для замены переменных среды оболочки,
и будет игнорировать любые аргументы командной строки CMD
или , запускаемые докером
.
Чтобы гарантировать, что docker stop
будет сигнализировать о любом длительно работающем исполняемом файле ENTRYPOINT
правильно, вам нужно не забыть запустить его с exec
:
ОТ ubuntu
ENTRYPOINT exec top -b
Когда вы запустите этот образ, вы увидите единственный процесс PID 1
:
$ docker run -it --rm --name test top
Mem: 1704520K используется, 352148K бесплатно, 0K shrd, 0K бафф, 140368121167873K кэшировано
ЦП: 5% usr 0% sys 0% nic 94% idle 0% io 0% irq 0% sirq
Средняя нагрузка: 0.08 0,03 0,05 2/98 6
PID PPID USER STAT VSZ% VSZ% CPU COMMAND
1 0 корень R 3164 0% 0% top -b
Который аккуратно выходит на докер-остановку
:
$ / usr / bin / time docker stop test
контрольная работа
реальный 0 м 0,20 с
пользователь 0 м 0,02 с
sys 0m 0,04 с
Если вы забыли добавить exec
в начало вашего ENTRYPOINT
:
ОТ ubuntu
ENTRYPOINT top -b
CMD --ignored-param1
Затем вы можете запустить его (присвоив ему имя для следующего шага):
$ docker run -it --name test top --ignored-param2
Mem: 1704184K используется, 352484K бесплатно, 0K SHRD, 0K бафф, 140621524238337K кэшировано
ЦП: 9% usr 2% sys 0% nic 88% idle 0% io 0% irq 0% sirq
Средняя нагрузка: 0.01 0,02 0,05 2/101 7
PID PPID USER STAT VSZ% VSZ% CPU COMMAND
1 0 корень S 3168 0% 0% / bin / sh -c top -b cmd cmd2
7 1 корень R 3164 0% 0% top -b
Из вывода top
видно, что указанный ENTRYPOINT
не является PID 1
.
Если вы затем запустите docker stop test
, контейнер не выйдет правильно -
stop
команда будет принудительно отправить SIGKILL
после тайм-аута:
$ docker exec -it test ps aux
КОМАНДА ПОЛЬЗОВАТЕЛЯ PID
1 корень / bin / sh -c top -b cmd cmd2
7 корень верх -b
8 корневых ps aux
$ / usr / bin / time docker stop test
контрольная работа
реальный 0м 10.19 с
пользователь 0 м 0,04 с
sys 0m 0,03 с
Понять, как взаимодействуют CMD и ENTRYPOINT
Инструкции CMD
и ENTRYPOINT
определяют, какая команда будет выполняться при запуске контейнера.
Есть несколько правил, описывающих их сотрудничество.
- В
Dockerfile должна быть указана хотя бы одна из команд
CMD
илиENTRYPOINT
. ENTRYPOINT
должен быть определен при использовании контейнера в качестве исполняемого файла.CMD
следует использовать как способ определения аргументов по умолчанию для командыENTRYPOINT
или для выполнения специальной команды в контейнере.CMD
будет переопределено при запуске контейнера с альтернативными аргументами.
В таблице ниже показано, какая команда выполняется для различных комбинаций ENTRYPOINT
/ CMD
:
№ ВХОДА | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [«exec_entry», «p1_entry»] | |
---|---|---|---|
Нет CMD | ошибка , недопустимая | / 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 |
Примечание
Если
CMD
определяется из базового образа, установкаENTRYPOINT
будет
сброситьCMD
на пустое значение.В этом сценарииCMD
должен быть определен в
текущее изображение должно иметь значение.
ОБЪЕМ
Инструкция VOLUME
создает точку монтирования с указанным именем
и отмечает, что он содержит внешние тома с собственного хоста или другого
контейнеры. Значение может быть массивом JSON, VOLUME ["/ var / log /"]
или обычным
строка с несколькими аргументами, например VOLUME / var / log
или VOLUME / var / log
. Для получения дополнительной информации / примеров и инструкций по монтажу через
/ var / db
Клиент Docker, см.
общих каталогов в томах
документация.
Команда docker run
инициализирует вновь созданный том любыми данными.
который существует в указанном месте в базовом образе. Например,
рассмотрите следующий фрагмент Dockerfile:
ОТ ubuntu
ЗАПУСК mkdir / myvol
RUN echo "hello world"> / myvol / приветствие
VOLUME / myvol
Этот файл Dockerfile приводит к созданию образа, который вызывает запуск докера
в
создайте новую точку монтирования по адресу / myvol
и скопируйте файл приветствия
во вновь созданный том.
Примечания к указанию томов
Помните о томах в Dockerfile
.
Тома в контейнерах на базе Windows : при использовании контейнеров на базе Windows
место назначения тома внутри контейнера должно быть одним из:- несуществующий или пустой каталог
- привод, отличный от
C:
Изменение громкости из Dockerfile : Если какие-либо шаги сборки изменят
данные в томе после того, как они были объявлены, эти изменения будут отменены.Форматирование JSON : список анализируется как массив JSON.
Слова следует заключать в двойные кавычки ("
), а не в одинарные кавычки ('
).Каталог хоста объявлен во время выполнения контейнера : Каталог хоста
(точка монтирования) по своей природе зависит от хоста. Это для сохранения имиджа
переносимость, поскольку не может быть гарантирована доступность данного каталога хоста
на всех хостах.По этой причине вы не можете смонтировать каталог хоста из
в Dockerfile. ИнструкцияVOLUME
не поддерживает указаниеhost-dir
параметр. Вы должны указать точку монтирования при создании или запуске контейнера.
ПОЛЬЗОВАТЕЛЬ
или
Инструкция USER
устанавливает имя пользователя (или UID) и, возможно, пользователя.
группа (или GID) для использования при запуске образа и для любых RUN
, CMD
и
ENTRYPOINT
, следующие за ним в Dockerfile
.
Обратите внимание, что при указании группы для пользователя у пользователя будет , только
указанное членство в группе. Любое другое настроенное членство в группах будет проигнорировано.
Предупреждение
Если у пользователя нет основной группы, тогда изображение (или следующее
инструкции) будет запускаться с корневой группой.
В Windows сначала необходимо создать пользователя, если это не встроенная учетная запись.
Это можно сделать с помощью командыnet user
, вызываемой как часть файла Dockerfile.
ОТ microsoft / windowsservercore
# Создать пользователя Windows в контейнере
RUN net user / добавить патрика
# Установите его для последующих команд
ПОЛЬЗОВАТЕЛЬ патрик
WORKDIR
Инструкция WORKDIR
устанавливает рабочий каталог для любых RUN
, CMD
,
ENTRYPOINT
, COPY
и ADD
инструкции, которые следуют за ним в Dockerfile
.
Если WORKDIR
не существует, он будет создан, даже если он не используется ни в одном
последующая инструкция Dockerfile
.
Инструкцию WORKDIR
можно использовать несколько раз в файле Dockerfile
. Если
указан относительный путь, он будет относиться к пути предыдущего
WORKDIR
инструкция. Например:
WORKDIR / a
WORKDIR b
WORKDIR c
RUN pwd
Результат последней команды pwd
в этом файле Dockerfile
будет / a / b / c
.
Команда WORKDIR
может разрешить переменные среды, ранее установленные с помощью
ENV
.Вы можете использовать только переменные среды, явно указанные в Dockerfile
.
Например:
ENV DIRPATH = / путь
WORKDIR $ DIRPATH / $ DIRNAME
RUN pwd
Результат последней команды pwd
в этом файле Dockerfile
будет
/ путь / $ DIRNAME
ARG
ARG <имя> [= <значение по умолчанию>]
Инструкция ARG
определяет переменную, которую пользователи могут передавать во время сборки в
построитель с помощью команды docker build
с использованием --build-arg
флаг.Если пользователь указывает аргумент сборки, который не был
определенный в Dockerfile, сборка выдает предупреждение.
[Предупреждение] Один или несколько аргументов сборки [foo] не использовались.
Dockerfile может включать одну или несколько инструкций ARG
. Например,
это действительный файл Dockerfile:
ОТ busybox
ARG user1
ARG buildno
# ...
Предупреждение:
Не рекомендуется использовать переменные времени сборки для передачи секретов, например
ключи github, учетные данные пользователя и т. д.Значения переменных времени сборки видны
любой пользователь образа с помощью командыdocker history
.См. «Сборку образов с помощью BuildKit»
раздел, чтобы узнать о безопасных способах использования секретов при создании изображений.
Значения по умолчанию
Команда ARG
может дополнительно включать значение по умолчанию:
ОТ busybox
ARG user1 = someuser
ARG buildno = 1
# ...
Если команда ARG
имеет значение по умолчанию и если значение не передано
во время сборки построитель использует значение по умолчанию.
Область применения
Определение переменной ARG
вступает в силу со строки, на которой оно
определено в Dockerfile
не из использования аргумента в командной строке или
в другом месте. Например, рассмотрим этот Dockerfile:
ОТ busybox
ПОЛЬЗОВАТЕЛЬ $ {пользователь: -some_user}
Пользователь ARG
USER $ пользователь
# ...
Пользователь создает этот файл, позвонив:
$ docker build --build-arg user = what_user.
USER
в строке 2 оценивается как some_user
, поскольку переменная user
определена в
следующая строка 3. ПОЛЬЗОВАТЕЛЬ
в строке 4 оценивает what_user
как пользователь
определено, и значение what_user
было передано в командной строке. До его определения
ARG
инструкция, любое использование переменной приводит к пустой строке.
Инструкция ARG
выходит за рамки в конце сборки
этап, на котором это было определено. Чтобы использовать аргумент на нескольких этапах, каждый этап должен
включить инструкцию ARG
.
ОТ busybox
НАСТРОЙКИ ARG
ЗАПУСТИТЬ ./ run / setup $ НАСТРОЙКИ
ОТ busybox
НАСТРОЙКИ ARG
RUN ./run/other $ SETTINGS
Использование переменных ARG
Вы можете использовать команду ARG
или ENV
, чтобы указать переменные, которые
доступный для инструкции RUN
. Переменные среды, определенные с помощью
ENV
инструкция всегда имеет приоритет над одноименной инструкцией ARG
. Рассмотреть возможность
этот Dockerfile с инструкциями ENV
и ARG
.
ОТ ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER = v1.0.0
RUN echo $ CONT_IMG_VER
Тогда предположим, что этот образ создан с помощью этой команды:
$ docker build --build-arg CONT_IMG_VER = v2.0.1.
В этом случае в инструкции RUN
используется
Лучшие практики для записи файлов Docker
Расчетное время чтения: 31 минута
В этом документе описаны передовые практики и методы построения.
эффективные изображения.
Docker создает образы автоматически, читая инструкции из
Dockerfile
- текстовый файл, содержащий по порядку все команды, необходимые для
построить заданный образ. Dockerfile
придерживается определенного формата и набора
инструкции, которые вы можете найти в справочнике Dockerfile.
Образ Docker состоит из слоев только для чтения, каждый из которых представляет
Инструкция Dockerfile. Слои уложены друг на друга, и каждый из них представляет собой дельту
изменяется от предыдущего слоя.Рассмотрим этот файл Dockerfile
:
ОТ ubuntu: 18.04
КОПИРОВАТЬ. /приложение
ЗАПУСТИТЬ make / app
CMD python /app/app.py
Каждая инструкция создает один слой:
-
FROM
создает слой из образаubuntu: 18.04
Docker. -
COPY
добавляет файлы из текущего каталога вашего клиента Docker. -
RUN
строит ваше приложение сmake
. -
CMD
указывает, какую команду выполнять в контейнере.
Когда вы запускаете изображение и генерируете контейнер, вы добавляете новый записываемый слой
(«контейнерный слой») поверх нижележащих слоев. Все изменения внесены в
работающий контейнер, такой как запись новых файлов, изменение существующих файлов и
удаляющие файлы, записываются на этот тонкий записываемый слой-контейнер.
Подробнее о слоях изображений (и о том, как Docker создает и хранит образы), см.
О драйверах накопителей.
Общие указания и рекомендации
Создание эфемерных контейнеров
Образ, определенный вашим Dockerfile
, должен генерировать контейнеры, которые
эфемерный, насколько это возможно.Под «эфемерным» мы подразумеваем, что контейнер можно остановить.
и уничтожены, а затем восстановлены и заменены абсолютным минимумом.
конфигурация.
См. Процессы в разделе Приложение с двенадцатью факторами
методологии, чтобы понять мотивацию запуска контейнеров в таком
мода без гражданства.
Понимание контекста сборки
Когда вы вводите команду docker build
, вызывается текущий рабочий каталог
контекст сборки .По умолчанию предполагается, что здесь находится Dockerfile,
но вы можете указать другое местоположение с помощью флага файла ( -f
). Несмотря на
где фактически находится Dockerfile
, все рекурсивное содержимое файлов и
каталоги в текущем каталоге отправляются демону Docker как сборка
контекст.
Пример контекста сборки
Создайте каталог для контекста сборки и
cd
в него. Напишите «привет» в
текстовый файл с именемhello
и создайте Dockerfile, который запускает на немcat
.Построить
изображение из контекста сборки (.
):mkdir myproject && cd myproject echo "привет"> привет echo -e "FROM busybox \ nCOPY / hello / \ nRUN cat / hello"> Dockerfile docker build -t helloapp: v1.
Переместите
Dockerfile
иhello
в отдельные каталоги и создайте второй
версия образа (не полагаясь на кеш последней сборки). Используйте-f
чтобы указать на Dockerfile и указать каталог контекста сборки:mkdir -p dockerfiles контекст mv Dockerfile dockerfiles && mv hello context docker build --no-cache -t helloapp: v2 -f dockerfiles / контекст Dockerfile
Случайное включение файлов, которые не нужны для создания образа
приводит к большему контексту сборки и большему размеру изображения.Это может увеличить
время для создания образа, время для его извлечения и отправки и среда выполнения контейнера
размер. Чтобы узнать, насколько велик ваш контекст сборки, найдите подобное сообщение, когда
сборка Dockerfile
:
Отправка контекста сборки демону Docker 187,8 МБ
Канал Dockerfile через stdin
Docker может создавать образы по конвейеру с Dockerfile
по stdin
с локальным или удаленным контекстом сборки .Соединение Dockerfile
через stdin
может быть полезно для выполнения разовых сборок без записи файла Docker на диск,
или в ситуациях, когда создается Dockerfile
, который не должен сохраняться
потом.
Примеры в этом разделе используют здесь документы
для удобства, но любой способ предоставитьDockerfile
наstdin
можно
используемый.Например, следующие команды эквивалентны:
echo -e 'FROM busybox \ nRUN echo "hello world"' | сборка докера -
Сборка докеров
- << EOF ОТ busybox RUN echo "привет, мир" EOF
Вы можете заменить примеры своим предпочтительным подходом или подходом
который лучше всего подходит для вашего варианта использования.
Создайте образ, используя Dockerfile из стандартного ввода, без отправки контекста сборки
Используйте этот синтаксис для создания образа с использованием файла Dockerfile
из stdin
, без
отправка дополнительных файлов в качестве контекста сборки. Дефис ( -
) занимает позицию
из PATH
и инструктирует Docker прочитать контекст сборки (который только
содержит Dockerfile
) из stdin
вместо каталога:
В следующем примере создается образ с использованием файла Dockerfile
, который передается через
стандартный ввод
.Никакие файлы не отправляются демону в качестве контекста сборки.
docker build -t myimage: latest - << EOF
ОТ busybox
RUN echo "привет, мир"
EOF
Пропуск контекста сборки может быть полезен в ситуациях, когда ваш Dockerfile
не требует копирования файлов в образ и повышает скорость сборки,
так как файлы не отправляются демону.
Если вы хотите повысить скорость сборки, исключив несколько файлов из сборки -
контекст, обратитесь к исключению с помощью.dockerignore.
Примечание : попытка создания файла Docker, который использует
COPY
илиADD
, завершится ошибкой
если используется этот синтаксис. Следующий пример иллюстрирует это:# создать каталог для работы mkdir пример CD пример # создать файл примера коснитесь somefile.txt docker build -t myimage: latest - << EOF ОТ busybox Скопируйте файл somefile.txt. ЗАПУСТИТЬ cat /somefile.txt EOF # наблюдаем, что сборка не удалась ... Шаг 2/3: КОПИРУЙТЕ файл.текст . Ошибка копирования: stat /var/lib/docker/tmp/docker-builder24
48/somefile.txt: нет такого файла или каталога
Сборка из локального контекста сборки с использованием файла Dockerfile из стандартного ввода
Используйте этот синтаксис для создания образа, используя файлы в вашей локальной файловой системе, но используя
Dockerfile
из стандартного ввода
. В синтаксисе используется опция -f
(или --file
) для
укажите Dockerfile
для использования, используя дефис ( -
) в качестве имени файла для указания
Docker для чтения Dockerfile
из stdin
:
сборка докеров [ОПЦИИ] -f- ПУТЬ
В приведенном ниже примере используется текущий каталог (.
) в качестве контекста сборки и строит
образ, использующий Dockerfile
, который передается через stdin
, используя здесь
документ.
# создать каталог для работы
mkdir пример
CD пример
# создать файл примера
коснитесь somefile.txt
# создать образ, используя текущий каталог в качестве контекста и файл Dockerfile, переданный через stdin
docker build -t myimage: последний -f-. << EOF
ОТ busybox
Скопируйте файл somefile.txt.
ЗАПУСТИТЬ cat /somefile.txt
EOF
Сборка из контекста удаленной сборки с использованием файла Dockerfile из стандартного ввода
Используйте этот синтаксис для создания образа с использованием файлов из удаленного репозитория git
,
используя Dockerfile
из stdin
.В синтаксисе используется опция -f
(или --file
) для
укажите Dockerfile
для использования, используя дефис ( -
) в качестве имени файла для указания
Docker для чтения Dockerfile
из stdin
:
сборка докеров [ОПЦИИ] -f- ПУТЬ
Этот синтаксис может быть полезен в ситуациях, когда вы хотите создать образ из
репозиторий, который не содержит Dockerfile
, или если вы хотите построить с индивидуальным
Dockerfile
, без поддержки собственного форка репозитория.
В приведенном ниже примере создается образ с использованием Dockerfile
из стандартного ввода
и добавляется
файл hello.c
из репозитория «hello-world» Git на GitHub.
docker build -t myimage: latest -f- https://github.com/docker-library/hello-world.git << EOF
ОТ busybox
КОПИРОВАТЬ hello.c.
EOF
Под капотом
При создании образа с использованием удаленного репозитория Git в качестве контекста сборки Docker
выполняетgit clone
репозитория на локальном компьютере и отправляет
эти файлы в качестве контекста сборки для демона.Для этой функции требуетсяgit
.
установлен на хосте, на котором вы запускаете командуdocker build
.
Исключить с помощью .dockerignore
Чтобы исключить файлы, не относящиеся к сборке (без реструктуризации исходного кода
репозиторий) используйте файл .dockerignore
. Этот файл поддерживает шаблоны исключения
аналогично файлам .gitignore
. Для получения информации о его создании см.
.dockerignore файл.
Использовать многоступенчатые сборки
Многоступенчатая сборка позволяет резко сократить
размер вашего окончательного изображения, не пытаясь уменьшить количество промежуточных
слои и файлы.
Поскольку образ создается на заключительном этапе процесса сборки, вы можете
минимизировать слои изображений за счет использования кеша сборки.
Например, если ваша сборка содержит несколько слоев, вы можете заказать их из
менее часто изменяется (для обеспечения возможности повторного использования кеша сборки) в более
часто менялось:
Установите инструменты, необходимые для сборки вашего приложения
Установить или обновить зависимости библиотеки
Создайте свое приложение
Dockerfile для приложения Go может выглядеть так:
ОТ голанга: 1.11-альпийская сборка AS
# Установить инструменты, необходимые для проекта
# Запустите `docker build --no-cache .`, чтобы обновить зависимости
ЗАПУСК apk добавить --no-cache git
ЗАПУСТИТЬ иди получить github.com/golang/dep/cmd/dep
# Список зависимостей проекта с помощью Gopkg.toml и Gopkg.lock
# Эти слои перестраиваются только при обновлении файлов Gopkg
КОПИРОВАТЬ Gopkg.lock Gopkg.toml / go / src / project /
WORKDIR / go / src / project /
# Установить зависимости библиотеки
ЗАПУСК ОТПРАВИТЬ Гарантия-только поставщик
# Скопируйте весь проект и соберите его
# Этот слой перестраивается при изменении файла в каталоге проекта
КОПИРОВАТЬ./ перейти / SRC / проект /
ЗАПУСТИТЬ идти build -o / bin / project
# В результате получается однослойное изображение
С нуля
КОПИРОВАТЬ --from = build / bin / project / bin / project
ENTRYPOINT ["/ bin / project"]
CMD ["--help"]
Не устанавливайте ненужные пакеты
Чтобы уменьшить сложность, зависимости, размеры файлов и время сборки, избегайте
установка дополнительных или ненужных пакетов только потому, что они могут быть
иметь." Например, вам не нужно включать текстовый редактор в изображение базы данных.
Разделение приложений
У каждого контейнера должна быть только одна проблема.Разделение приложений на
несколько контейнеров упрощают горизонтальное масштабирование и повторное использование контейнеров.
Например, стек веб-приложения может состоять из трех отдельных
контейнеры, каждый со своим уникальным изображением, для управления веб-приложением,
база данных и кэш в памяти независимо друг от друга.
Ограничение каждого контейнера одним процессом - хорошее практическое правило, но не
жесткое и быстрое правило. Например, не только контейнеры могут быть
порожден процессом инициализации,
некоторые программы могут самостоятельно порождать дополнительные процессы.За
Например, сельдерей может порождать несколько рабочих
процессов, и Apache может создать один процесс на
запрос.
Следите за тем, чтобы контейнеры были как можно более чистыми и модульными. Если
контейнеры зависят друг от друга, вы можете использовать сети контейнеров Docker
чтобы эти контейнеры могли обмениваться данными.
Минимизировать количество слоев
В старых версиях Docker важно было минимизировать количество
слои в ваших изображениях, чтобы убедиться, что они работают.Следующие особенности
были добавлены, чтобы уменьшить это ограничение:
Только инструкции
RUN
,COPY
,ADD
создают слои. Прочие инструкции
создавать временные промежуточные образы и не увеличивать размер сборки.По возможности используйте многоступенчатые сборки и копируйте только
нужные артефакты в окончательное изображение. Это позволяет вам включать инструменты
и отладочную информацию на промежуточных этапах сборки без увеличения
размер финального изображения.
Сортировка многострочных аргументов
По возможности упрощайте последующие изменения, сортируя многострочные аргументы
буквенно-цифровым образом. Это помогает избежать дублирования пакетов и сделать
список намного проще обновлять. Это также упрощает чтение PR и
обзор. Также помогает добавление пробела перед обратной косой чертой ( \
).
Вот пример из образа buildpack-deps
:
ВЫПОЛНИТЬ apt-get update && apt-get install -y \
бзр \
cvs \
мерзавец \
ртутный \
подрывная деятельность \
&& rm -rf / var / lib / apt / lists / *
Использовать кеш сборки
При создании образа Docker выполняет инструкции в вашем
Dockerfile
, выполняя каждый в указанном порядке.Поскольку каждая инструкция
проверено, Docker ищет в своем кеше существующее изображение, которое он может повторно использовать,
вместо создания нового (дублированного) изображения.
Если вы вообще не хотите использовать кеш, вы можете использовать --no-cache = true
вариант в команде docker build
. Однако, если вы позволите Docker использовать
кеш, важно понимать, когда он может и не может найти соответствующий
образ. Основные правила, которым следует Docker, изложены ниже:
Начиная с родительского образа, который уже находится в кеше, следующий
инструкция сравнивается со всеми дочерними изображениями, полученными из этой базы
изображение, чтобы увидеть, был ли один из них построен с использованием той же инструкции.Если
нет, кеш недействителен.В большинстве случаев просто сравнивая инструкцию в
Dockerfile
с одной
дочерних изображений достаточно. Однако некоторые инструкции требуют большего
осмотр и объяснение.Для инструкций
ADD
иCOPY
содержимое файла (ов)
на изображении проверяются, и для каждого файла вычисляется контрольная сумма.
Время последнего изменения и последнего доступа к файлу (-ам) не учитывается.
эти контрольные суммы.Во время поиска в кеше контрольная сумма сравнивается с
контрольная сумма в существующих образах. Если что-то изменилось в файле (ах), например
как содержимое и метаданные, то кеш становится недействительным.Помимо команд
ADD
иCOPY
, проверка кэша не смотрит на
файлы в контейнере для определения совпадения кеша. Например, при обработке
командаRUN apt-get -y update
файлы, обновленные в контейнере
не проверяются, чтобы определить, существует ли попадание в кэш.В таком случае просто
сама командная строка используется для поиска совпадения.
Как только кеш становится недействительным, все последующие команды Dockerfile
создают новые
изображения и кеш не используются.
Инструкции Dockerfile
Эти рекомендации разработаны, чтобы помочь вам создать эффективную и
поддерживаемый Dockerfile
.
ИЗ
Ссылка на файл Dockerfile для инструкции FROM
По возможности используйте текущие официальные изображения в качестве основы для ваших
изображений.Мы рекомендуем альпийский образ, поскольку он
жестко контролируется и имеет небольшой размер (в настоящее время менее 5 МБ), хотя
являясь полным дистрибутивом Linux.
ЯРЛЫК
Что такое метки объектов
Вы можете добавлять метки к своему изображению, чтобы упорядочить изображения по проектам, записям
информация о лицензировании, для помощи в автоматизации или по другим причинам. Для каждого
label добавьте строку, начинающуюся с LABEL
и одну или несколько пар ключ-значение.
В следующих примерах показаны различные допустимые форматы.Пояснительные комментарии включены в строку.
Строки с пробелами должны быть заключены в кавычки. или пробелы должны быть экранированы. Внутренний
символы кавычек ("
) также должны быть экранированы.
# Установить одну или несколько индивидуальных меток
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
, чтобы предотвратить появление дополнительных слоев
от создания. В этом больше нет необходимости, но комбинирование меток все еще возможно.
поддерживается.
# Установить несколько меток на одной строке
LABEL com.example.version = "0.0.1-beta" com.example.release-date = "2015-02-12"
Вышеупомянутое также можно записать как:
# Установить несколько меток одновременно, используя символы продолжения строки, чтобы разбить длинные строки
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"
См. Описание меток объектов
для рекомендаций о допустимых ключах и значениях ярлыков. Для информации о
запрашивая ярлыки, обратитесь к элементам, связанным с фильтрацией в
Управление метками на объектах.
См. Также LABEL в справочнике по Dockerfile.
ЗАПУСК
Ссылка на файл Dockerfile для инструкции RUN
Разделить длинные или сложные операторы RUN
на несколько строк, разделенных
обратная косая черта, чтобы сделать ваш Dockerfile
более читаемым, понятным и
ремонтопригодный.
кв-получить
Вероятно, наиболее распространенным вариантом использования RUN
является приложение apt-get
.
Поскольку она устанавливает пакеты, команда RUN apt-get
имеет несколько ошибок.
ищите.
Избегайте RUN apt-get upgrade
и dist-upgrade
, так как многие из «необходимых»
пакеты из родительских образов не могут обновляться внутри
непривилегированный контейнер. Если пакет
содержащийся в родительском образе устарел, обратитесь к его сопровождающим.если ты
знать, что есть конкретный пакет, foo
, который необходимо обновить, используйте
apt-get install -y foo
для автоматического обновления.
Всегда объединяйте RUN apt-get update
с apt-get install
в том же RUN
заявление. Например:
ВЫПОЛНИТЬ apt-get update && apt-get install -y \
пакет-бар \
пакет-баз \
пакет-foo \
&& rm -rf / var / lib / apt / lists / *
Использование только apt-get update
в операторе RUN
вызывает проблемы с кешированием и
последующие инструкции apt-get install
завершаются ошибкой.Например, скажем, у вас есть
Dockerfile:
ОТ ubuntu: 18.04
ЗАПУСТИТЬ apt-get update
ЗАПУСТИТЬ apt-get install -y curl
После построения образа все слои находятся в кеше Docker. Предположим, вы позже
измените apt-get install
, добавив дополнительный пакет:
ОТ ubuntu: 18.04
ЗАПУСТИТЬ apt-get update
ЗАПУСТИТЬ apt-get install -y curl nginx
Docker видит исходную и измененную инструкции как идентичные и повторно использует
кеш из предыдущих шагов.В результате apt-get update
- это , а не .
потому что сборка использует кешированную версию. Поскольку обновление apt-get
не
run, ваша сборка потенциально может получить устаревшую версию curl
и
nginx
пакетов.
Использование RUN apt-get update && apt-get install -y
гарантирует, что ваш файл Dockerfile
устанавливает последние версии пакета без дальнейшего кодирования или руководства
вмешательство. Этот метод известен как «очистка кеша».Вы также можете добиться
очистка кеша путем указания версии пакета. Это называется закреплением версий,
например:
ВЫПОЛНИТЬ apt-get update && apt-get install -y \
пакет-бар \
пакет-баз \
пакет-foo = 1.3. *
Закрепление версий заставляет сборку получить определенную версию независимо от
что в кеше. Этот метод также может уменьшить количество отказов из-за непредвиденных изменений.
в необходимых пакетах.
Ниже приведена хорошо сформированная инструкция RUN
, которая демонстрирует все apt-get
.
рекомендации.
ВЫПОЛНИТЬ apt-get update && apt-get install -y \
aufs-tools \
automake \
build-essential \
завиток \
dpkg-sig \
libcap-dev \
libsqlite3-dev \
ртутный \
реппро \
рубин1.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> / номер
Docker выполняет эти команды с помощью интерпретатора / bin / sh -c
, который только
оценивает код выхода последней операции в конвейере, чтобы определить успех.
В приведенном выше примере этот этап сборки завершается успешно и создает новое изображение до тех пор, пока
поскольку команда wc -l
завершается успешно, даже если команда wget
не выполняется.
Если вы хотите, чтобы команда завершилась ошибкой из-за ошибки на любом этапе конвейера,
добавьте set -o pipefail &&
, чтобы избежать непредвиденной ошибки
построить из непреднамеренно успешных. Например:
RUN set -o pipefail && wget -O - https://some.site | wc -l> / номер
Не все оболочки поддерживают опцию
-o pipefail
.В таких случаях как
тире оболочка
на
Образы на основе Debian, рассмотрите возможность использования формы exec изRUN
для явного
выберите оболочку, которая поддерживает опциюpipefail
.Например:RUN ["/ bin / bash", "-c", "set -o pipefail && wget -O - https://some.site | wc -l> / number"]
CMD
Ссылка на файл Dockerfile для инструкции CMD
Команда CMD
должна использоваться для запуска программного обеспечения, содержащегося в вашем
изображение вместе с любыми аргументами. CMD
почти всегда следует использовать в форме
of CMD ["исполняемый файл", "param1", "param2"…]
. Таким образом, если изображение для
сервис, такой как Apache и Rails, вы должны запустить что-то вроде CMD
.Действительно, такая форма инструкции рекомендуется
["apache2", "- DFOREGROUND"]
для любого служебного образа.
В большинстве других случаев для CMD
должна быть предоставлена интерактивная оболочка, такая как bash,
питон и перл. Например, CMD ["perl", "-de0"]
, CMD ["python"]
или CMD.
. Использование этой формы означает, что когда вы выполняете что-то вроде
["php", "-a"]
docker run -it python
, вы попадете в рабочую оболочку, готовую к работе.
CMD
следует редко использовать в качестве CMD ["param", "param"]
в
в сочетании с ENTRYPOINT
, если
вы и ваши ожидаемые пользователи уже хорошо знакомы с тем, как ENTRYPOINT
работает.
EXPOSE
Ссылка на файл Docker для инструкции 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
также полезна для обеспечения необходимой среды
переменные, специфичные для сервисов, которые вы хотите поместить в контейнер, например Postgres
PGDATA
.
Наконец, 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 / postgress &&…
ENV PATH = / usr / local / postgres- $ PG_MAJOR / bin: $ PATH
Подобно постоянным переменным в программе (в отличие от жесткого кодирования
значений), этот подход позволяет вам изменить одну инструкцию ENV
на
автоматически увеличивать версию программного обеспечения в вашем контейнере.
Каждая строка ENV
создает новый промежуточный уровень, как и команды RUN
. Этот
означает, что даже если вы отключите переменную среды в будущем слое, она
все еще сохраняется в этом слое, и его значение нельзя сбросить. Вы можете проверить это
создание Dockerfile, как показано ниже, а затем его сборка.
ОТ альпийский
ENV ADMIN_USER = "отметка"
RUN echo $ ADMIN_USER> ./mark
RUN отключено от ADMIN_USER
$ docker run --rm test sh -c 'echo $ ADMIN_USER'
отметка
Чтобы предотвратить это и действительно отключить переменную среды, используйте команду RUN
с командами оболочки, чтобы устанавливать, использовать и отключать переменную на одном уровне.Вы можете разделить свои команды с помощью ;
или &&
. Если вы воспользуетесь вторым методом,
и одна из команд дает сбой, docker build
тоже не работает. Обычно это
хорошая идея. Использование \
в качестве символа продолжения строки для Linux Dockerfiles
улучшает читаемость. Вы также можете поместить все команды в сценарий оболочки
и пусть команда RUN
просто запустит этот сценарий оболочки.
ОТ альпийский
ВЫПОЛНИТЬ экспорт ADMIN_USER = "mark" \
&& эхо $ ADMIN_USER>./отметка \
&& отключить ADMIN_USER
CMD sh
$ docker run --rm test sh -c 'echo $ ADMIN_USER'
ДОБАВИТЬ или КОПИРОВАТЬ
Хотя ADD
и COPY
функционально похожи, в общем случае COPY
является предпочтительным. Это потому, что он более прозрачен, чем ADD
. Только КОПИЯ
поддерживает базовое копирование локальных файлов в контейнер, а ADD
имеет
некоторые функции (например, извлечение только локального tar и поддержка удаленных URL), которые
не сразу очевидно.Следовательно, лучше всего для ADD
использовать локальный tar-файл.
автоматическое извлечение в образ, как в ADD rootfs.tar.xz /
.
Если у вас есть несколько шагов Dockerfile
, которые используют разные файлы из вашего
context, КОПИРОВАТЬ
их по отдельности, а не все сразу. Это гарантирует, что
кеш сборки каждого шага становится недействительным (принудительно повторный запуск шага), если
изменяются специально необходимые файлы.
Например:
Требования к КОПИИ.txt / tmp /
RUN pip install --requirement /tmp/requirements.txt
КОПИРОВАТЬ. / tmp /
Приводит к меньшему количеству инвалидаций кэша для шага RUN
, чем если бы вы поместили
КОПИЯ. / tmp /
до него.
Поскольку размер изображения имеет значение, использование ADD
для получения пакетов с удаленных URL-адресов является
категорически не рекомендуется; вместо этого вы должны использовать curl
или wget
. Таким образом вы можете
Удалите файлы, которые вам больше не нужны после того, как они были извлечены, а вы не
нужно добавить еще один слой в ваше изображение.Например, вам следует избегать
такие вещи, как:
ДОБАВИТЬ https://example.com/big.tar.xz / usr / src / things /
ЗАПУСТИТЬ tar -xJf /usr/src/things/big.tar.xz -C / usr / src / things
ЗАПУСТИТЬ make -C / usr / src / things all
И вместо этого сделайте что-нибудь вроде:
ЗАПУСТИТЬ mkdir -p / usr / src / things \
&& curl -SL https://example.com/big.tar.xz \
| tar -xJC / usr / src / вещи \
&& make -C / usr / src / things all
Для других элементов (файлов, каталогов), которые не требуют ADD
tar
возможность автоматического извлечения, вы всегда должны использовать COPY
.
ENTRYPOINT
Ссылка на файл Dockerfile для инструкции ENTRYPOINT
Лучше всего использовать ENTRYPOINT
, чтобы установить основную команду изображения, что позволяет
образ для запуска, как если бы это была эта команда (а затем используйте CMD
в качестве
флаги по умолчанию).
Начнем с примера образа для инструмента командной строки s3cmd
:
ENTRYPOINT ["s3cmd"]
CMD ["--help"]
Теперь образ можно запустить следующим образом, чтобы отобразить справку по команде:
Или используя правильные параметры для выполнения команды:
$ docker run s3cmd ls s3: // mybucket
Это полезно, потому что имя изображения может дублировать ссылку на двоичный файл, как
показано в команде выше.
Инструкция ENTRYPOINT
также может использоваться в сочетании с помощником.
скрипт, позволяющий ему работать аналогично приведенной выше команде, даже
при запуске инструмента может потребоваться более одного шага.
Например, официальный образ Postgres
использует следующий скрипт в качестве ENTRYPOINT
:
#! / Bin / bash
set -e
если ["$ 1" = 'postgres']; тогда
chown -R postgres "$ PGDATA"
если [-z "$ (ls -A" $ PGDATA ")"]; тогда
gosu postgres initdb
фи
exec gosu postgres "$ @"
фи
exec "$ @"
Настроить приложение как PID 1
Этот скрипт использует команду
exec
Bash
так что последнее запущенное приложение становится PID 1 контейнера.Этот
позволяет приложению получать любые сигналы Unix, отправленные в контейнер.
Для получения дополнительной информации см. СправкуENTRYPOINT
.
Вспомогательный сценарий копируется в контейнер и запускается через ENTRYPOINT
на
начало контейнера:
КОПИЯ ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["postgres"]
Этот сценарий позволяет пользователю взаимодействовать с Postgres несколькими способами.
Можно просто запустить Postgres:
Или его можно использовать для запуска Postgres и передачи параметров на сервер:
$ docker run postgres postgres --help
Наконец, его также можно использовать для запуска совершенно другого инструмента, такого как Bash:
$ docker run --rm -it postgres bash
ОБЪЕМ
Ссылка на файл Dockerfile для инструкции VOLUME
Команда VOLUME
должна использоваться для доступа к любой области хранения базы данных,
хранилище конфигурации или файлы / папки, созданные вашим докер-контейнером.Вы
настоятельно рекомендуется использовать VOLUME
для любых изменяемых и / или обслуживаемых пользователем
части вашего изображения.
ПОЛЬЗОВАТЕЛЬ
Ссылка на файл Dockerfile для инструкции USER
Если служба может работать без прав, используйте USER
, чтобы перейти на некорневой
пользователь. Начните с создания пользователя и группы в 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 (\ 0) символы. Обходной путь - передать флаг--no-log-init
в
useradd. Оболочка Debian / Ubuntuadduser
не поддерживает этот флаг.
Избегайте установки или использования sudo
, поскольку он имеет непредсказуемый TTY и
поведение пересылки сигналов, которое может вызвать проблемы. Если вам абсолютно необходимо
функциональность аналогична sudo
, например инициализация демона как root
, но
запуская его как не root
, рассмотрите возможность использования «gosu».
Наконец, чтобы уменьшить количество слоев и сложность, избегайте переключения USER
туда и обратно
часто.
WORKDIR
Ссылка на файл Dockerfile для инструкции WORKDIR
Для ясности и надежности вы всегда должны использовать абсолютные пути для своих
WORKDIR
.Кроме того, вы должны использовать WORKDIR
вместо дополнительных инструкций.
например, RUN cd… && do-something
, которые трудно читать, устранять неполадки и
поддерживать.
НАСТРОЙКА
Ссылка на файл Dockerfile для инструкции ONBUILD
Команда ONBUILD
выполняется после завершения текущей сборки Dockerfile
.
ONBUILD
выполняется в любом дочернем изображении, производном ИЗ
текущего изображения. Думать
команды ONBUILD
в качестве инструкции родительский Dockerfile
дает
ребенку Dockerfile
.
Сборка Docker выполняет команд ONBUILD
перед любой командой в дочернем
Dockerfile
.
ONBUILD
полезен для изображений, которые будут построены ИЗ
заданного
образ. Например, вы можете использовать ONBUILD
для образа языкового стека, который
создает произвольное пользовательское программное обеспечение, написанное на этом языке, в пределах
Dockerfile
, как вы можете видеть в вариантах Ruby ONBUILD
.
Образы, созданные с помощью ONBUILD
, должны получить отдельный тег, например:
рубин: 1.9-onbuild
или ruby: 2.0-onbuild
.
Будьте осторожны, помещая ADD
или COPY
в ONBUILD
. Образ «onbuild»
катастрофически терпит неудачу, если в контексте новой сборки отсутствует ресурс,
добавлен. Добавление отдельного тега, как рекомендовано выше, помогает смягчить это за счет
позволяя автору Dockerfile
сделать выбор.
Примеры официальных изображений
Эти официальные образы содержат примерный файл Dockerfile
s:
Дополнительные ресурсы:
родительский образ, образы, файл докеров, лучшие практики, концентратор, официальный образ
Запустить образ как контейнер
Расчетное время чтения: 9 минут
Предварительные требования
Выполните шаги по созданию образа узла JS в разделе Создание образа узла.
Обзор
В предыдущем модуле мы создали наше примерное приложение, а затем создали файл Dockerfile, который использовали для создания образа. Мы создали наш образ с помощью команды docker build
. Теперь, когда у нас есть изображение, мы можем запустить его и посмотреть, правильно ли работает наше приложение.
Контейнер - это обычный процесс операционной системы, за исключением того, что этот процесс изолирован и имеет свою собственную файловую систему, собственную сеть и собственное изолированное дерево процессов, отдельное от хоста.
Чтобы запустить образ внутри контейнера, мы используем команду docker run
. Для команды запуска docker требуется один параметр - имя образа. Давайте запустим наш образ и убедимся, что он работает правильно. Выполните следующую команду в своем терминале.
Когда вы запустите эту команду, вы заметите, что не вернулись в командную строку. Это связано с тем, что наше приложение является REST-сервером и будет работать в цикле, ожидая входящих запросов, без возврата управления обратно в ОС, пока мы не остановим контейнер.
Давайте сделаем GET-запрос к серверу с помощью команды curl.
$ curl --request POST \
--url http: // localhost: 8000 / test \
--header 'тип содержимого: приложение / json' \
--данные '{
"msg": "тестирование"
} '
curl: (7) Не удалось подключиться к порту localhost 8000: в соединении отказано
Наша команда curl не удалась, потому что в соединении с нашим сервером было отказано. Это означает, что мы не смогли подключиться к localhost через порт 8000. Это ожидается, потому что наш контейнер работает изолированно, что включает в себя сеть.Остановим контейнер и перезапустим его, используя порт 8000, опубликованный в нашей локальной сети.
Чтобы остановить контейнер, нажмите ctrl-c. Это вернет вас в окно терминала.
Чтобы опубликовать порт для нашего контейнера, мы будем использовать флаг --publish
(для краткости -p
) в команде docker run. Формат команды --publish
: [порт хоста]: [порт контейнера]
. Поэтому, если мы хотим открыть порт 8000 внутри контейнера для порта 3000 вне контейнера, мы передадим 3000: 8000 флагу --publish.
Запустите контейнер и откройте порт 8000 для порта 8000 на хосте.
$ docker run --publish 8000: 8000 узел-докер
Теперь давайте повторно запустим команду curl сверху.
$ curl --request POST \
--url http: // localhost: 8000 / test \
--header 'тип содержимого: приложение / json' \
--данные '{
"msg": "тестирование"
} '
{"code": "success", "payload": [{"msg": "testing", "id": "dc0e2c2b-793d-433c-8645-b3a553ea26de", "createDate": "2020-09-01T17: 36:09.897Z "}]}
Успех! Нам удалось подключиться к приложению, запущенному внутри нашего контейнера на порту 8000. Вернитесь к терминалу, на котором работает ваш контейнер, и вы должны увидеть, что запрос POST зарегистрирован в консоли.
2020-09-01T17: 36: 09: 8770 ИНФОРМАЦИЯ: POST / test
Нажмите ctrl-c, чтобы остановить контейнер.
Работа в автономном режиме
Пока это замечательно, но наш пример приложения представляет собой веб-сервер, и нам не нужно подключать терминал к контейнеру.Docker может запускать ваш контейнер в автономном режиме или в фоновом режиме. Для этого мы можем использовать --detach
или -d
для краткости. Docker запустит ваш контейнер так же, как и раньше, но на этот раз «отсоединится» от контейнера и вернет вас в окно терминала.
$ docker run -d -p 8000: 8000 узел-докер
ce02b3179f0f10085db9edfccd731101868f58631bdf918ca490ff6fd223a93b
Docker запустил наш контейнер в фоновом режиме и распечатал идентификатор контейнера на терминале.
Опять же, давайте убедимся, что наш контейнер работает правильно. Выполните ту же команду curl сверху.
$ curl --request POST \
--url http: // localhost: 8000 / test \
--header 'тип содержимого: приложение / json' \
--данные '{
"msg": "тестирование"
} '
{"code": "success", "payload": [{"msg": "testing", "id": "dc0e2c2b-793d-433c-8645-b3a553ea26de", "createDate": "2020-09-01T17: 36: 09.897Z "}]}
Список контейнеров
Поскольку мы запускали наш контейнер в фоновом режиме, как мы узнаем, работает ли наш контейнер или какие еще контейнеры работают на нашей машине? Что ж, мы можем запустить команду docker ps
.Как и в Linux, чтобы увидеть список процессов на вашем компьютере, мы должны запустить команду ps. В том же духе мы можем запустить команду docker ps
, которая покажет нам список контейнеров, запущенных на нашей машине.
$ докер пс
КОНТЕЙНЕР ИДЕНТИФИКАЦИЯ ИЗОБРАЖЕНИЕ КОМАНДА СОЗДАНО СОСТОЯНИЕ ИМЕНА ПОРТОВ
ce02b3179f0f node-docker "docker-entrypoint.s…" 6 минут назад Вверх на 6 минут 0.0.0.0: 8000-> 8000 / tcp чудесный_калам
Команда ps
сообщает кучу информации о наших запущенных контейнерах. Мы можем видеть идентификатор контейнера, изображение, запущенное внутри контейнера, команду, которая использовалась для запуска контейнера, время его создания, статус, открытые порты и имя контейнера.
Вам, наверное, интересно, откуда взялось название нашего контейнера. Поскольку мы не указали имя для контейнера при его запуске, Docker сгенерировал случайное имя.Мы исправим это через минуту, но сначала нам нужно остановить контейнер. Чтобы остановить контейнер, запустите команду docker stop
, которая делает именно это, останавливает контейнер. Вам нужно будет передать имя контейнера, или вы можете использовать идентификатор контейнера.
$ docker stop чудесный_калам
замечательный_калам
Теперь повторно запустите команду docker ps
, чтобы увидеть список запущенных контейнеров.
$ докер пс
КОНТЕЙНЕР ИДЕНТИФИКАЦИЯ ИЗОБРАЖЕНИЕ КОМАНДА СОЗДАНО СОСТОЯНИЕ НАЗВАНИЯ ПОРТОВ
Остановить, запустить и назвать контейнеры
Контейнеры Docker можно запускать, останавливать и перезапускать.Когда мы останавливаем контейнер, он не удаляется, но его статус меняется на «Остановлен», и процесс внутри контейнера останавливается. Когда мы запускали команду docker ps
, по умолчанию выводились только запущенные контейнеры. Если мы передадим --all
или -a
для краткости, мы увидим все контейнеры в нашей системе, независимо от того, остановлены они или запущены.
$ докер пс -а
КОНТЕЙНЕР ИДЕНТИФИКАЦИЯ ИЗОБРАЖЕНИЕ КОМАНДА СОЗДАНА СОСТОЯНИЕ НАЗВАНИЯ ПОРТОВ
ce02b3179f0f node-docker "точка входа докера.s… "16 минут назад Выход (0) 5 минут назад amazing_kalam
ec45285c456d node-docker "docker-entrypoint.s…" 28 минут назад Завершился (0) 20 минут назад agitated_moser
fb7a41809e5d node-docker "docker-entrypoint.s…" 37 минут назад Завершился (0) 36 минут назад goofy_khayyam
Если вы следовали инструкциям, вы должны увидеть несколько контейнеров в списке. Это контейнеры, которые мы запустили и остановили, но не были удалены.
Давайте перезапустим контейнер, который мы только что остановили. Найдите имя контейнера, который мы только что остановили, и замените имя контейнера ниже в команде перезапуска.
$ перезапуск докера чудесный_калам
Теперь снова перечислите все контейнеры с помощью команды ps.
$ docker ps - все
КОНТЕЙНЕР ИДЕНТИФИКАЦИЯ ИЗОБРАЖЕНИЕ КОМАНДА СОЗДАНО СОСТОЯНИЕ ИМЕНА ПОРТОВ
ce02b3179f0f node-docker "точка входа докера.s… »19 минут назад Вверх на 8 секунд 0.0.0.0:8000->8000/tcp amazing_kalam
ec45285c456d node-docker "docker-entrypoint.s…" 31 минуту назад Завершился (0) 23 минуты назад agitated_moser
fb7a41809e5d node-docker "docker-entrypoint.s…" 40 минут назад Завершился (0) 39 минут назад goofy_khayyam
Обратите внимание, что контейнер, который мы только что перезапустили, был запущен в автономном режиме и имеет открытый порт 8000.Также обратите внимание на статус контейнера «Up X секунд». Когда вы перезапускаете контейнер, он будет запущен с теми же флагами или командами, с которыми он был изначально запущен.
Давайте остановим и удалим все наши контейнеры и посмотрим, как исправить проблему случайного именования.
Остановите только что запущенный контейнер. Найдите имя вашего запущенного контейнера и замените имя в приведенной ниже команде на имя контейнера в вашей системе.
$ docker stop чудесный_калам
замечательный_калам
Теперь, когда все наши контейнеры остановлены, давайте удалим их.Когда контейнер удаляется, он больше не работает и не находится в остановленном состоянии. Однако процесс внутри контейнера был остановлен, а метаданные контейнера удалены.
$ docker ps - все
КОНТЕЙНЕР ИДЕНТИФИКАЦИЯ ИЗОБРАЖЕНИЕ КОМАНДА СОЗДАНО СОСТОЯНИЕ ИМЕНА ПОРТОВ
ce02b3179f0f node-docker "docker-entrypoint.s…" 19 минут назад Повышение на 8 секунд 0.0.0.0: 8000-> 8000 / TCP замечательный_калам
ec45285c456d node-docker "docker-entrypoint.s…" 31 минуту назад Завершился (0) 23 минуты назад agitated_moser
fb7a41809e5d node-docker "docker-entrypoint.s…" 40 минут назад Завершился (0) 39 минут назад goofy_khayyam
Чтобы удалить контейнер, просто выполните команду docker rm
, передав имя контейнера. Вы можете передать команде несколько имен контейнеров в одной команде.
И снова убедитесь, что вы заменили имена контейнеров в приведенной ниже команде именами контейнеров из вашей системы.
$ docker rm чудесный_калам agitated_moser goofy_khayyam
замечательный_калам
agitated_moser
goofy_khayyam
Снова выполните команду docker ps --all
, чтобы убедиться, что все контейнеры исчезли.
А теперь давайте рассмотрим надоедливую проблему случайного имени. Стандартная практика - давать своим контейнерам имена по той простой причине, что легче определить, что выполняется в контейнере и с каким приложением или службой оно связано.Так же, как хорошие соглашения об именах для переменных в вашем коде упрощают чтение. Так называем ваши контейнеры.
Чтобы назвать контейнер, нам просто нужно передать флаг --name
команде запуска.
$ docker run -d -p 8000: 8000 --name rest-server node-docker
1aa5d46418a68705c81782a58456a4ccdb56a309cb5e6bd399478d01eaa5cdda
$ docker ps
КОНТЕЙНЕР ИДЕНТИФИКАЦИЯ ИЗОБРАЖЕНИЕ КОМАНДА СОЗДАНО СОСТОЯНИЕ ИМЕНА ПОРТОВ
1aa5d46418a6 node-docker "точка входа докера.s… "3 секунды назад Up 3 секунды 0.0.0.0:8000->8000/tcp rest-server
Теперь мы можем легко идентифицировать наш контейнер по имени.
Заключение
В этом модуле мы рассмотрели запуск контейнеров, порты публикации и запуск контейнеров в отсоединенном режиме. Мы также рассмотрели управление контейнерами путем их запуска, остановки и перезапуска. Мы также рассмотрели возможность именования наших контейнеров, чтобы их было легче идентифицировать.
В следующем модуле мы рассмотрим запуск базы данных в контейнере и подключение ее к нашему приложению.
начало работы, Node JS, запуск, контейнер
Точка входа в контейнер · Los Techies
На этот раз в нашей серии о контейнерах мы собираемся более внимательно изучить, что происходит при запуске контейнера и как мы можем на это повлиять. В частности, мы собираемся узнать о ключевых словах ENTRYPOINT
и CMD
в Dockerfile
, а также о том, как они соотносятся друг с другом.
Всякий раз, когда мы запускаем контейнер из образа, нам нужно объявить, какие команды должны выполняться контейнером при запуске.Это называется точкой входа. Обычно, когда мы определяем Dockerfile
, мы указываем такую точку входа как (один из последних) элементов в объявлении. В качестве примера возьмем Dockerfile
для слишком простого приложения Python, состоящего из одного файла app.py
с кодом. Обычно при запуске приложения Python непосредственно на нашей машине разработки, то есть не в контейнере, мы просто запускаем приложение с помощью этой команды
приложение Python.ру
, следовательно, наш Dockerfile
будет выглядеть так:
обратите внимание на последнюю строку, объявляющую ENTRYPOINT
. Синтаксис, который я выбрал в приведенном выше примере, является одним из возможных способов объявления точки входа. Альтернативный синтаксис - использование массива слов, например
ENTRYPOINT ["python", "app.py"]
Согласно документации, предпочтительнее второй вариант, но, честно говоря, я считаю, что первый гораздо более читабельным; тем не менее, ниже вы увидите, почему синтаксис массива имеет смысл или в определенных сценариях это даже единственный способ работы.
А пока предположим, что app.py
содержит следующий код
Теперь мы можем создать образ, используя Dockerfile
docker build -t python-sample.
И мы должны увидеть что-то вроде этого
Теперь мы можем запустить контейнер из нашего нового образа, например,
docker run -it --rm python-sample
, и мы должны увидеть этот результат
Время от времени мы хотим запустить контейнер из образа, для которого определена ENTRYPOINT
, но мы хотим заменить эту точку входа своей собственной.К счастью, интерфейс командной строки Docker позволяет нам просто сделать это, используя аргумент --entrypoint
при запуске контейнера. В качестве примера: если мы хотим запустить контейнер из нашего образа python-sample
, но предпочитаем запускать оболочку bash при запуске контейнера, мы можем использовать команду запуска, подобную этой
docker run -it --rm --entrypoint / bin / bash python-sample
Эту возможность переопределить точку входа невозможно переоценить. Это очень помогает во многих сценариях.Лично для меня наиболее распространенным вариантом использования является устранение проблем с контейнером путем переопределения точки входа с помощью вызова оболочки bash вместо предопределенной точки входа. Если я имею дело с более простым образом, на котором не установлен bash, например, контейнер Alpine Linux, тогда я просто использую оболочку по умолчанию / bin / sh
.
Другой вариант использования - запуск тестов. Предположим, в нашем проекте есть файл tests.py
, который содержит некоторые тесты.
Затем мы можем запустить контейнер в тестовом режиме следующим образом
docker run -dt --name test --entrypoint python tests.Python-образец
Это действительно полезно! Мы можем использовать один и тот же образ контейнера для генерации онтейнеров, которые выполняют несколько или совершенно разные вещи.
То же самое работает, когда мы используем docker-compose
. Предположим, у нас есть следующий файл docker-compose.yml
в нашем проекте
В строке 5 вы можете увидеть, как мы объявляем, какую точку входа использовать при запуске контейнера.
Docker также определил ключевое слово CMD
, которое можно использовать в файле Dockerfile
.То, что мы объявляем командой, также используется во время запуска контейнера. Хорошо, это звучит запутанно ... Не волнуйтесь, мне тоже потребовалось время, чтобы наконец найти хорошее описание, определяющее различия между ENTRYPOINT
и CMD
.
Проиллюстрируем это на примере. В этом примере я собираюсь использовать официальный образ Alpine Linux, поскольку он очень мал и имеет все необходимое для этой демонстрации. Позвольте мне сначала начать со следующей команды
docker run --rm -it alpine echo "Hello World"
Здесь мы запускаем экземпляр образа alpine в интерактивном режиме и сообщаем Docker, какая команда должна выполняться при запуске контейнера.Результат, как мы видим ниже, соответствует ожиданиям, контейнер просто выводит «Hello World» на терминал. Поскольку мы использовали в команде --rm
, контейнер будет автоматически уничтожен после остановки его выполнения.
Обратите внимание, что контейнер останавливается, как только завершается основной процесс, выполняющийся внутри контейнера. В нашем простом примере это, конечно, происходит сразу после повторения «Hello World».
Теперь мы можем добиться того же с помощью немного измененной команды
docker run --rm -it --entrypoint echo alpine "Hello World"
Итак, что только что произошло? Мы определили команду запуска с помощью --entrypoint
как команду echo
.Часть после имени изображения (alpine) в данном случае - это только строка «Hello World». Он передается в качестве параметра команде запуска. В результате контейнер запускается Docker командой echo "Hello World"
. Итак, в чем разница между первым и вторым? В первом случае мы не декларировали точку входа. В этом случае Docker автоматически принимает / bin / sh -c
в качестве команды запуска и передает ей (как строку) все, что мы определяем в нашей команде docker run
после имени образа.В первом случае это эхо «Hello World»
. Таким образом, этот контейнер был запущен с
/ bin / sh -c "echo \" Hello World \ ""
.
Теперь поработаем с Dockerfile
. Наша первая версия файла выглядит так, то есть мы продолжаем работу с нашим изображением Alpine и сообщением «Hello World»
давайте создадим изображение из этого
docker build -t мой-привет-мир.
и запустите контейнер этого образа
docker run --rm -it мой-привет-мир
мы должны увидеть
В этом примере мы не определяли точку входа явно, поэтому Docker автоматически принимает / bin / sh -c
.Но мы определяем параметры с ключевым словом CMD
, которые будут передаваться в (неявную) точку входа.
В соответствии с тем, что я сказал выше, теперь мы также можем достичь того же результата со следующим Dockerfile
вариант
и действительно, после создания образа и запуска контейнера мы имеем тот же конечный результат
docker build -t my-hello-world-2.
docker run --rm -it мой-привет-мир-2
Обратите внимание, что в Dockerfile мы должны использовать синтаксис массива для ENTRYPOINT
и CMD
, чтобы образец работал.
Теперь я могу переопределить часть, определенную в части CMD
, например следующим образом
docker run --rm -it my-hello-world-2 "Привет, Габриэль"
и действительно работает как положено
Последний вариант моего Dockerfile
использует только ключевое слово ENTRYPOINT
Снова после создания образа и запуска контейнера мы видим ожидаемый результат
В этом посте я немного подробнее рассмотрел, что именно представляют собой ключевые слова ENTRYPOINT
и CMD
в Dockerfile
.То, что я здесь написал, может показаться тривиальным, и это определенно не ракетостроение. Но часто мы упускаем из виду кажущиеся тривиальными вещи и, как следствие, придумываем решения, которые являются более сложными, чем необходимо, или, что еще хуже, у нас есть рабочий пример, и мы не можем полностью объяснить, «почему» он работает.
В проекте, над которым я сейчас работаю, у нас есть много разных контейнеров, с которыми мы работаем, и мы используем их в разных средах и контекстах. Как следствие, мы активно используем вариативность, которую нам дают ENTRYPOINT
и CMD
.
Понимание инструкций Docker CMD и ENTRYPOINT
При создании контейнера Docker обычно цель состоит в том, чтобы любой мог просто выполнить docker run
и запустить контейнер. В сегодняшней статье мы собираемся изучить две ключевые инструкции Dockerfile
, которые позволяют нам сделать именно это. Давайте рассмотрим различия между командами CMD
и ENTRYPOINT
.
На первый взгляд, команды CMD
и ENTRYPOINT
выглядят так, как будто они выполняют одну и ту же функцию.Однако, если копнуть глубже, легко увидеть, что эти две инструкции выполняют совершенно разные задачи.
ApacheBench Dockerfile
В качестве примера мы собираемся создать контейнер Docker, который просто запускает утилиту ApacheBench.
В предыдущих статьях мы обнаружили простоту и полезность инструмента нагрузочного тестирования ApacheBench. Однако этот тип утилит командной строки, как правило, не относится к типу приложений, которые можно было бы Dockerize.«Общее использование Docker больше ориентировано на создание сервисов, а не на отдельные инструменты выполнения, такие как ApacheBench.
Основная причина этого заключается в том, что обычно контейнеры Docker не построены для приема дополнительных параметров при запуске. Это затрудняет использование инструмента командной строки в контейнере.
Давайте посмотрим на это в действии, создав контейнер Docker, который можно использовать для выполнения ApacheBench на любом сайте.
ОТ ubuntu: последний
ЗАПУСТИТЬ apt-get update && \
apt-get install -y apache2-utils && \
rm -rf / var / lib / apt / lists / *
CMD ab
В файле Dockerfile
мы просто используем образ ubuntu: latest
в качестве основного образа контейнера, устанавливаем пакет apache2-utils
и затем определяем, что команда для этого контейнера - это команда ab
.
Поскольку этот контейнер Docker планируется использовать в качестве исполнителя для команды ab
, имеет смысл установить значение инструкции CMD
на команду ab
. Однако, если мы запустим этот контейнер, мы начнем видеть интересную разницу между этим контейнером и другими контейнерами приложений.
Однако, прежде чем мы сможем запустить этот контейнер, нам сначала нужно его построить. Мы можем сделать это с помощью команды docker build
.
$ docker build -t ab.Отправка контекста сборки демону Docker 2.048 kB
Шаг 1/3: ИЗ ubuntu: последний
---> ebcd9d4fca80
Шаг 2/3: ЗАПУСТИТЬ apt-get update && apt-get install -y apache2-utils && rm -rf / var / lib / apt / lists / *
---> Использование кеша
---> d9304ff09c98
Шаг 3/3: CMD ab
---> Использование кеша
---> ecfc71e7fba9
Успешно построенный ecfc71e7fba9
При создании этого контейнера я пометил его именем или
. Это означает, что мы можем просто запустить этот контейнер с именем ab
.
$ docker run ab
ab: неправильное количество аргументов
Использование: ab [параметры] [http [s]: //] имя хоста [: порт] / путь
Варианты:
-n запросы Количество запросов для выполнения
-c concurrency Количество одновременных запросов
-t timelimit Максимальное количество секунд в секундах. потратить на сравнительный анализ
Это означает -n 50000
-s таймаут Макс. ждать каждого ответа
По умолчанию 30 секунд
Когда мы запускаем контейнер ab
, мы получаем сообщение об ошибке от команды ab
, а также сведения об использовании.Причина этого в том, что мы определили команду CMD
для команды ab
без указания каких-либо флагов или целевого хоста, с которым нужно выполнить нагрузочный тест. Эта инструкция CMD
используется для определения того, какую команду контейнер должен выполнять при запуске. Поскольку мы определили это как команду ab
без аргументов, она выполнила команду ab
без аргументов.
Однако, как и большинство инструментов командной строки, ab
работает не так.Для ab
вам нужно указать, какой URL
вы хотите проверить.
Что мы можем сделать для выполнения этой работы, так это переопределить команду CMD
при запуске контейнера. Мы можем сделать это, добавив команду и аргументы, которые мы хотим выполнить, в конце команды docker run
.
$ docker run ab ab http://bencane.com/
Бенчмаркинг bencane.com (проявите терпение) ..... готово
Уровень параллелизма: 1
Время, затраченное на тесты: 0.343 секунды
Выполненных запросов: 1
Неудачные запросы: 0
Всего передано: 98505 байт
Передано HTML: 98138 байт
Запросы в секунду: 2,92 [# / сек] (среднее)
Время на запрос: 342,671 [мс] (среднее)
Время на запрос: 342,671 [мс] (среднее значение для всех одновременных запросов)
Скорость передачи: 280,72 [Кбайт / сек] получено
Когда мы добавляем ab http://bencane.com
в конец нашей команды docker run
, мы можем переопределить инструкцию CMD
и успешно выполнить команду ab
.Однако, хотя мы и добились успеха, этот процесс отмены инструкции CMD
довольно неуклюж.
ВХОД
Здесь сияет инструкция ENTRYPOINT
. Инструкция ENTRYPOINT
работает очень аналогично CMD
в том, что она используется для указания команды, выполняемой при запуске контейнера. Однако разница в том, что ENTRYPOINT
не позволяет вам переопределить команду.
Вместо этого все, что добавляется в конец команды docker run
, добавляется к команде.Чтобы лучше понять это, давайте изменим нашу инструкцию CMD
на инструкцию ENTRYPOINT
.
ОТ ubuntu: последний
ЗАПУСТИТЬ apt-get update && \
apt-get install -y apache2-utils && \
rm -rf / var / lib / apt / lists / *
ENTRYPOINT ["ab"]
После редактирования Dockerfile
нам нужно будет снова создать образ.
$ docker build -t ab.
Отправка контекста сборки демону Docker 2.048 кБ
Шаг 1/3: ИЗ ubuntu: последний
---> ebcd9d4fca80
Шаг 2/3: ЗАПУСТИТЬ apt-get update && apt-get install -y apache2-utils && rm -rf / var / lib / apt / lists / *
---> Использование кеша
---> d9304ff09c98
Шаг 3/3: ENTRYPOINT ab
---> Использование кеша
---> aa020cfe0708
Успешно построено aa020cfe0708
Теперь мы можем снова запустить контейнер ab
; однако на этот раз вместо того, чтобы указывать ab http://bencane.com
, мы можем просто добавить http: // bencane.com
до конца команды docker run
.
$ docker run ab http://bencane.com/
Бенчмаркинг bencane.com (проявите терпение) ..... готово
Уровень параллелизма: 1
Время на тесты: 0,436 секунды
Выполненных запросов: 1
Неудачные запросы: 0
Всего передано: 98505 байт
Передано HTML: 98138 байт
Запросы в секунду: 2,29 [# / сек] (среднее)
Время на запрос: 436,250 [мс] (среднее)
Время на запрос: 436,250 [мс] (среднее значение для всех одновременных запросов)
Скорость передачи: 220.51 [Кбайт / сек] получено
Как видно из приведенного выше примера, мы фактически превратили наш контейнер в исполняемый файл. При желании мы могли бы добавить дополнительные флаги к инструкции ENTRYPOINT
, чтобы упростить сложный инструмент командной строки в контейнер Docker с одним аргументом.
Будьте осторожны с синтаксисом
В инструкции ENTRYPOINT
следует обратить внимание на то, что синтаксис имеет решающее значение. Технически ENTRYPOINT
поддерживает синтаксис ENTRYPOINT ["команда"]
и синтаксис ENTRYPOINT команды
.Однако, хотя оба они поддерживаются, они имеют два разных значения и меняют принцип работы ENTRYPOINT
.
Давайте изменим наш Dockerfile
, чтобы он соответствовал этому синтаксису, и посмотрим, как он меняет поведение наших контейнеров.
ОТ ubuntu: последний
ЗАПУСТИТЬ apt-get update && \
apt-get install -y apache2-utils && \
rm -rf / var / lib / apt / lists / *
ENTRYPOINT ab
С внесенными изменениями приступим к созданию контейнера.
$ docker build -t ab.Отправка контекста сборки демону Docker 2.048 kB
Шаг 1/3: ИЗ ubuntu: последний
---> ebcd9d4fca80
Шаг 2/3: ЗАПУСТИТЬ apt-get update && apt-get install -y apache2-utils && rm -rf / var / lib / apt / lists / *
---> Использование кеша
---> d9304ff09c98
Шаг 3/3: ENTRYPOINT ab
---> Использование кеша
---> bbfe2686a064
Успешно построенный bbfe2686a064
Когда контейнер построен, давайте снова запустим его, используя те же параметры, что и раньше.
$ docker run ab http: // bencane.com /
ab: неправильное количество аргументов
Использование: ab [параметры] [http [s]: //] имя хоста [: порт] / путь
Варианты:
-n запросы Количество запросов для выполнения
-c concurrency Количество одновременных запросов
-t timelimit Максимальное количество секунд в секундах. потратить на сравнительный анализ
Это означает -n 50000
-s таймаут Макс. ждать каждого ответа
По умолчанию 30 секунд
Похоже, мы вернулись к тому же поведению, что и инструкция CMD
.Однако, если мы попытаемся переопределить ENTRYPOINT
, мы увидим другое поведение, чем при переопределении инструкции CMD
.
$ docker run ab ab http://bencane.com/
ab: неправильное количество аргументов
Использование: ab [параметры] [http [s]: //] имя хоста [: порт] / путь
Варианты:
-n запросы Количество запросов для выполнения
-c concurrency Количество одновременных запросов
-t timelimit Максимальное количество секунд в секундах. потратить на сравнительный анализ
Это означает -n 50000
-s таймаут Макс.ждать каждого ответа
По умолчанию 30 секунд
С помощью инструкции ENTRYPOINT
невозможно переопределить инструкцию во время выполнения команды docker run
, как в случае с CMD
. Это подчеркивает другое использование ENTRYPOINT
как метода обеспечения выполнения определенной команды при запуске рассматриваемого контейнера, независимо от попыток переопределить ENTRYPOINT
.
Сводка
В этой статье мы довольно много рассказали о CMD
и ENTRYPOINT
; тем не менее, эти две инструкции по-прежнему используются в других целях, которые позволяют настроить запуск контейнера Docker.Чтобы увидеть некоторые из этих примеров, вы можете взглянуть на справочную документацию Docker Dockerfile
.
Однако в приведенном выше примере у нас теперь есть способ «докеризовать» простые инструменты командной строки, такие как ab
, что открывает довольно много интересных вариантов использования. Если он у вас есть, не стесняйтесь поделиться им в комментариях ниже.
Dockerfile: ENTRYPOINT против CMD - CenturyLink Cloud Developer Center
При просмотре инструкций, доступных для использования в Dockerfile, некоторые из них могут поначалу показаться избыточными (или, по крайней мере, иметь значительное совпадение).Мы уже рассмотрели ADD и COPY, а теперь рассмотрим ENTRYPOINT и CMD.
И ENTRYPOINT, и CMD позволяют указать команду запуска для образа, но между ними есть тонкие различия. Во многих случаях вы захотите выбрать один или другой, но их также можно использовать вместе. Мы рассмотрим все эти сценарии в следующих разделах.
Ежемесячные советы Docker
ENTRYPOINT или CMD
В конечном итоге и ENTRYPOINT, и CMD позволяют определить, какой исполняемый файл должен запускаться при запуске контейнера из вашего образа.Фактически, если вы хотите, чтобы ваш образ был запускаемым (без дополнительных docker run
аргументов командной строки), вы должны указать ENTRYPOINT или CMD.
Попытка запустить изображение, для которого не объявлены ENTRYPOINT или CMD, приведет к ошибке
$ docker run alpine
FATA [0000] Ответ от демона об ошибке: команда не указана
Многие базовые образы дистрибутива Linux, которые вы найдете в Docker Hub, будут использовать оболочку, например / bin / sh или / bin / bash , в качестве исполняемого файла CMD.Это означает, что любой, кто запускает эти образы, по умолчанию попадает в интерактивную оболочку (при условии, конечно, что они использовали флаги -i
и -t
с командой docker run
).
Это имеет смысл для базового образа общего назначения, но вы, вероятно, захотите выбрать более конкретный CMD или ENTRYPOINT для своих собственных изображений.
Заменяет
ENTRYPOINT или CMD, которые вы указываете в своем Dockerfile, определяют исполняемый файл по умолчанию для вашего образа.Однако у пользователя есть возможность переопределить любое из этих значений во время выполнения.
Например, предположим, что у нас есть следующий файл Dockerfile
ОТ ubuntu: верный
CMD ping localhost
Если мы создадим этот образ (с тегом "demo") и запустим его, мы увидим следующий результат:
$ docker run -t демонстрация
PING localhost (127.0.0.1) 56 (84) байтов данных.
64 байта от localhost (127.0.0.1): icmp_seq = 1 ttl = 64 time = 0,051 мс
64 байта от localhost (127.C
--- статистика пинга localhost ---
2 пакета переданы, 2 получены, потеря пакетов 0%, время 999 мс
rtt min / avg / max / mdev = 0,026 / 0,032 / 0,039 / 0,008 мс
Вы можете видеть, что исполняемый файл ping запускался автоматически при запуске контейнера. Однако мы можем переопределить CMD по умолчанию, указав аргумент после имени изображения при запуске контейнера:
$ docker run demo hostname
6c1573c0d4c0
В этом случае hostname был запущен вместо ping
ENTRYPOINT по умолчанию может быть аналогичным образом переопределен, но для этого требуется использование флага --entrypoint
:
$ docker run --entrypoint hostname demo
075a2fa95ab7
Учитывая, насколько проще переопределить CMD, рекомендуется использовать CMD в вашем Dockerfile, когда вы хотите, чтобы пользователь вашего образа имел гибкость для запуска любого исполняемого файла, который они выберут при запуске контейнера.Например, возможно, у вас есть общий образ Ruby, который по умолчанию запускает интерактивный сеанс irb ( CMD irb
), но вы также хотите дать пользователю возможность запускать произвольный сценарий Ruby ( docker run ruby ruby -e 'помещает "Hello"'
)
Напротив, ENTRYPOINT следует использовать в сценариях, где вы хотите, чтобы контейнер вел себя исключительно так, как если бы это был исполняемый файл, который он обертывает. То есть, когда вы не хотите или не ожидаете, что пользователь переопределит указанный вами исполняемый файл.
Во многих ситуациях может быть удобно использовать Docker в качестве переносимой упаковки для конкретного исполняемого файла. Представьте, что у вас есть утилита, реализованная в виде сценария Python, который вам нужно распространить, но вы не хотите обременять конечного пользователя установкой правильной версии интерпретатора и зависимостей. Вы можете упаковать все в образ Docker с помощью ENTRYPOINT, ссылающегося на ваш скрипт. Теперь пользователь может просто docker запустить
ваш образ, и он будет вести себя так, как будто они запускают ваш скрипт напрямую.
Конечно, вы можете добиться того же с помощью CMD, но использование ENTRYPOINT посылает убедительное сообщение о том, что этот контейнер предназначен только для выполнения этой одной команды.
Утилита ENTRYPOINT станет более ясной, когда мы покажем, как можно объединить ENTRYPOINT и CMD вместе, но мы вернемся к этому позже.
Shell против Exec
Инструкции ENTRYPOINT и CMD поддерживают две разные формы: форму оболочки и форму exec .В приведенном выше примере мы использовали оболочку формы , которая выглядит так:
Исполняемый файл CMD param1 param2
При использовании формы оболочки указанный двоичный файл выполняется с вызовом оболочки с использованием / bin / sh -c
. Вы можете ясно увидеть это, если запустите контейнер, а затем посмотрите на вывод docker ps
:
$ docker run -d демо
15bfcddb11b5cde0e230246f45ba6eeb1e6f56edb38aab9c478408cb615
$ docker ps -l
СОЗДАНА КОМАНДА ИЗОБРАЖЕНИЯ ID КОНТЕЙНЕРА
15bfcddb4312 demo: последний "/ bin / sh -c 'ping localhost'" 2 секунды назад
Здесь мы снова запустили "демонстрационный" образ, и вы можете увидеть, что была выполнена команда / bin / sh -c 'ping localhost'
.
Кажется, что это работает нормально, но есть некоторые тонкие проблемы, которые могут возникнуть при использовании оболочки формы либо инструкции ENTRYPOINT, либо CMD. Если мы заглянем внутрь нашего работающего контейнера и посмотрим на запущенные процессы, мы увидим что-то вроде этого:
$ docker exec 15bfcddb ps -f
UID PID PPID C STIME TTY TIME CMD
корень 1 0 0 20:14? 00:00:00 / bin / sh -c ping localhost
корень 9 1 0 20:14? 00:00:00 пинг localhost
корень 49 0 0 20:15? 00:00:00 пс -f
Обратите внимание, что процесс, работающий как PID 1, - это , а не наша команда ping, а исполняемый файл / bin / sh .Это может быть проблематично, если нам нужно отправлять какие-либо сигналы POSIX в контейнер, поскольку / bin / sh не будет пересылать сигналы дочерним процессам (подробные сведения см. В разделе «Изящная остановка контейнеров Docker»).
Помимо проблемы с PID 1, вы также можете столкнуться с проблемами с оболочкой формы , если вы создаете минимальный образ, который даже не включает двоичный файл оболочки. Когда Docker создает команду для запуска, он не проверяет, доступна ли оболочка внутри контейнера - если в вашем образе нет / bin / sh , контейнер просто не запустится.
Лучшим вариантом является использование формы exec инструкций ENTRYPOINT / CMD, которая выглядит следующим образом:
CMD ["исполняемый файл", "параметр1", "параметр2"]
Обратите внимание, что содержимое, появляющееся после инструкции CMD, в этом случае форматируется как массив JSON.
Когда используется форма exec инструкции CMD, команда будет выполняться без оболочки.
Давайте изменим наш Dockerfile из приведенного выше примера, чтобы увидеть это в действии:
ОТ ubuntu: верный
CMD ["/ bin / ping", "localhost"]
Восстановите образ и посмотрите на команду, созданную для работающего контейнера:
$ docker build -t demo.[усечено]
$ docker run -d демо
90cd472887807467d699b55efaf2ee5c4c79eb74ed7849fc4d2dbfea31dce441
$ docker ps -l
СОЗДАНА КОМАНДА ИЗОБРАЖЕНИЯ ID КОНТЕЙНЕРА
90cd47288780 demo: последний "/ bin / ping localhost" 4 секунды назад
Теперь / bin / ping запускается напрямую, без промежуточного процесса оболочки (и, как следствие, внутри контейнера будет PID 1).
Независимо от того, используете ли вы ENTRYPOINT или CMD (или оба), рекомендуется всегда использовать форму exec , чтобы было очевидно, какая команда выполняется как PID 1 внутри вашего контейнера.
ENTRYPOINT и CMD
До этого момента мы обсуждали, как использовать ENTRYPOINT или CMD, чтобы указать исполняемый файл вашего образа по умолчанию. Однако в некоторых случаях имеет смысл использовать CMD ENTRYPOINT и вместе.
Комбинация ENTRYPOINT и CMD позволяет вам указать исполняемый файл по умолчанию для вашего образа, а также предоставить аргументы по умолчанию для этого исполняемого файла, которые могут быть переопределены пользователем. Давайте посмотрим на пример:
ОТ ubuntu: верный
ENTRYPOINT ["/ bin / ping", "- c", "3"]
CMD ["локальный хост"]
Давайте создадим и запустим этот образ без дополнительных docker run
аргументов:
$ docker build -t ping.[усечено]
$ docker запустить пинг
PING localhost (127.0.0.1) 56 (84) байтов данных.
64 байта от localhost (127.0.0.1): icmp_seq = 1 ttl = 64 time = 0,025 мс
64 байта от localhost (127.0.0.1): icmp_seq = 2 ttl = 64 time = 0,038 мс
64 байта от localhost (127.0.0.1): icmp_seq = 3 ttl = 64 time = 0,051 мс
--- статистика пинга localhost ---
3 пакета переданы, 3 получены, потеря пакетов 0%, время 1999 мс
rtt min / avg / max / mdev = 0,025 / 0,038 / 0,051 / 0,010 мс
$ docker ps -l
СОЗДАНА КОМАНДА ИЗОБРАЖЕНИЯ ID КОНТЕЙНЕРА
82df66a2a9f1 ping: latest "/ bin / ping -c 3 localhost" 6 секунд назад
Обратите внимание, что выполненная команда представляет собой комбинацию значений ENTRYPOINT и CMD, указанных в файле Dockerfile.Если указаны как ENTRYPOINT, так и CMD, строка (строки) CMD будут добавлены к ENTRYPOINT, чтобы сгенерировать командную строку контейнера.
Помните, что значение CMD можно легко переопределить, предоставив один или несколько аргументов команде docker run после имени образа. В этом случае мы могли бы направить наш пинг на другой хост, сделав что-то вроде этого:
$ docker запустить ping docker.io
PING docker.io (162.242.195.84) 56 (84) байт данных.
64 байта из 162.242.195.84: icmp_seq = 1 ttl = 61 время = 76,7 мс
64 байта из 162.242.195.84: icmp_seq = 2 ttl = 61 time = 81,5 мс
64 байта из 162.242.195.84: icmp_seq = 3 ttl = 61 time = 77,8 мс
--- docker.io статистика пинга ---
3 пакета передано, 3 получено, потеря пакетов 0%, время 2003 мс
rtt min / avg / max / mdev = 76,722 / 78,695 / 81,533 / 2,057 мс
$ docker ps -l --no-trunc
СОЗДАНА КОМАНДА ИЗОБРАЖЕНИЯ ID КОНТЕЙНЕРА
0d739d5ea4e5 ping: latest "/ bin / ping -c 3 docker.io" 51 секунду назад
Запуск образа начинается с ощущения запуска любого другого исполняемого файла - вы указываете имя команды, которую хотите запустить, а затем аргументы, которые вы хотите передать этой команде.
Обратите внимание на то, как аргумент -c 3
, который был включен как часть ENTRYPOINT, по сути, становится «жестко закодированным» аргументом для команды ping (флаг -c
используется для ограничения количества запросов ping до указанного число). Он включается в каждый вызов изображения и не может быть переопределен так же, как параметр CMD.
Всегда Exec
При одновременном использовании ENTRYPOINT и CMD важно, чтобы всегда и использовали форму exec обеих инструкций.Попытка использовать оболочку формы или смешивание и сопоставление оболочки и exec форм почти никогда не даст вам желаемого результата.
В таблице ниже показана командная строка, полученная в результате объединения различных форм команд ENTRYPOINT и CMD.
Команда Dockerfile
ENTRYPOINT / bin / ping -c 3
CMD локальный хост / bin / sh -c '/ bin / ping -c 3' / bin / sh -c localhost
ENTRYPOINT ["/ bin / ping", "- c", "3"]
CMD локальный хост / bin / ping -c 3 / bin / sh -c localhost
ENTRYPOINT / bin / ping -c 3
CMD ["локальный хост"] "/ bin / sh -c '/ bin / ping -c 3' localhost
ENTRYPOINT ["/ bin / ping", "- c", "3"]
CMD ["локальный хост"] / bin / ping -c 3 локальный хост
Единственный из них, который приводит к действительной командной строке, - это когда ENTRYPOINT и CMD оба указаны с использованием формы exec .