Docker-образ создаётся во время сборки, а Docker-контейнер - во время запуска приложения.
Docker-файл - сердце Docker'а. Он указывает Docker'у как построить образ, который будет использоваться при создании контейнера.
Каждый Docker-образ содержит файл с именем Dockerfile (он без расширения). При вызове docker build предполагается, что Dockerfile будет находиться в текущей рабочей директории. Но с помощью флага -f можно указать другое расположение.
Контейнер состоит из ряда слоёв. Все слои доступны только для чтения, кроме последнего - он располагается над остальными. Docker-файл указывает порядок добавления слоёв. Каждый слой - это просто файл с изменением предыдущего слоя. В Unix практически всё является файлом. Базовый слой, его ещё называют родительским, - это начальный слой. При загрузке Docker-образа из удалённого репозитория скачиваются только отсутствующие у вас слои. Docker экономит место и время, повторно используя уже существующие слои.
Инструкция Docker-файла - слово в верхнем регистре, которое стоит перед аргументом какой-либо команды. Каждая строка в Docker-файле может содержать инструкцию, все они обрабатываются сверху вниз. Инструкции выглядят так:
FROM ubuntu:18.04 COPY . /app
И только инструкции FROM, RUN, COPY и ADD создают слои в конечном образе. Другие инструкции производят настройку, добавляют метаданные или же просто говорят Docker'у сделать что-либо во время запуска (например открыть порт или выполнить команду).
Несколько Docker-инструкций
Инструкции и примеры к ним
Docker-файл чисто теоретически может содержать только одну строчку:
FROM ubuntu:18.04
FROM
Docker-файл должен начинаться с инструкции FROM или ARG, за которой следует FROM. Команда FROM говорит Docker'у использовать базовый образ, который соответствует репозиторию и тегу. В этом примере хранилище образов - Ubuntu. Ubuntu - название официального Docker-репозитория, в котором и содержится данная ОС. Заметьте, что этот Docker-файл содержит тег для базового образа: 18.04, который указывает Docker'у, какую именно версию образа нужно использовать. Если тег не указан, по умолчанию берётся последняя версия образа. Но лучше всё же указывать тег базового образа. Когда Docker-файл, приведённый выше, используется для создания локального Docker-образа впервые, он загружает слои, указанные в образе Ubuntu.
Кроме того, что ваш однострочный образ сжат, он ещё и медленный, предоставляет мало информации и ничего не делает во время запуска контейнера. Посмотрите на более длинный Docker-файл, который запускает более легковесный образ, а также выполняет скрипт во время запуска.
FROM python:3.7.2-alpine3.8 LABEL maintainer="jeffmshale@gmail.com" ENV ADMIN="jeff" RUN apk update && apk upgrade && apk add bash COPY . ./app ADD https://raw.githubusercontent.com/discdiver/pachy-vid/master/sample_vids/vid1.mp4 \ /my_app_directory RUN ["mkdir", "/a_directory"] CMD ["python", "./my_script.py"]
Но что же это всё обозначает?
В роли базового образа выступает официальный Python-образ с тегом 3.7.2-alpine3.8. Как вы можете увидеть из исходников, образ включает в себя Linux, Python и ничего более. Alpine-образы очень популярны, потому что они маленькие, быстрые и безопасные. Однако Alpine-образы не поставляются сразу со всеми компонентами, характерными для вашей ОС. Некоторые пакеты вам придётся установить самостоятельно.
LABEL
Следующая инструкция - LABEL. LABEL добавляет метаданные к образу, предоставляет контактную информацию. Она не замедляет процесс запуска и не занимает много места, наоборот, обеспечивает образ полезной информацией, так что обязательно используйте её.
ENV
ENV создаёт переменную окружения, которая становится доступной во время запуска контейнера. В примере выше вы могли видеть использование переменной ADMIN при создании контейнера. ENV удобна для обозначения констант. Если константа используется в нескольких местах файла Dockerfile, и вам понадобится изменить её значение позднее, это можно будет сделать в одном месте.
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.
Готовы к большему?
В следующем примере представлены ещё несколько Docker-инструкций:
FROM python:3.7.2-alpine3.8 LABEL maintainer="jeffmshale@gmail.com" # Install dependencies RUN apk add --update git # Set current working directory WORKDIR /usr/src/my_app_directory # Copy code from your local context to the image working directory COPY . . # Set default value for a variable ARG my_var=my_default_value # Set code to run at container run time ENTRYPOINT ["python", "./app/my_script.py", "my_var"] # Expose our port to the world EXPOSE 8000 # Create a volume for data storage VOLUME /my_volume
В 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.
ARG
Определяет переменную для передачи из командной строки в образ. Для ARG можно указать значение по умолчанию: ARG my_var=my_default_value. В отличие от ENV-переменных, ARG-переменные не доступны для запущенных контейнеров. Однако вы можете использовать их для установки дефолтных значений для ENV-переменных, когда вы создаёте образ. И затем ENV-переменные сохраняются.
ENTRYPOINT
ENTRYPOINT тоже позволяет вам задавать дефолтные команды и аргументы во время запуска контейнера. Она похожа на CMD, но параметры ENTRYPOINT не переопределяются, если контейнер запущен с параметрами командной строки. Docker-файл обязательно должен содержать либо CMD-инструкцию, либо ENTRYPOINT-инструкцию.
EXPOSE
Инструкция EXPOSE показывает, какой порт пробрасывать из контейнера.
VOLUME
VOLUME определяет, где контейнер будет хранить постоянные данные и получать к ним доступ.