Конспект цикла статей про Docker

Содержание

Терминология

Ниже перечислены термины, связанные с технологией Docker (само слово Docker в определениях терминов опущено):

  • Платформа - совокупность программ, позволяющая работать с контейнерами.
  • Движок - клиент-серверное приложение.
  • Клиент - часть движка, утилита командной строки, взаимодействующая с демоном для управления образами, контейнерами, сетями и томами.
  • Демон - часть движка, сервер, обслуживающий запросы от клиента и управляющий образами, контейнерами, сетями и томами.
  • Том - средство хранения данных приложений.
  • Реестр - удалённое хранилище образов.
  • Хаб - hub.docker.com, самый крупный реестр с образами, используемый платформой по умолчанию.
  • Репозиторий - совокупность образов с одинаковыми именами и разными тегами.
  • Образ - результат сборки из Dockerfile.
  • Контейнер - выполняющийся образ.
  • Сеть - среда, позволяющая организовать связь между контейнерами.
  • compose - инструмент для упрощения развёртывания приложений, состоящих из нескольких контейнеров.
  • swarm - средство для управления развёртыванием контейнеров.
  • Сервисы - конкретная совокупность контейнеров, на которых работает приложение.

Кроме swarm существует более популярное средство управления развёртыванием контейнеров - это Kubernetes.

Файл Dockerfile

Файл содержит инструкции по сборке образа.

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

Файл должен начинаться с инструкции FROM или с инструкции ARG, за которой следует инструкция FROM. Рассмотрим пример Dockerfile:

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"]

Инструкция FROM указывает образ, на базе которого будет собираться текущий образ.

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

Инструкци ENV задаёт переменную окружения, которая будет доступна для использования внутри работающего контейнера.

Инструкция COPY предписывает взять файл из среды, где была запущена команда сборки, и поместить его внутрь образа. Если в образе нет указанного каталога, он будет создан.

Инструкция ADD делает то же самое, что и инструкция COPY, но умеет брать исходный файл из удалённого источника или из архива tar. Рекомендуется отдавать предпочтение инструкции COPY.

Инструкции RUN, CMD и ENTRYPOINT выполняют команды, но используются для разных целей. Эти инструкции принимают две формы аргументов: shell-форму и exec-форму.

Пример shell-формы можно увидеть в первой команде RUN из приведённого выше файла Dockerfile:

RUN apk update && apk upgrade && apk add bash

Пример exec-формы можн увидеть в следующей инструкции RUN и в инструкции CMD в приведённом выше файле Dockerfile:

RUN ["mkdir", "/a_directory"]
CMD ["python", "./my_script.py"]

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

Образ состоит из нескольких слоёв. Каждый из слоёв, кроме текущего, не подлежит изменению. Если необходимо изменить в нижележащем слое какой-либо файл, то его новая версия добавляется в текущий слой.

Инструкция RUN создаёт новый слой, в котором выполняются указанные в ней команды.

Инструкция CMD указывает команду, которую нужно выполнить при запуске контейнера. Её результаты не добавляются в образ при сборке. В одном файле Dockerfile может быть только одна инструкция CMD, в противном случае будет использоваться последняя. Переопределить аргументы выполняемой команды можно, указав их после команды docker run. Саму выполняемую команду и начальную часть аргументов можно отделить в инструкцию ENTRYPOINT, в таком случае их нельзя будет переопределить с помощью аргументов команды docker run.

Рассмотрим ещё один пример Dockerfile:

FROM python:3.7.2-alpine3.8
LABEL maintainer="jeffmshale@gmail.com"
# Устанавливаем зависимости
RUN apk add --update git
# Задаём текущую рабочую директорию
WORKDIR /usr/src/my_app_directory
# Копируем код из локального контекста в рабочую директорию образа
COPY . .
# Задаём значение по умолчанию для переменной
ARG my_var=my_default_value
# Настраиваем команду, которая должна быть запущена в контейнере во время его выполнения
ENTRYPOINT ["python", "./app/my_script.py", "my_var"]
# Открываем порты
EXPOSE 8000
# Создаём том для хранения данных
VOLUME /my_volume

Инструкция WORKDIR позволяет изменить текущий каталог образа, с которым работают инструкции COPY, ADD, RUN, CMD и ENTRIYPOINT. Эта инструкция автоматически создаст указанный каталог, если она не существует.

Инструкция ARG позволяет создать переменные окружения, которые будут доступны в образе в процессе сборки образа, но не будут доступны после запуска образа. значнения этих переменных можно переопределять из командной строки во время сборки образа. Для того, чтобы значения переменных, указанных в командной строке при сборке образа, не пропали после запуска контейнера, можно использовать значения перменных, определённых в инструкциях ARG, в инструкциях ENV.

С помощью инструкции EXPOSE можно указать порты, которые работающее приложение откроет на прослушивание внутри контейнера. Для того, чтобы порты контейнера были открыты для клиентов за пределами контейнера, нужно дообавить к команде запуска docker run опцию -p, а если порт уже был указан в инструкции EXPOSE, то все объявленные порты можно открыть указанием опции -P.

С помощью инструкции VOLUME можно указать место для постоянного хранения файлов.

Остались нерассмотренными команды USER, ONBUILD, STOPSIGNAL, SHELL и HEALTHCHECK.

Новые слои образа создаются только инструкциями FROM, COPY, ADD и RUN. Результат сборки каждого слоя помещается в кэш. Если команда сборки не изменилась, используется уже собранный слой из кэша. При выполнении инструкций COPY и ADD вычисляется также хэш-сумма файла, подлежащего добавлению в слой. Если файл не изменился, используется кэшированный слой.

Для принудительной сборки образа без использования слоёв из кэша можно указать команде docker build дополнительную опцию - --no-cache=True.

Если определённый слой изменился, то кроме него подлежат повтороной сборке и последующие слои. Поэтому нужно стараться компоновать файл Dockerfile таким образом, чтобы в начале файла располагались команды, которые будут меняться с наименьшей вероятностью. Старайтесь объединять последовательности команд RUN в одну команду, чтобы уменьшить количество слоёв.

Чтобы в инструкциях типа RUN pip install -r requirements.txt не использовался слой из кэша, можно прибегнуть к приведённому ниже трюку:

COPY requirements.txt /tmp/
RUN pip install -r /tmp/requirements.txt
COPY . /tmp/

Другой вариант:

RUN --mount=type=bind,source=requirements.txt,target=/tmp/requirements.txt \
pip install --requirement /tmp/requirements.txt

Многоступенчатая сборка образов

При многоступенчатой сборке образов в файле Dockerfile имеется несколько инструкций FROM. Файлы, полученные в процессе сборки одного образа, можно перенести в следующий, отбросив при этом лишние файлы, образовавшиеся в процессе сборки предыдущего образа.

Рассмотрим пример такого файла Dockerfile:

FROM golang:1.7.3 AS build
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=build /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]

В первой инструкции FROM указно имя первого этапа сборки - build:

FROM golang:1.7.3 AS build

В команде COPY после второй инструкции FROM в качестве источника, откуда надо взять файл, указан этап сборки с именем build:

COPY --from=build /go/src/github.com/alexellis/href-counter/app .

Файл .dockerignore

При выполнении команды docker build ищет в одном каталоге с файлом Dockerfile файл .dockeringore. В этом файле можно перечислить файлы и каталоги, которые не попадут в образ в процессе его сборки.

Пример файла .dockerignore:

# Игнорировать каталоги .git и .cache
.git
.cache

# Игнорировать все файлы, которые имеют расширение .class во всех каталогах, включая корневой
**/*.class

# Игнорировать все файлы в формате markdown, оставить только файлы README*.md, но README-secret.md тоже проигнорировать
*.md
!README*.md
README-secret.md

Размеры образов

Размеры образов можно узнать с помощью следующих команд:

  • docker container ls -s - выводит размеры выполняющихся контейнеров,
  • docker image ls - выводит размеры образов,
  • docker image history <образ>:<метка> - выводит размеры слоёв, из которых состоит образ,

Команды

Команды для управления контейнерами начинаются с имени клиента docker, вслед за ним указываются область действия и команда. Областью действия могут быть образы image, контейнеры container и т.п.

Например, для управления контейнерами предусмотрены следующие команды:

  • docker container create - создание контейнера из образа,
  • docker container start - запуск существующего контейнера,
  • docker container run - создание контейнера и его запуск,
    • Опция -i или --interactive связывает контейнер со стандартными потоками ввода и вывода,
    • Опция -t или --tty связывает стандартные потоки ввода и вывода с псевдотерминалом,
    • Опция -p или --port позволяет отображать порт контейнера в порт хост машины, сначала указывается порт хост-машины, потом через двоеточие - порт контейнера,
    • Опция --rm предписывает автоматически удалить контейнер после завершения его работы,
    • Опция -d или --detach предписывает запустить контейнер в фоновом режиме,
    • Опция --network host предписывает использовать внутри контейнера сетевой стек хост-машины,
    • Опция -v или --volume предписывает связать каталог хоста и каталог контейнера, разделённые двоеточием,
    • Опция --mount предписывает смонтировать в контейнер указанный том, полное описание см. в разделе Работа с томами,
  • docker container ls - вывод списка работающих контейнеров,
    • Опция -a или --all позволяет вывести список всех контейнеров, а не только выполняющихся,
    • Опция -s или --size предписывает также показать размеры контейнеров,
  • docker container inspect - вывод подробной информации о контейнере,
  • docker container logs - вывод логов,
  • docker container stop - остановка работающего контейнера с отправкой главному процессу контейнера сигнала SIGTERM, и, через некоторое время, SIGKILL,
  • docker container kill - остановка работающего контейнера с отправкой главному процессу контейнера сигнала SIGKILL,
  • docker container rm - удаление остановленного контейнера.

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

  • docker image build - сборка образа,
    • docker image build -t <репозиторий>/<образ>:<метка> <каталог> - опция -t или --tag позволяет указать метку создаваемого образа, а <каталог> должен содержать файл Dockerfile,
  • docker image push - отправка образа <репозиторий>/<образ>:<метка> в удалённый реестр,
  • docker image ls - вывод списка образов с их размерами,
  • docker image history - вывод сведений о слоях образа,
  • docker image inspect - вывод подробной информации об образе, в том числе - сведений о слоях,
  • docker image rm - удаление образа.

Примеры других команд:

  • docker version — вывод сведений о версиях клиента и сервера Docker,
  • docker login — вход в реестр Docker,
  • docker system prune — удаление неиспользуемых контейнеров, сетей и образов, которым не назначено имя и тег.

Работа с томами

По умолчанию в контейнере можно сохранять данные, которые пропадут вместе с контейнером.

Для ускорения работы контейнера можно подключить в контейнер файловую систему tmpfs, находящуюся в оперативной памяти.

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

Хранить данные вне контейнера можно также с помощью механизма томов. В Dockerfile том можно описать командой VOLUME /<том>, однако при запуске контейнера дополнительно нужно будет указать точку монтирования тома.

Команды для работы с томами:

  • docker volume create - создать новый том,
    • Опция --name <том> позволяет указать имя создаваемого тома,
  • docekr volume ls - выводит список имеющихся томов,
  • docker volume inspect <том> - исследовать свойства указанного тома,
  • docker volume rm <том> - удалить указанный том,
  • docker volume prune - удалить все тома, не используемые контейнерами. Если контейнера нет, но том всё равно не удаётся удалить, моожно воспользоваться командой docker system prune, после чего вновь попробовать удалить том.

Для монтирования томов исторически первыми появились опции -v и --volume, которы принимают разделённые двоеточием полный путь к каталогу хост-машины и польный путь к каталогу внутри контейнера.

Позже появилась опция --mount, которая принимает разделённые запятыми пары вида ключ=значение. Предусмотрены следующие ключи:

  • type - указвыает один из типов монтирования: bind, volume или tmpfs,
  • source или src - указывает имя тома,
  • destination, dst или target - указывает на точку монитрования тома внутри контейнера,
  • readonly - ключ без значения, указывает на необходимость смонтировать том в режиме "только чтение".

Мотивация

eggstream 13 фев 2019 в 20:26

Для чего мне все это нужно?

  1. воспроизводимость окружения, чтобы не случилось так, что разработчик говорит, что у него всё работает, а при разворачивании на сервере всё ломается из-за того, что разработчик забыл описать в требованиях какую-то зависимость.
  2. изолированость окружения, чтобы не случилось так, что для работы одному проекту нужна версия 1 какого-то инструмента, а другому проекту — версия 2, а одновременно на одну машину эти версии не становятся.
  3. поддержка чистоты на хост-машине, чтобы не случилось так, что для проекта нужно установить с десяток зависимостей, которые потом нужно долго и нудно выковыривать из системы, контейнер удаляется в одну команду, не зависимо от того, насколько глубоко пакеты в нём интегрированы в систему
  4. замена пакетному менеджеру(?), чтобы не случилось так, что для вашего дистрибутива или для вашего пакетного менеджера нет готовых бинарников нужного пакета, можно просто поднять его в докере и не морочить себе голову с курением мануалов (на самом деле этот пункт — лишь забавный побочный эффект от других преимуществ докера)
  5. простота разворачивания, чтобы можно было проще настраивать инструменты непрерывного развертывания

vabxant 18 фев 2019 в 00:26

Во всю эту красотишшу надо добавить пару камушков, ну чтобы юные падаваны не расшибли себе лоб. 1. Докер практически исключительно для кода, не для данных. Данные надо держать где-то снаружи, и потом пробрасывать внутрь контейнеров папки/диски/луны/чотамувас для тех данных, которые должны пережить рестарт контейнера. Не, ну если надо на локалхосте поставить какого-нибудь монстра, два часа потыкать в него и снести — можно и внутри данные хранить. В тестовых контейнерах опять же можно. Но на проде так делать не надо. 2. Вводя докер, вы переходите на коммуникации через tcp/ip. Монолитный nginx + php-fpm + mysql на сокетах может работать раза в два быстрее, чем то же самое, но в отдельных контейнерах по локальному tcp/ip. Потому что сокет он в разделяемой общей памяти и без копирования, а по сети данные минимум трижды копируются при упаковке/распаковке пакетов. 3. Вводя докер, вы вводите серьёзные зависимости непонятно от кого. Ну, точнее, следите, чьи именно репы вы используете. Они могут что-нибудь сломать в очередном апдейте (бывало и много раз), или вообще запихать какую-нибудь гадость (не слышал, но...).

Использованные материалы

Дополнительные материалы