Разное

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.

Ссылки:

  1. 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 с краткими комментариями, а потом разберем их подробнее:

  1. FROM — задаёт базовый (родительский) образ
  2. LABEL — описывает метаданные (например, кто создал образ)
  3. ENV — устанавливает постоянные переменные среды
  4. RUN — выполняет команду и создаёт слой образа
  5. COPY — копирует в контейнер файлы и директории
  6. ADD — копирует файлы и папки в контейнер, может распаковывать .tar-файлы
  7. CMD — описывает команду с аргументами, которая выполняется при запуске контейнера
  8. WORKDIR — задаёт рабочую директорию для следующей инструкции
  9. ARG — задаёт переменные для передачи Docker во время сборки образа
  10. ENTRYPOINT — задает команду с аргументами для вызова во время выполнения контейнера
  11. EXPOSE — указывает на необходимость открыть порт
  12. 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

GET     /tasks

POST    /tasks

DELETE  /tasks/{id}

GET     /tasks/{id}

Сама задача моделируется как постоянный объект, который будет управляться Hibernate ORM и храниться в базе данных отношений MySQL .

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

@Entity

@Table(name = "tasks")

public class Task {

    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    @Column(name = "id")

    private Integer id;

     

    @Column(name = "title", nullable = false, length = 255)

    private String title;

     

    @Column(name = "description", nullable = true, columnDefinition = "TEXT")

    private String description;

     

    

    ...

}

На этом сходство обоих приложений заканчивается, и каждое из них будет следовать своему идиоматическому пути развития.

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

buildscript {

    repositories {

    }

   

    dependencies {

        classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.0.M6"

    }

}

 

plugins {

    id 'com.palantir.docker' version '0.13.0'

}

 

apply plugin: "org.springframework.boot"

apply plugin: 'java'

apply plugin: 'eclipse'

apply plugin: 'application'

 

sourceCompatibility = 1.8

targetCompatibility = 1.8

 

dependencies {

    compile("org.flywaydb:flyway-core:4.2.0")

    compile("org.springframework.boot:spring-boot-starter-webflux:2.0.0.M6")

    compile("org.springframework.boot:spring-boot-starter-data-jpa:2.0.0.M6")

    compile("org.springframework.boot:spring-boot-starter-actuator:2.0.0.M6")

    compile("mysql:mysql-connector-java:8.0.7-dmr")

}

 

repositories {

    maven {

        mavenCentral()

    }

}

 

springBoot {

    mainClassName = "com.javacodegeeks.spring.AppStarter"

}

 

jar {

    mainClassName = "com.javacodegeeks.spring.AppStarter"

    baseName = 'spring-boot-webapp '

    version = project.version

}

 

bootJar {

    baseName = 'spring-boot-webapp '

    version = project.version

}

 

docker {

    name "jcg/spring-boot-webapp:$project.version"

    tags 'latest'

    dependsOn build

    files bootJar

    dockerfile file('src/main/docker/Dockerfile')

    buildArgs([BUILD_VERSION: project.version])

}

Это на самом деле довольно просто, все мясо в основном находится в разделе build.gradle файла build.gradle . Вы также можете заметить, что мы используем наш собственный Dockerfile , src/main/docker/Dockerfile , чтобы предоставить Docker инструкции по созданию образа.

01

02

03

04

05

06

07

08

09

10

11

FROM openjdk:8-jdk-alpine

ARG BUILD_VERSION

 

ENV DB_HOST localhost

ENV DB_PORT 3306

 

ADD spring-boot-webapp-${BUILD_VERSION}.jar spring-boot-webapp.jar

 

EXPOSE 19900

 

ENTRYPOINT exec java $JAVA_OPTS -Ddb.host=$DB_HOST -Ddb.port=$DB_PORT -jar /spring-boot-webapp.jar

Действительно, так просто, как только можно. Обратите внимание, как мы используем инструкцию ARG (и настройку build.gradle файле build.gradle ) для передачи аргументов в изображение. В этом случае мы передаем версию проекта, чтобы найти окончательные артефакты сборки. Еще одна интересная деталь, на которую стоит обратить внимание, — это использование инструкций ENV для соединения хоста и порта экземпляра MySQL для подключения. И, как вы уже догадались, инструкция EXPOSE информирует Docker о том, что контейнер прослушивает порт 19900 во время выполнения.

Круто, а что дальше? Ну, нам просто нужно запустить нашу сборку Gradle , вот так:

1

2

3

4

> gradle clean docker dockerTag

...

BUILD SUCCESSFUL in 12s

15 actionable tasks: 14 executed, 1 up-to-date

Задача dockerTag самом деле не нужна, но из-за этой проблемы, о которой сообщалось в отношении плагина Palantir Docker Gradle, мы должны явно вызвать его, чтобы правильно dockerTag наше изображение. Давайте проверим, есть ли у нас изображение, доступное локально.

1

2

3

4

5

> docker image ls

REPOSITORY               TAG            IMAGE ID      CREATED             SIZE

jcg/spring-boot-webapp   0.0.1-SNAPSHOT 65057c7ae9ba  21 seconds ago      133MB

jcg/spring-boot-webapp   latest         65057c7ae9ba  21 seconds ago      133MB

...

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

1

2

3

4

5

6

docker run --rm -d \

  --name mysql \

  -e MYSQL_ROOT_PASSWORD='p$ssw0rd' \

  -e MYSQL_DATABASE=my_app_db \

  -e MYSQL_ROOT_HOST=% \

  mysql:8.0.2

Теперь мы готовы запустить наше приложение в виде контейнера Docker . Есть несколько способов, которыми мы могли бы ссылаться на контейнер MySQL , при этом определяемая пользователем сеть является предпочтительным вариантом. Для простых случаев, подобных нашему, мы можем просто обратиться к нему, назначив DB_HOST среды DB_HOST IP-адрес работающего контейнера MySQL , например:

1

2

3

4

5

docker run -d --rm \

  --name spring-boot-webapp \

  -p 19900:19900 \

  -e DB_HOST=`docker inspect --format '{{ .NetworkSettings.IPAddress }}' mysql` \

  jcg/spring-boot-webapp:0.0.1-SNAPSHOT

Сопоставив порт 19900 от контейнера к хосту, мы могли общаться с нашим приложением, получая доступ к его REST (ful) API из curl, используя localhost качестве имени хоста. Давайте сделаем это прямо сейчас.

01

02

03

04

05

06

07

08

09

10

11

$ curl -X POST http://localhost:19900/tasks \

   -d '[{"title": "Task #1", "description": "Sample Task"}]' \

   -H "Content-Type: application/json"

 

[

  {

    "id":1,

    "title":"Task #1",

    "description":"Sample Task"

  }

]

1

2

3

4

5

6

7

8

9

$ curl http://localhost:19900/tasks

 

[

  {

    "id":1,

    "title":"Task #1",

    "description":"Sample Task"

  }

]

1

2

3

4

5

6

7

$ curl http://localhost:19900/tasks/1

 

{

  "id":1,

  "title":"Task #1",

  "description":"Sample Task"

}

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

FROM gradle:4.3.0-jdk8-alpine

 

ADD src src

ADD build.gradle .

ADD gradle.properties .

 

RUN gradle build

 

FROM openjdk:8-jdk-alpine

ARG BUILD_VERSION

 

ENV DB_HOST localhost

ENV DB_PORT 3306

 

COPY --from=0 /home/gradle/build/libs/spring-boot-webapp-${BUILD_VERSION}.jar spring-boot-webapp.jar

 

EXPOSE 19900

 

ENTRYPOINT exec java $JAVA_OPTS -Ddb.host=$DB_HOST -Ddb.port=$DB_PORT -jar /spring-boot-webapp.jar

Первая часть определения 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 image build \

  --build-arg BUILD_VERSION=0.0.1-SNAPSHOT \

  -f Dockerfile.build \

  -t jcg/spring-boot-webapp:latest \

  -t jcg/spring-boot-webapp:0.0.1-SNAPSHOT .

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

1

2

3

4

5

$ docker image ls

REPOSITORY               TAG            IMAGE ID       CREATED           SIZE

jcg/spring-boot-webapp   0.0.1-SNAPSHOT  02abf724da64  10 seconds ago    133MB

jcg/spring-boot-webapp   latest          02abf724da64  10 seconds ago    133MB

...

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

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

<project

  xmlns=http://maven.apache.org/POM/4.0.0

  xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance

  <modelVersion>4.0.0</modelVersion>

 

  <groupId>com.javacodegeeks</groupId>

  <artifactId>dropwizard-webapp</artifactId>

  <version>0.0.1-SNAPSHOT</version>

  <packaging>jar</packaging>

 

  <properties>

      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

  </properties>

 

  <dependencyManagement>

    <dependencies>

      <dependency>

        <groupId>io.dropwizard</groupId>

        <artifactId>dropwizard-bom</artifactId>

        <version>1.2.0</version>

        <type>pom</type>

        <scope>import</scope>

      </dependency>

    </dependencies>

  </dependencyManagement>

 

  <dependencies>

    <dependency>

      <groupId>io.dropwizard</groupId>

      <artifactId>dropwizard-core</artifactId>

    </dependency>

 

    <dependency>

      <groupId>io.dropwizard</groupId>

      <artifactId>dropwizard-hibernate</artifactId>

    </dependency>

         

    <dependency>

      <groupId>mysql</groupId>

      <artifactId>mysql-connector-java</artifactId>

      <version>8.0.7-dmr</version>

    </dependency>

 

    <dependency>

      <groupId>io.dropwizard.modules</groupId>

      <artifactId>dropwizard-flyway</artifactId>

      <version>1.2.0-1</version>

    </dependency>

         

    <dependency>

      <groupId>com.google.guava</groupId>

      <artifactId>guava</artifactId>

    </dependency>

     

    <dependency>

      <groupId>junit</groupId>

      <artifactId>junit</artifactId>

      <scope>test</scope>

    </dependency>

  </dependencies>

 

  <build>

    <plugins>

      <plugin>

        <groupId>org.apache.maven.plugins</groupId>

        <artifactId>maven-compiler-plugin</artifactId>

        <version>3.1</version>

        <configuration>

          <source>1.8</source>

          <target>1.8</target>

        </configuration>

      </plugin>

      <plugin>

        <groupId>org.apache.maven.plugins</groupId>

        <artifactId>maven-jar-plugin</artifactId>

        <version>3.0.2</version>

        <configuration>

          <archive>

            <manifest>

              <mainClass>com.javacodegeeks.docker.AllApiApp</mainClass>

            </manifest>

          </archive>

        </configuration>

      </plugin>

      <plugin>

        <groupId>org.apache.maven.plugins</groupId>

        <artifactId>maven-shade-plugin</artifactId>

        <version>3.1.0</version>

        <configuration>

          <filters>

            <filter>

              <artifact>*:*</artifact>

              <excludes>

                <exclude>META-INF/*.SF</exclude>

                <exclude>META-INF/*.DSA</exclude>

                <exclude>META-INF/*.RSA</exclude>

              </excludes>

            </filter>

          </filters>

        </configuration>

        <executions>

          <execution>

            <phase>package</phase>

            <goals>

              <goal>shade</goal>

            </goals>

            <configuration>

              <transformers>

                <transformer

implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>

                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">                                 

                  <mainClass>com.javacodegeeks.dw.AppStarter</mainClass>

                </transformer>

              </transformers>

            </configuration>

          </execution>

        </executions>

      </plugin>

      <plugin>

        <groupId>com.spotify</groupId>

        <artifactId>docker-maven-plugin</artifactId>

        <version>1.0.0</version>

        <configuration>

          <imageName>jcg/dropwizard-webapp:${project.version}</imageName>

          <dockerDirectory>src/main/docker</dockerDirectory>

          <resources>

            <resource>

              <targetPath>/</targetPath>

              <directory>${project.build.directory}</directory>

              <include>${project.build.finalName}.jar</include>

            </resource>

            <resource>

              <targetPath>/</targetPath>

              <directory>${project.basedir}</directory>

              <include>application.yml</include>

            </resource>

          </resources>

          <buildArgs>

            <BUILD_VERSION>${project.version}</BUILD_VERSION>

          </buildArgs>

          <imageTags>

            <tag>latest</tag>

          </imageTags>

        </configuration>

      </plugin>

    </plugins>

  </build>

</project>

Честно говоря, это выглядит значительно более многословно, чем сборка Gradle , но если мы на секунду представим, что все теги XML пропали, мы получим в основном идентичное определение, по крайней мере, в случае плагинов Docker . Dockerfile немного отличается, хотя:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

FROM openjdk:8-jdk-alpine

ARG BUILD_VERSION

 

ENV DB_HOST localhost

ENV DB_PORT 3306

 

ADD dropwizard-webapp-${BUILD_VERSION}.jar dropwizard-webapp.jar

ADD application.yml application.yml

ADD docker-entrypoint.sh docker-entrypoint.sh

RUN chmod a+x /docker-entrypoint.sh

 

EXPOSE 19900 19901

 

ENTRYPOINT ["/docker-entrypoint.sh"]

Из-за особенностей приложения Dropwizard , мы должны связать файл конфигурации, в нашем случае application.yml , вместе с приложением. Вместо того, чтобы выставлять только один порт 19900 , мы должны выставить другой, 19901 , для административных задач. И последнее, но не менее важное: мы предоставляем скрипт для инструкции ENTRYPOINT , docker-entrypoint.sh .

01

02

03

04

05

06

07

08

09

10

#!/bin/sh

 

set -e

java $JAVA_OPTS -DDB_HOST=$DB_HOST -DDB_PORT=$DB_PORT -jar /dropwizard-webapp.jar db migrate application.yml

 

if [ ! $? -ne 0 ]; then

  exec java $JAVA_OPTS -DDB_HOST=$DB_HOST -DDB_PORT=$DB_PORT -jar /dropwizard-webapp.jar server application.yml

fi

 

exec "$@"

Причиной добавления некоторой сложности здесь является то, что по умолчанию пакет дополнений Dropwizard Flyway не выполняет автоматическую миграцию схемы базы данных. Мы могли бы обойти это, но самый простой способ — запустить команду db migrate перед запуском приложения Dropwizard . Это именно то, что мы делаем внутри сценария оболочки выше. Теперь пришло время запустить сборку!

01

02

03

04

05

06

07

08

09

10

11

12

>  mvn clean package docker:build

 

...

 

Successfully tagged jcg/dropwizard-webapp:0.0.1-SNAPSHOT

[INFO] Built jcg/dropwizard-webapp:0.0.1-SNAPSHOT

[INFO] Tagging jcg/dropwizard-webapp:0.0.1-SNAPSHOT with latest

[INFO] ---------------------------------------------------------

[INFO] BUILD SUCCESS

[INFO] ---------------------------------------------------------

 

...

Давайте посмотрим, будет ли наше изображение доступно локально на этот раз.

1

2

3

4

5

> docker image ls

REPOSITORY               TAG            IMAGE ID      CREATED             SIZE

jcg/dropwizard-webapp    0.0.1-SNAPSHOT fa9c310683b1  20 seconds ago      128MB

jcg/dropwizard-webapp    latest         fa9c310683b1  20 seconds ago      128MB

...

Отлично, предполагая, что контейнер MySQL запущен и работает (эта часть не меняется вообще, мы могли бы использовать ту же команду из предыдущего раздела ), мы могли бы просто запустить наш контейнер приложения Dropwizard .

1

2

3

4

5

6

docker run -d --rm \

  --name dropwizard-webapp \

  -p 19900:19900 \

  -p 19901:19901 \

  -e DB_HOST=`docker inspect --format '{{ .NetworkSettings.IPAddress }}' mysql` \

  jcg/dropwizard-webapp:0.0.1-SNAPSHOT

Мы также отображаем порты 19900 и 19901 из контейнера на хост, чтобы мы могли использовать localhost качестве имени хоста в curl .

01

02

03

04

05

06

07

08

09

10

11

$ curl -X POST http://localhost:19900/tasks \

   -d '[{"title": "Task #1", "description": "Sample Task"}]' \

   -H "Content-Type: application/json"

 

[

  {

    "id":1,

    "title":"Task #1",

    "description":"Sample Task"

  }

]

1

2

3

4

5

6

7

8

9

$ curl http://localhost:19900/tasks

 

[

  {

    "id":1,

    "title":"Task #1",

    "description":"Sample Task"

  }

]

1

2

3

4

5

6

7

$ curl http://localhost:19900/tasks/1

 

{

  "id":1,

  "title":"Task #1",

  "description":"Sample Task"

}

Обратите внимание, что при сопоставлении портов хоста мы можем запустить либо 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

FROM maven:3.5.2-jdk-8-alpine

 

ADD src src

ADD pom.xml .

 

RUN mvn package

 

FROM openjdk:8-jdk-alpine

ARG BUILD_VERSION

 

ENV DB_HOST localhost

ENV DB_PORT 3306

 

COPY --from=0 /target/dropwizard-webapp-${BUILD_VERSION}.jar dropwizard-webapp.jar

ADD application.yml application.yml

ADD src/main/docker/docker-entrypoint.sh docker-entrypoint.sh

RUN chmod a+x /docker-entrypoint.sh

 

EXPOSE 19900 19901

 

ENTRYPOINT ["/docker-entrypoint.sh"]

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

1

2

3

4

5

docker image build \

  --build-arg BUILD_VERSION=0.0.1-SNAPSHOT \

  -f Dockerfile.build \

  -t jcg/dropwizard-webapp:latest \

  -t jcg/dropwizard-webapp:0.0.1-SNAPSHOT .

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

1

2

3

4

5

> docker image ls

REPOSITORY             TAG             IMAGE ID       CREATED          SIZE

jcg/dropwizard-webapp  0.0.1-SNAPSHOT  5b006fcc9a1d   26 seconds ago   128MB

jcg/dropwizard-webapp  latest          5b006fcc9a1d   26 seconds ago   128MB

...

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

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
 ---> Использование кеша
 ---> 2a1c

f5f Шаг 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» к / relativeDir / :

.

  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-адреса и копируется в .

  • Если - это URL, а заканчивается косой чертой в конце, то
    имя файла выводится из URL-адреса, и файл загружается в
    <самый старый> / <имя файла> . Например, ADD http: // example.com / foobar / будет
    создайте файл / foobar . URL-адрес должен иметь нетривиальный путь, чтобы
    в этом случае можно найти соответствующее имя файла ( http://example.com
    не будет работать).

  • Если - это каталог, копируется все содержимое каталога,
    включая метаданные файловой системы.

Примечание

Сам каталог не копируется, только его содержимое.

  • Если - это локальный tar-архив в распознанном формате сжатия
    (identity, gzip, bzip2 или xz), затем он распаковывается как каталог. Ресурсы
    из удаленного URL-адреса не распакованы . Когда каталог копируется или
    распакованный, он имеет то же поведение, что и tar -x , результатом является объединение:

    1. Все, что было на пути назначения и
    2. Содержимое исходного дерева, конфликты разрешены в пользу
      из «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» к / relativeDir / :

.

  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
передаст аргумент -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 определяют, какая команда будет выполняться при запуске контейнера.
Есть несколько правил, описывающих их сотрудничество.

  1. В

    Dockerfile должна быть указана хотя бы одна из команд CMD или ENTRYPOINT .

  2. ENTRYPOINT должен быть определен при использовании контейнера в качестве исполняемого файла.

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

  4. 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-builder2448/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 / Ubuntu adduser не поддерживает этот флаг.

Избегайте установки или использования 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 демо
15bfcddb11b5cde0e230246f45ba6eeb1e6f56edb38a

ab9c478408cb615 $ 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 .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *