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-инструкций

  • FROM - задаёт родительский (главный) образ;
  • LABEL - добавляет метаданные для образа. Хорошее место для размещения информации об авторе;
  • ENV - создаёт переменную окружения;
  • RUN - запускает команды, создаёт слой образа. Используется для установки пакетов и библиотек внутри контейнера;
  • COPY  - копирует файлы и директории в контейнер;
  • ADD  - делает всё то же, что и инструкция COPY. Но ещё может распаковывать локальные .tar файлы;
  • CMD - указывает команду и аргументы для выполнения внутри контейнера. Параметры могут быть переопределены. Использоваться может только одна инструкция CMD;
  • WORKDIR - устанавливает рабочую директорию для инструкции CMD и ENTRYPOINT;
  • ARG - определяет переменную для передачи Docker'у во время сборки;
  • ENTRYPOINT - предоставляет команды и аргументы для выполняющегося контейнера. Суть его несколько отличается от CMD, о чём мы поговорим ниже;
  • EXPOSE - открывает порт;
  • VOLUME - создаёт точку монтирования для работы с постоянным хранилищем.

Инструкции и примеры к ним

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