«1. Обзор

В Dockerfile мы часто сталкиваемся с такими инструкциями, как run, cmd или entrypoint. На первый взгляд, все они используются для указания и запуска команд. Но какая между ними разница? И как они взаимодействуют друг с другом?

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

2. Настройка

Для начала создадим скрипт log-event.sh. Он просто добавляет одну строку в файл, а затем печатает его:

#!/bin/sh

echo `date` [email protected] >> log.txt;
cat log.txt;

А теперь давайте создадим простой Dockerfile:

FROM alpine
ADD log-event.sh /

Он будет использовать наш скрипт, добавляя строки в log.txt в разные сценарии.


3. Команда запуска

Команда запуска выполняется, когда мы собираем образ. Это означает, что команда, переданная для запуска, выполняется поверх текущего изображения в новом слое. Затем результат фиксируется на изображении. Давайте посмотрим, как это выглядит в действии.

Во-первых, мы добавим в наш Dockerfile инструкцию запуска:

FROM alpine
ADD log-event.sh /
RUN ["/log-event.sh", "image created"]

Во-вторых, давайте создадим наш образ с помощью:

docker build -t myimage .

Теперь мы ожидаем, что образ Docker будет содержать файл log.txt с одной созданной линией изображения внутри. Давайте проверим это, запустив контейнер на основе образа:

docker run myimage cat log.txt

При выводе списка содержимого файла мы увидим такой вывод:

Fri Sep 18 20:31:12 UTC 2020 image created

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

Давайте теперь снова построим наш образ. Мы замечаем, что время создания в нашем журнале не изменилось. Это происходит потому, что Docker кэширует результат выполнения инструкции, если Dockerfile не изменился. Если мы хотим аннулировать кеш, нам нужно передать параметр «no-cache» команде сборки.

4. Команда cmd

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

...
RUN ["/log-event.sh", "image created"]
CMD ["/log-event.sh", "container started"]

После сборки образа запустим его и проверим вывод:

$ docker run myimage
Fri Sep 18 18:27:49 UTC 2020 image created
Fri Sep 18 18:34:06 UTC 2020 container started

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

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

$ docker run myimage cat log.txt
Fri Sep 18 18:27:49 UTC 2020 image created

На этот раз cmd, указанный в Dockerfile, игнорируется. Это потому, что мы указали аргументы для команды запуска docker.

Давайте продолжим и посмотрим, что произойдет, если у нас есть более одной записи cmd в Dockerfile. Давайте добавим новую запись, которая будет отображать другое сообщение:

...
RUN ["/log-event.sh", "image created"]
CMD ["/log-event.sh", "container started"]
CMD ["/log-event.sh", "container running"]

После создания образа и повторного запуска контейнера мы увидим следующий вывод:

$ docker run myimage
Fri Sep 18 18:49:44 UTC 2020 image created
Fri Sep 18 18:49:58 UTC 2020 container running

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

5. Команда точки входа

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

Давайте заменим запись cmd в нашем Dockerfile на точку входа:

...
RUN ["/log-event.sh", "image created"]
ENTRYPOINT ["/log-event.sh"]

Теперь давайте запустим контейнер, предоставив пользовательскую текстовую запись:

$ docker run myimage container running now
Fri Sep 18 20:57:20 UTC 2020 image created
Fri Sep 18 20:59:51 UTC 2020 container running now

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

Как и в случае с cmd, в случае нескольких записей точек входа учитывается только последняя.

6. Взаимодействие между cmd и точкой входа

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

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

...
RUN ["/log-event.sh", "image created"]
ENTRYPOINT ["/log-event.sh"]
CMD ["container started"]

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

$ docker run myimage
Fri Sep 18 21:26:12 UTC 2020 image created
Fri Sep 18 21:26:18 UTC 2020 container started

Мы также можем переопределить их, если выберем Итак:

$ docker run myimage custom event
Fri Sep 18 21:26:12 UTC 2020 image created
Fri Sep 18 21:27:25 UTC 2020 custom event

Следует отметить различное поведение точки входа при использовании в форме оболочки. Давайте обновим точку входа в нашем Dockerfile:

...
RUN ["/log-event.sh", "image created"]
ENTRYPOINT /log-event.sh
CMD ["container started"]

В этой ситуации при запуске контейнера мы увидим, как Docker игнорирует любые аргументы, переданные либо в docker run, либо в cmd.

7. Заключение

В этой статье мы рассмотрели различия и сходства между инструкциями Docker: run, cmd и entrypoint. Мы наблюдали, в какой момент они вызываются. Кроме того, мы рассмотрели их использование и то, как они работают вместе.