Основные инструкции Docker
В этой статье вы поближе познакомитесь с Docker-файлом и Docker-инструкциями. Получите дельные советы, посмотрите на примеры использования.
118К открытий125К показов
Это третья часть серии статей про Docker и она всецело посвящена Docker-файлам. В первой части основные концепции Docker объясняются на простых примерах из жизни. Во второй статье — краткий обзор экосистемы Docker.
Docker-образы
Docker-образ создаётся во время сборки, а Docker-контейнер — во время запуска приложения.
Docker-файл — сердце Docker’а. Он указывает Docker’у как построить образ, который будет использоваться при создании контейнера.
Каждый Docker-образ содержит файл с именем Dockerfile (он без расширения). При вызове docker build
предполагается, что Dockerfile будет находиться в текущей рабочей директории. Но с помощью флага -f
можно указать другое расположение.
Контейнер состоит из ряда слоёв. Все слои доступны только для чтения, кроме последнего – он располагается над остальными. Docker-файл указывает порядок добавления слоёв.
Каждый слой — это просто файл с изменением предыдущего слоя. В Unix практически всё является файлом.
Базовый слой, его ещё называют родительским, – это начальный слой.
При загрузке Docker-образа из удалённого репозитория скачиваются только отсутствующие у вас слои. Docker экономит место и время, повторно используя уже существующие слои.
Инструкция Docker-файла — слово в верхнем регистре, которое стоит перед аргументом какой-либо команды. Каждая строка в Docker-файле может содержать инструкцию, все они обрабатываются сверху вниз. Инструкции выглядят так:
И только инструкции FROM
, RUN
, COPY
и ADD
создают слои в конечном образе. Другие инструкции производят настройку, добавляют метаданные или же просто говорят Docker’у сделать что-либо во время запуска (например открыть порт или выполнить команду).
Эта статья предполагает использование Unix Docker-образа. Вы, конечно, можете использовать и Windows Docker-образ, но он медленнее, менее удобный и, вообще, его не часто применяют. Так что, пользуйтесь Unix по возможности.
Несколько Docker-инструкций
FROM
— задаёт родительский (главный) образ;LABEL
— добавляет метаданные для образа. Хорошее место для размещения информации об авторе;ENV
— создаёт переменную окружения;RUN
— запускает команды, создаёт слой образа. Используется для установки пакетов и библиотек внутри контейнера;COPY
— копирует файлы и директории в контейнер;ADD
— делает всё то же, что и инструкцияCOPY
. Но ещё может распаковывать локальные.tar
файлы;CMD
— указывает команду и аргументы для выполнения внутри контейнера. Параметры могут быть переопределены. Использоваться может только одна инструкцияCMD
;WORKDIR
— устанавливает рабочую директорию для инструкцииCMD
иENTRYPOINT
;ARG
— определяет переменную для передачи Docker’у во время сборки;ENTRYPOINT
— предоставляет команды и аргументы для выполняющегося контейнера. Суть его несколько отличается отCMD
, о чём мы поговорим ниже;EXPOSE
— открывает порт;VOLUME
— создаёт точку подключения директории для добавления и хранения постоянных данных.
Инструкции и примеры к ним
Docker-файл чисто теоретически может содержать только одну строчку:
FROM
Docker-файл должен начинаться с инструкции FROM
или ARG
, за которой следует FROM
. Команда FROM
говорит Docker’у использовать базовый образ, который соответствует репозиторию и тегу.
В этом примере хранилище образов — Ubuntu. Ubuntu — название официального Docker-репозитория, в котором и содержится данная ОС.
Заметьте, что этот Docker-файл содержит тег для базового образа: 18.04, который указывает Docker’у, какую именно версию образа нужно использовать. Если тег не указан, по умолчанию берётся последняя версия образа. Но лучше всё же указывать тег базового образа. Когда Docker-файл, приведённый выше, используется для создания локального Docker-образа впервые, он загружает слои, указанные в образе Ubuntu.
При создании Docker-контейнера, вы помещаете наверх слой, который впоследствии можно будет изменить.
Когда образ запущен, и нужно произвести некоторые изменения, файл копируется на верхний слой, доступный для редактирования. Узнать, как это делается, можно здесь.
Подробнее про Docker-файл
Кроме того, что ваш однострочный образ сжат, он ещё и медленный, предоставляет мало информации и ничего не делает во время запуска контейнера. Посмотрите на более длинный Docker-файл, который запускает более легковесный образ, а также выполняет скрипт во время запуска.
Но что же это всё обозначает?
В роли базового образа выступает официальный Python-образ с тегом 3.7.2-alpine3.8. Как вы можете увидеть из исходников, образ включает в себя Linux, Python и ничего более. Alpine-образы очень популярны, потому что они маленькие, быстрые и безопасные. Однако Alpine-образы не поставляются сразу со всеми компонентами, характерными для вашей ОС. Некоторые пакеты вам придётся установить самостоятельно.
LABEL
Следующая инструкция — LABEL
. LABEL
добавляет метаданные к образу, предоставляет контактную информацию. Она не замедляет процесс запуска и не занимает много места, наоборот, обеспечивает образ полезной информацией, так что обязательно используйте её. Больше про LABEL
читайте здесь.
ENV
ENV
создаёт переменную окружения, которая становится доступной во время запуска контейнера. В примере выше вы могли видеть использование переменной ADMIN
при создании контейнера.
ENV
удобна для обозначения констант. Если константа используется в нескольких местах файла Dockerfile, и вам понадобится изменить её значение позднее, это можно будет сделать в одном месте.
Docker-файл зачастую предоставляет несколько путей решения одной задачи. Будет хорошо, если в вашем решении будет учитываться баланс Docker-соглашений, прозрачность и скорость. К примеру, RUN
, CMD
и ENTRYPOINT
служат различным целям и могут использоваться для выполнения команд.
RUN
RUN
создаёт слой во время запуска. Docker фиксирует состояние образа после каждой инструкции RUN
.
Чаще всего используется для установки нужных пакетов внутрь контейнера. В примере выше RUN apk update && apk upgrade
говорит Docker’у обновить пакеты из базового образа. && apk add bash
указывает на то, что для базового образа нужно установить bash
.
apk
— это сокращение от Alpine Linux package manager. Если вы используете базовый образ не Alpine Linux, то установка пакетов производится командой RUN apt-get
.
RUN
и её родственные инструкции: CMD
, ENTRYPOINT
— могут быть как форме оболочки, так и в форме shell-скрипта. Во втором случае используют JSON-синтаксис: RUN ["my_executable", "my_first_param1", "my_second_param2"]
. А в примере выше использовалась форма оболочки: RUN apk update && apk upgrade && apk add bash
.
Позднее в вашем Docker-файле вы будете создавать новую директорию, используя ["mkdir", "/a_directory"]
. Не забывайте, что в JSON нужно использовать двойные кавычки!
COPY
Инструкция COPY . ./app
говорит Docker’у, что нужно скопировать файлы и папки из вашей локальной сборки в рабочую директорию образа. COPY
создаст все нужные папки, если они отсутствуют.
ADD
ADD
делает то же самое, что и COPY
, но с двумя отличиями. ADD
может загружать файлы по URL, а также извлекать локальные TAR-файлы.
В примере выше ADD
копировала файлы по URL внутрь директории контейнера. Но официальныя документация не рекомендует использовать ADD
так, потому что потом вы попросту не сможете удалить файлы. А дополнительные файлы увеличивают размер образа.
Ещё официальная документация для ясности рекомендует использовать, когда это возможно, COPY
вместе ADD
. Жаль только, что в Docker’е невозможно использовать ADD
и COPY
в одной команде.
Заметьте, инструкция содержит символ \
. Это нужно для лучшей читаемости – так вы разбиваете длинную инструкцию на несколько строк.
CMD
CMD
— инструкция для запуска чего-либо во время запуска самого контейнера. По ходу сборки она не фиксирует никакого результата. В примере выше во время сборки запускался скрипт my_script.py
.
Ещё пара моментов о CMD
:
- Только одна
CMD
-инструкция на весь Docker-файл. Иначе все кроме последней будут проигнорированы; CMD
может включать исполняемый файл;- Если же
CMD
не содержит никакого файла, обязательно должна быть инструкцияENTRYPOINT
. В этом случает обе инструкции должны быть в формате JSON; - Аргументы командной строки для запуска Docker переопределяют аргументы, предоставленные
CMD
в Docker-файле.
Готовы к большему?
В следующем примере представлены ещё несколько Docker-инструкций:
В Docker-файле вы можете добавлять комментарии. Комментарии начинаются со знака #
.
Обычно установка пакетов — приоритетная задача для Docker’а. Как говорилось ранее, есть несколько способов загрузки пакетов при помощи инструкции RUN
.
Для Alpine Docker-образа вы используете apk. apk
для типичной Linux-сборки — apt-get
. Например, пакеты для базового Ubuntu-образа могут быть установлены и обновлены так: RUN apt-get update && apt-get install my_package
.
В дополнение к apk
и apt-get
, Python-пакеты могут быть установлены через pip, wheel и conda. Методы варьируются в зависимости от языка.
Нижележащие слои должны предоставить свое средство установки и управления пакетами. Если возникнет проблема с установкой пакетов, убедитесь, что у вас установлен менеджер пакетов.
Можно использовать RUN
вместе с pip и списком нужных пакетов. Для этого объедините команды установки пакетов в одну инструкцию и разделите их символом продолжения строки (\
). Этот метод позволяет улучшить читаемость и уменьшить количество слоев (из-за отсутствия возможности использовать несколько RUN
инструкций).
Также вы можете запустить установщик, указав ему файл, содержащий все зависимости для вашего образа. Обычно он называется requirements.txt.
WORKDIR
Меняет текущую рабочую директорию в контейнере для инструкций: COPY
, ADD
, RUN
и ENTRYPOINT
.
Несколько замечаний:
- Предпочтительно задать абсолютный путь с помощью WORKDIR, а не перемещаться по файловой системе с помощью команд
cd
в Docker-файле; WORKDIR
автоматически создаёт директорию, если её ещё нет;- Можно использовать несколько
WORKDIR
-инструкций. Если используются относительные пути — каждая инструкция поменяет рабочую директорию.
ARG
Определяет переменную для передачи из командной строки в образ. Для ARG
можно указать значение по умолчанию: ARG my_var=my_default_value
.
В отличие от ENV
-переменных, ARG
-переменные не доступны для запущенных контейнеров. Однако вы можете использовать их для установки дефолтных значений для ENV
-переменных, когда вы создаёте образ. И затем ENV-переменные сохраняются. Больше про это вы найдёте здесь.
ENTRYPOINT
ENTRYPOINT
тоже позволяет вам задавать дефолтные команды и аргументы во время запуска контейнера. Она похожа на CMD
, но параметры ENTRYPOINT
не переопределяются, если контейнер запущен с параметрами командной строки.
Вместо этого аргументы командной строки, передаваемые docker run myimagename
, добавляются к аргументам инструкции ENTRYPOINT
. Например, docker run my_image bash
добавляет аргумент bash
в конец, ко всем другим аргументам ENTRYPOINT
.
Docker-файл обязательно должен содержать либо CMD
-инструкцию, либо ENTRYPOINT
-инструкцию.
В официальной документации есть несколько советов, которые помогут сделать выбор между CMD
и ENTRYPOINT
для начальной команды:
- Если вам нужно запускать одну и туже команду несколько раз, выбирайте
ENTRYPOINT
; - Используйте
ENTRYPOINT
, когда ваш контейнер выступает в роли исполняющейся программы; - При наличии дополнительных дефолтных аргументов, которые могут быть изменены через командную строку, лучше подойдёт
CMD
.
В примере выше, ENTRYPOINT ["python", "my_script.py", "my_var"]
запускает в контейнере Python-скрипт my_script.py
с аргументом my_var
. Затем переменная my_var
может быть использована в my_script
argparse. Заметьте, у my_var
есть дефолтное значение, ранее установленное в Docker-файле при помощи ARG
. Так что, если аргумент не будет задан через командную строку, возьмётся его значение по умолчанию.
Как правило, Docker рекомендует вам использовать исполняемую форму с JSON-синтаксисом ENTRYPOINT ["executable", "param1", "param2"]
.
EXPOSE
Инструкция EXPOSE
показывает, какой порт пробрасывать из контейнера.
Используйте команду docker run
с флагом -p
для пробрасывания и сопоставления нескольких портов во время запуска. Флаг в верхнем регистре -P
будет пробрасывать все открытые порты.
VOLUME
VOLUME
определяет, где контейнер будет хранить постоянные данные и получать к ним доступ.
Заключение
В этой статье не были упомянуты такие инструкции, как USER
, ONBUILD
, STOPSIGNAL
, SHELL
, и HEALTHCHECK
, информацию про них вы сможете найти здесь.
118К открытий125К показов