ШПАРГАЛКИ
CI/CD
KUBERNETES
  • DOCKERFILE
  • DOCKER-COMPOSE
  • JENKINSFILE
  • JENKINS-PIPELINE
  • GITLAB CI/CD
  • GROOVIE
  • INSTALL
  • PODS
  • INGRESS

Шпаргалка по 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.

Традиционно 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.

docker build -t svendowideit/ambassador .

Sending build context to Docker daemon 15.36 kB
Step 1 : FROM alpine:3.2
 ---> 31f630c65071
Step 2 : MAINTAINER SvenDowideit@home.org.au
 ---> 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.

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

Исходя из вышеизложенного следующие примеры будут не правильными:

Неправильно из-за переноса строк:

# direc \
tive=value

Не верно, т.к. одна директива используется дважды:

# 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

# escape=\ (backslash)

или

# escape=` (backtick)

Директива 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. Экранирование также обрабатывается для подстанавливаемых переменных.

Переменные среды обозначаются в Dockerfile либо $variable_name, либо ${variable_name}. Они обрабатываются одинаково, и синтаксис скобок обычно используется для решения проблем с именами переменных без пробелов, например ${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

Все файлы с расширением .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 <image>
# или
FROM <image>:<tag>
# или
FROM <image>@<digest>

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

  • FROM должен быть первой инструкцией в Dockerfile (не считая комментариев и директив парсера).

  • FROM может использоваться несколько раз в пределах одного Dockerfile для создания нескольких образов. Просто отслеживайте последний ID образа перед каждой новой командой FROM.

  • Значения tag или digest не обязательны. Если любая из этих опций не задана Докер по умолчанию использует значение latest. Сборщик Docker возвращает ошибку если значение tag не найдено.

FROM AS (много-этапные сборки)

Много-этапные (multi-stage builds) сборки позволяют значительно упростить этот процесс и описать его внутри Dockerfile. Каждая инструкция FROM может использовать индивидуальный базовый образ и каждая из них начинает новую стадию сборки docker образа. Но основное преимущество, что вы можете копировать необходимые артефакты из одной стадии в другую. В результате все вышеперечисленные шаги могут быть описаны вот так
Dockerfile:

FROM golang:latest as build
COPY . .
RUN go build ./src/main.go

FROM alpine:latest as production
COPY --from=build /go/main .
CMD ["./main"]

И все что вам остается, это выполнить команду:

docker image build -t hello_world:latest .


Примечание: отдельно стоит добавить, что к предыдущим образам вы можете обращаться как по алиасу указанному в инструкции FROM golang:latest as build — как в примере выше

COPY --from=build /go/main .

так и по индексу

COPY --from=0 /go/main .

MAINTAINER

MAINTAINER Sergey Antropoff <sergey@antropoff.ru>

Инструкция 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 также указано альтернативное решение.

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"
},


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.

Если пользователь задает аргументы для docker run то они переопределяют аргументы по умолчанию из CMD.

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

EXPOSE

EXPOSE <port> [<port>...]

Инструкция EXPOSE указывает Docker что контейнер слушает определенные порты после запуска. EXPOSE не делает порты контейнера доступными для хоста. Для этого, вы должны использовать флаг -p (что бы открыть диапазон портов) или флаг -P что бы открыть все порты из EXPOSE. Можно задать один номер порта и пробросить его на другой внешний порт.

Docker поддерживает создание сетей без необходимости открытия портов.

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 через STDIN (docker build - < somefile), то у вас нет контекста сборки, и вы можете использовать в Dockerfile только URL для инструкции ADD. Вы также можете передавать сжатый архив через STDIN: (docker build - < archive.tar.gz), Dockerfile в корне архива, а остальная часть архива будет рассматриваться как контекст сборки.

Примечание: Если URL с удаленными файлами защищен аутентификацией, вам стоит использовать RUN wget, RUN curl или любой другой инструмент из контейнера, поскольку инструкция ADD не поддерживает аутентификацию.

Примечание: Первое вхождение инструкции ADD аннулирует кэш всех последующих инструкций в Dockerfile если содержимое <src> было изменено. В том числе и кэш инструкций< code>RUN. 

ADD подчиняется следующим правилам:

  • Путь <src> должен соответствовать контексту сборки; вы не можете выполнить ADD ../something /something, по тому что на первом шаге docker build происходит отправка каталогов контекста (и подкаталогов) в docker daemon.

  • Если <src> является URL и <dest> не заканчивается слешем, то файл загружается из URL и копируется в <dest>.

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

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

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

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

    1. Не зависят от того существует ли путь в который происходит копирование
    2. Файловые конфликты в исходном дереве разрешаются в пользу копируемого

Примечание: Вне зависимости от того поддерживается ли расширение файла или нет, распаковка производится исключительно на основе анализа содержимого файла, а не его имени. К примеру, если название пустого файла заканчивается на .tar.gz он не будет рассматриваться как сжатый файл и не будет сгенерировано ошибки декомпрессии, он просто будет скопирован в указанное место.

  • Если <src> является файлом любого типа, он копируется вместе с метаданными. В случае, если <dest> заканчивается косой чертой (/), он будет считаться каталогом и содержимое <src> будет записано в <dest>/base(<src>).

  • Если задано несколько <src> ресурсов, или задан шаблон, то <dest> должен быть каталогом и заканчиваться косой чертой (/).

  • Если <dest> не заканчивается слешем, он будет рассматриваться как обычный файл и содержимое <src> будет записано <dest>.

  • Если <dest> не существует, то он будет создан со всеми недостающими каталогами.

COPY

COPY имеет две формы:

  • COPY <src>... <dest>
  • COPY ["<src>",... "<dest>"] (эта форма используется для путей с пробелами)

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

Возможно задать несколько <src> путей, но путь должен находится внутри контекста сборки.

Каждый <src> может содержать групповые символы (wildcards) обработка которых осуществляется с использованием правил Go filepath.Match. К примеру:

COPY hom* /mydir/      # adds all files starting with "hom"
COPY hom?.txt /mydir/  # ? is replaced with any single character, e.g., "home.txt"

<dest> абсолютный, или относительный путь для WORKDIR, куда будет произведено копирование в файловую систему контейнера.

COPY test relativeDir/   # adds "test" to `WORKDIR`/relativeDir/
COPY test /absoluteDir/  # adds "test" to /absoluteDir/

Все файлы и папки создаются с UID и GID равными 0.

Примечание: Если сборка производится с использованием STDIN (docker build - < somefile), то контекста сборки не существует и инструкция COPY не может быть использована.

COPY подчиняется следующим правилам:

  • Путь <src> должен находится внутри контекста сборки; вы не можете выполнить инструкцию COPY ../something /something, по тому что на первом шаге docker build посылает каталог контекста (вместе с подкаталогами) в docker daemon.

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

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

  • Если <src> является файлом любого типа, он копируется вместе с метаданными. В случае, если <dest> заканчивается косой чертой (/), он будет считаться каталогом и содержимое <src> будет записано в <dest>/base(<src>).

  • Если задано несколько <src> ресурсов, или задан шаблон, то <dest> должен быть каталогом и заканчиваться косой чертой (/).

  • Если <dest> не заканчивается слешем, он будет рассматриваться как обычный файл и содержимое <src> будет записано <dest>.

  • Если <dest> не существует, то он будет создан со всеми недостающими каталогами.

ENTRYPOINT

ENTRYPOINT имеет две формы:

  • ENTRYPOINT ["executable", "param1", "param2"] (exec форма, предпочтительная)
  • ENTRYPOINT command param1 param2 (shell форма)

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

К примеру, следующая команда запустит nginx с прослушиванием 80 порта:

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

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

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

Только последняя инструкция ENTRYPOINT из Dockerfile будет запущена.

Примеры exec формы ENTRYPOINT

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

FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]

Когда вы запустите контейнер, то можете увидеть что top единственный процесс:

$ docker run -it --rm --name test  top -H
top - 08:25:00 up  7:27,  0 users,  load average: 0.00, 0.01, 0.05
Threads:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.1 us,  0.1 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:   2056668 total,  1616832 used,   439836 free,    99352 buffers
KiB Swap:  1441840 total,        0 used,  1441840 free.  1324440 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
    1 root      20   0   19744   2336   2080 R  0.0  0.1   0:00.04 top

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

$ docker exec -it test ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  2.6  0.1  19752  2352 ?        Ss+  08:24   0:00 top -b -H
root         7  0.0  0.1  15572  2164 ?        R+   08:25   0:00 ps aux

Вы можете без проблем остановить top выполнив команду docker stop test.

Следующий Dockerfile демонстрирует как с помощью ENTRYPOINT запустить Apache в фоновом режим (то есть с PID 1):

FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

Если вам нужно написать стартовый скрипт для одиночного исполняемого файла, вы можете убедиться что конечный исполняемый файл получает сигналы Unix с помощью команд exec и gosu:

#!/bin/bash
set -e

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

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

    exec gosu postgres "$@"
fi

exec "$@"

И наконец, если вам нужно произвести дополнительную очистку (или обмениваться данными с другими контейнерами) при выключении, или координировать больше одного исполняемого файла, вы можете убедиться что скрипт ENTRYPOINT получает Unix сигналы, передает их и делает кое что еще:

#!/bin/sh
# Note: I've written this using sh so it works in the busybox container too

# USE the trap if you need to also do manual cleanup after the service is stopped,
#     or need to start multiple services in the one container
trap "echo TRAPed signal" HUP INT QUIT TERM

# start service in background here
/usr/sbin/apachectl start

echo "[hit enter key to exit] or run 'docker stop <container>'"
read

# stop service and clean up here
echo "stopping apache"
/usr/sbin/apachectl stop

echo "exited $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 START   TIME COMMAND
root         1  0.1  0.0   4448   692 ?        Ss+  00:42   0:00 /bin/sh /run.sh 123 cmd cmd2
root        19  0.0  0.2  71304  4440 ?        Ss   00:42   0:00 /usr/sbin/apache2 -k start
www-data    20  0.2  0.2 360468  6004 ?        Sl   00:42   0:00 /usr/sbin/apache2 -k start
www-data    21  0.2  0.2 360468  6000 ?        Sl   00:42   0:00 /usr/sbin/apache2 -k start
root        81  0.0  0.1  15572  2140 ?        R+   00:44   0:00 ps aux

$ docker top test
PID                 USER                COMMAND
10035               root                {run.sh} /bin/sh /run.sh 123 cmd cmd2
10054               root                /usr/sbin/apache2 -k start
10055               33                  /usr/sbin/apache2 -k start
10056               33                  /usr/sbin/apache2 -k start

$ /usr/bin/time docker stop test
test
real    0m 0.27s
user    0m 0.03s
sys 0m 0.03s

Примечание: вы можете заменить ENTRYPOINT опцией --entrypoint, но указать можно только бинарный файл для exec (sh -c не будет использоваться).

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

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

Примеры shell формы ENTRYPOINT

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

FROM ubuntu
ENTRYPOINT exec top -b

Когда вы запустите данный образ, вы увидите одиночный процесс с PID 1:

$ docker run -it --rm --name test top
Mem: 1704520K used, 352148K free, 0K shrd, 0K buff, 140368121167873K cached
CPU:   5% usr   0% sys   0% nic  94% idle   0% io   0% irq   0% sirq
Load average: 0.08 0.03 0.05 2/98 6
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
    1     0 root     R     3164   0%   0% top -b

Который будет правильно завершен при выполнении команды docker stop:

$ /usr/bin/time docker stop test
test
real    0m 0.20s
user    0m 0.02s
sys 0m 0.04s

Если вы забыли добавить exec в начале ENTRYPOINT:

FROM ubuntu
ENTRYPOINT top -b
CMD --ignored-param1

Вы можете затем запустить образ (добавив имя для следующего шага):

$ docker run -it --name test top --ignored-param2
Mem: 1704184K used, 352484K free, 0K shrd, 0K buff, 140621524238337K cached
CPU:   9% usr   2% sys   0% nic  88% idle   0% io   0% irq   0% sirq
Load average: 0.01 0.02 0.05 2/101 7
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
    1     0 root     S     3168   0%   0% /bin/sh -c top -b cmd cmd2
    7     1 root     R     3164   0%   0% top -b

Вы можете увидеть из вывода top что ENTRYPOINT не имеет PID 1.

Если вы затем выполните docker stop test, контейнер не будет остановлен правильно, команда stopбудет вынуждена отправить SIGKILL после таймаутаt:

$ docker exec -it test ps aux
PID   USER     COMMAND
    1 root     /bin/sh -c top -b cmd cmd2
    7 root     top -b
    8 root     ps aux

$ /usr/bin/time docker stop test
test
real    0m 10.19s
user    0m 0.04s
sys 0m 0.03s

Взаимодействие CMD и ENTRYPOINT

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

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

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

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

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

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

Без ENTRYPOINT ENTRYPOINT exec_entry p1_entry ENTRYPOINT [“exec_entry”, “p1_entry”]
Без CMD error, not allowed /bin/sh -c exec_entry p1_entry exec_entry p1_entry
CMD [“exec_cmd”, “p1_cmd”] exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_cmd p1_cmd exec_entry p1_entry exec_cmd p1_cmd
CMD [“p1_cmd”, “p2_cmd”] p1_cmd p2_cmd /bin/sh -c exec_entry p1_entry p1_cmd p2_cmd 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 /bin/sh -c exec_cmd p1_cmd exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd

VOLUME

VOLUME ["/data"]

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

Команда docker run инициализирует новый том с любыми данными которые существуют в указанном месте в пределах базового образа. К примеру, рассмотрим следующий фрагмент кода из Dockerfile:

FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol

В результате запуска образа собранного из данного Dockerfile командой docker run, будет создана новая точка монтирования /myvol и в ней будет создан файл greeting.

Примечание: Если в процессе каких либо шагов сборки будут изменены данные после объявления тома, то эти изменения будут потеряны.

Примечание: Список обрабатывается как JSON массив, что подразумевает обязательное заключение слов в двойные кавычки (") и ни в коем случае одинарные (').

USER

USER daemon

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

WORKDIR

WORKDIR /path/to/workdir

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

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

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

В результате команда pwd из Dockerfile вернет значение /a/b/c.

Инструкция WORKDIR может использовать переменные окружения заданные через ENV. Вы можете использовать переменные окружения явно заданные в Dockerfile. К примеру:

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

В результате команда pwd в этом Dockerfile вернет /path/$DIRNAME

ARG

ARG <name>[=<default value>]

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

One or more build-args were not consumed, failing build.

Автор Dockerfile может задать одну переменную задав ARG один раз или несколько переменных использовав ARGнесколько раз. Рассмотрим пример корректного Dockerfile:

FROM busybox
ARG user1
ARG buildno
...

Автор Dockerfile может опционально задать значение по умолчанию для переменной с помощью инструкции ARG:

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

Если ARG имеет значение по умолчанию и значение данной переменной не задано при сборке, сборщик использует значение по умолчанию.

Задание переменной с помощью инструкции ARG вступает в силу начинаю со строки на которой она была определена в Dockerfile, а не с момента использования в командной строке или где-то еще. Например рассмотрим следующий Dockerfile:

1 FROM busybox
2 USER ${user:-some_user}
3 ARG user
4 USER $user
...

Далее произведем сборку вызвав команду:

$ docker build --build-arg user=what_user Dockerfile

Инструкция USER в строке 2 принимает значение some_user поскольку переменная user задается только в 3 строке. Инструкция USER в строке 4 принимает значение what_user поскольку переменной user было задано значение what_user. До определения с помощью инструкции ARG, любое использование переменной возвращает пустую строку.

Предупреждение: Не рекомендуется использовать в переменных сборки секретные ключи например от github, учетные данные пользователя и т. д. Значение переменных сборки доступны любому пользователю образа с помощью команды docker history.

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

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER v1.0.0
4 RUN echo $CONT_IMG_VER

Затем запустим сборку образа следующей командой:

$ docker build --build-arg CONT_IMG_VER=v2.0.1 Dockerfile

В данном случае, инструкция RUN использует v1.0.0 вместо значения ARG заданного в строке пользователемv2.0.1. Это поведение похоже на поведение shell скриптов где локальные переменные переопределяют переменные переданные в качестве аргументов или унаследованные от окружения.

Использовав пример выше, но с другой спецификацией ENV вы можете создать более полезные взаимодействия между инструкциями ARG и ENV:

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER ${CONT_IMG_VER:-v1.0.0}
4 RUN echo $CONT_IMG_VER

В отличие от инструкции ARG, значения ENV всегда присутствуют в созданном образе. Рассмотрим сборку без флага the --build-arg:

$ docker build Dockerfile

В данном примере Dockerfile, переменная CONT_IMG_VER сохранится в образе, но ее значение будет равно v1.0.0 поскольку оно задано по умолчанию в строке 3 инструкцией ENV.

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

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

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

Для этого, просто передайте нужный аргумент в командной строке с помощью флага --build-arg <varname>=<value>.

Влияние на кэш сборки

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

Для примера, рассмотрим два следующих Dockerfile:

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 RUN echo $CONT_IMG_VER

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 RUN echo hello

Если задать --build-arg CONT_IMG_VER=<value> в командной строке, в обоих случаях, строка 2 не вызывает промах кэша, в отличие от строки 3. В случае где команда RUN зависит от CONT_IMG_VER=<value>, если <value> изменится, мы получаем промах кеша.

Рассмотрим другой пример с такой же командной строкой:

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER $CONT_IMG_VER
4 RUN echo $CONT_IMG_VER

В данном примере, промах кэша происходит в строке 3. Это происходит по тому что значение переменной в ENV зависит от переменной ARG и эта переменная меняется через командную строку. В этом примере, команда ENV включает значение переменной в образ.

Если инструкция ENV заменяет значение переменной в инструкции ARG с тем же именем, как в этом Dockerfile:

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER hello
4 RUN echo $CONT_IMG_VER

Строка 3 не вызывает промаха кэша по тому что значение CONT_IMG_VER является константой (hello). В результате, переменные окружения и значения используемые в RUN (строка 4) не меняются от сборки к сборке.

ONBUILD

ONBUILD [INSTRUCTION]

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

Любая инструкция сборки может быть установлена в качестве триггера.

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

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

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

Как это работает:

  1. Когда докер встречает инструкцию ONBUILD, сборщик добавляет триггер в метаданные образа. Инструкция не оказывает влияния на текущую сборку.
  2. В конце сборки, список всех триггеров сохраняется в манифесте образа, в ключе OnBuild. Список можно посмотреть с помощью команды docker inspect.
  3. Позже образ может быть использован в качестве базового для новой сборки, с помощью инструкции FROM. Как часть обработки инструкции FROM, сборщик ищет ONBUILD триггеры и выполняет их в том же порядке в котором они были добавлены. Если один из триггеров вызывает ошибку, инструкция FROM обрывается и вызывает ошибку сборки. Если все триггеры отработали, инструкция FROM завершается и сборка продолжается как обычно.
  4. Триггеры очищаются в финальном образе после окончания сборки. Другими словами, дочерние образы не унаследуют триггеры прародителей.

Для примера можно добавить что ни будь вроде этого:

[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]

Предупреждение: Цепи инструкций ONBUILD ONBUILD не допускаются.

Предупреждение: Инструкция ONBUILD не поддерживает FROM или MAINTAINER.

STOPSIGNAL

STOPSIGNAL signal

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

HEALTHCHECK

Инструкция HEALTHCHECK имеет две формы:

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

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

Когда у контейнера задана проверка на работоспособность, он имеет дополнительный статус состояния. Этот статус изначально равен starting. Всякий раз когда проверка состояния проходит успешно, он становится равен healthy (вне зависимости от предыдущего состояния). После определенного числа неудачных проверок статус изменяется на unhealthy.

Перед CMD можно задать следующие опции:

  • --interval=DURATION (по умолчанию: 30s)
  • --timeout=DURATION (по умолчанию: 30s)
  • --retries=N (по умолчанию: 3)

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

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

Параметр retries отвечает за количество неудачных проверок необходимое для смены статуса контейнера на unhealthy.

Можно задать только одну инструкцию HEALTHCHECK в Dockerfile. Если указано несколько HEALTHCHECK, только последняя будет работать.

Команда после ключевого слова CMD может быть либо командой оболочки (например HEALTHCHECK CMD /bin/check-running) или выполнить массив (по аналогии с другими командами Dockerfile; для большей информации читайте документацию по ENTRYPOINT).

Статус завершения команды указывает на состояние контейнера. Возможные значения:

  • 0: success - контейнер исправен и готов к использованию
  • 1: unhealthy - контейнер работает неправильно
  • 2: reserved - не используйте данный код выхода

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

HEALTHCHECK --interval=5m --timeout=3s \
  CMD curl -f http://localhost/ || exit 1

Что бы помочь в отладке, любой исходящий текст (в кодировке UTF-8) который возвращают команды в stdout или stderr будет сохранен в статусе состояния и может быть запрошен командой docker inspect. Такой вывод должен быть коротким (только первые 4096 байт сохраняются на текущий момент).

Когда статус состояния контейнера изменяется, событие health_status генерируется с новым статусом.

Функция HEALTHCHECK была добавлена в Docker 1.12.

SHELL

SHELL ["executable", "parameters"]

Инструкция SHELL позволяет заменить стандартную оболочку для выполнения команд на пользовательскую. Оболочкой по умолчанию в Linux является ["/bin/sh", "-c"], а в Windows ["cmd", "/S", "/C"]. Инструкция SHELL записывается в JSON формате в Dockerfile.

Инструкция SHELL в частности полезна в Windows где есть две совершенно разных оболочки: cmd и powershell, а также альтернативные включая sh.

Инструкция SHELL может использоваться несколько раз. Каждая инструкция SHELL заменяет предыдущую инструкцию SHELL, и влияет на все последующие инструкции. К примеру:

FROM windowsservercore

# Executed as cmd /S /C echo default
RUN echo default

# Executed as cmd /S /C powershell -command Write-Host default
RUN powershell -command Write-Host default

# Executed as powershell -command Write-Host hello
SHELL ["powershell", "-command"]
RUN Write-Host hello

# Executed as cmd /S /C echo hello
SHELL ["cmd", "/S"", "/C"]
RUN echo hello

Следующие инструкции могут быть затронуты командой SHELL при использовании shell формы в Dockerfile: RUN, CMD и ENTRYPOINT.

Следующий пример представляет собой общий шаблон для Windows который может быть упрощен с помощью инструкции SHELL:

...
RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
...

Команда выполняемая docker будет выглядеть так:

cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"

Это не эффективно по двум причинам. Во-первых, вызывается не обязательная команда cmd.exe (т. е. оболочка). Во-вторых, каждая инструкция RUN в shell форме требует ставить перед командой powershell -command.

Есть два механизма позволяющих упростить решение. Одно из них, это использование JSON формы для инструкции RUN:

...
RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]
...

В то время как JSON форма является однозначной и не использует не обязательный cmd.exe, но эта форма записи достаточно избыточна из-за необходимости использовать двойные кавычки и экранирование. Альтернытивным решением является использование инструкции SHELL и shell формы команд, что делает синтаксис более естественным для пользователей Windows, особенно при комбинировании с директивой парсера escape:

# escape=`

FROM windowsservercore
SHELL ["powershell","-command"]
RUN New-Item -ItemType Directory C:\Example
ADD Execute-MyCmdlet.ps1 c:\example\
RUN c:\example\Execute-MyCmdlet -sample 'hello world'
В результате получаем:

PS E:\docker\build\shell> docker build -t shell .
Sending build context to Docker daemon 3.584 kB
Step 1 : FROM windowsservercore
 ---> 5bc36a335344
Step 2 : SHELL powershell -command
 ---> Running in 87d7a64c9751
 ---> 4327358436c1
Removing intermediate container 87d7a64c9751
Step 3 : RUN New-Item -ItemType Directory C:\Example
 ---> Running in 3e6ba16b8df9

    Directory: C:\

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----         6/2/2016   2:59 PM                Example

 ---> 1f1dfdcec085
Removing intermediate container 3e6ba16b8df9
Step 4 : ADD Execute-MyCmdlet.ps1 c:\example\
 ---> 6770b4c17f29
Removing intermediate container b139e34291dc
Step 5 : RUN c:\example\Execute-MyCmdlet -sample 'hello world'
 ---> Running in abdcf50dfd1f
Hello from Execute-MyCmdlet.ps1 - passed hello world
 ---> ba0e25255fda
Removing intermediate container abdcf50dfd1f
Successfully built ba0e25255fda
PS E:\docker\build\shell>

Инструкция SHELL также может быть использована для изменения режима работы оболочки. Например, использовав SHELL cmd /S /C /V:ON|OFF в Windows можно разрешить или запретить отложенное расширение переменных среды.

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

Инструкция SHELL была добавлена в Docker 1.12.

Примеры Dockerfile

Ниже вы можете увидеть некоторые примеры синтаксиса Dockerfile.

# Nginx
#
# VERSION               0.0.1

FROM      ubuntu
MAINTAINER Victor Vieux <victor@docker.com>

LABEL Description="This image is used to start the foobar executable" Vendor="ACME Products" Version="1.0"
RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server

# Firefox over VNC
#
# VERSION               0.3

FROM ubuntu

# Install vnc, xvfb in order to create a 'fake' display and firefox
RUN apt-get update && apt-get install -y x11vnc xvfb firefox
RUN mkdir ~/.vnc
# Setup a password
RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way, but it does the trick)
RUN bash -c 'echo "firefox" >> /.bashrc'

EXPOSE 5900
CMD    ["x11vnc", "-forever", "-usepw", "-create"]

# Multiple images example
#
# VERSION               0.1

FROM ubuntu
RUN echo foo > bar
# Will output something like ===> 907ad6c2736f

FROM ubuntu
RUN echo moo > oink
# Will output something like ===> 695d7793cbe4

# You`ll now have two images, 907ad6c2736f with /bar, and 695d7793cbe4 with
# /oink.

Оцените статью

Еще никто не оставил комментарий. Будьте первым!