131 подписчик

Большая экскурсия в мир Docker

Всем доброго времени суток!

У клавиатуры Павел Злой и в этой дебютной статье я бы хотел обобщить свой многолетний опыт работы с Docker, но сделать так чтобы это было в максимально понятном и доступном для начинающих специалистов формате, впереди вас ожидает много текста :)

Введение

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

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

Если вдруг что-то не очень понятно, то не стесняйтесь задавать вопросы в комментариях, по мере сил постараюсь отвечать :)

Краткая историческая справка

В далёком 2010 году, в инкубаторе стартатов Y Combinator участвовала команда состоящая из Kamel Founadi, Solomon Hykes и Sebastien Pahl. Коллектив получил финансирование и благодаря этому в 2011 году была основана компания Docker Inc.

Причина их успеха была в инновационной платформе контейнеризации приложений, выполняемых в изолированных окружениях на базе подсистемы LXC названная Docker, а так же инструментарий для компоновки Docker-контейнеров при помощи Dockefile (докерфайлов).

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

В марте 2013 года на конференции PyCon система Docker дебютировала как публичный проект (то есть исходные коды были опубликованы на GitHub).

В течении последующего года Docker продолжал использовать подсистему LXC для управления контейнерами, но начиная с версии 0.9 был переведён на свою собственную библиотеку под названием libcontainer, которая была написана на языке Go.

В июне 2015 года компания Docker анонсировала начало работ над новым, платформонезависимым стандартом для запуска любых контейнеров и некоторое время спустя, в марте 2018, проект Docker был переведён на версию libcontainer полностью совместимую со стандартом containerd.io

Контейнеризация != Виртуализация

Первое, что должен запомнить любой специалист, начинающий своё знакомство с миром DevOps: Docker - это не виртуальная машина и даже не система управления виртуальными машинами, подсистема Docker выполняет код собранный в контейнеры напрямую обращаясь к оборудованию, как если бы исполняемое приложение было запущено напрямую в операционной системе (ОС) штатными средствами. Иными словами производительность приложений запущенных в Docker контейнерах будет выше чем у виртуальной машины и немного хуже, чем у приложений запущенных на железе (но это в основном касается вопросов межсетевого взаимодействия).

Подробности можно найти в исследовательском отчёте "An Updated Performance Comparison of Virtual Machines and Linux Containers" за авторством команды инженеров IBM, в данном исследовании производится сравнение производительности приложений запущенных в Docker контейнере, в KVM и напрямую на железе.

Родная среда для запуска приложений собранных в Docker-контейнеры это Linux, потому что ядро Linux обладает рядом уникальных особенностей, недоступных в других ОС. Соответственно и запуск контейнеров предполагается выполнять на Linux.

Мнение о том, что Docker это некая система виртуализации - ошибочное, но появилось оно по вполне понятным причинам. Дело в том, что на Windows и MacOS иного способа запустить Linux приложения попросту нет, именно поэтому разработчики Docker специально для данных ОС и придумали костыль^W решение с применением подсистем виртуализации. Поскольку Docker-контейнеры должны запускаться в Linux стоит понимать, что виртуальная машина запускает Linux для обслуживания приложений в Docker-контейнерах, иными словами, лишний, но с учётом ограничений, необходимый оверинжиниринг.

Кстати, аналогичная ситуация и с FreeBSD, там тоже требуется прослойка в виде виртуальной машины с Linux на борту.

Сами понимаете, производительность Docker-контейнеров запущенных в виртуальных машинах будет на самой лучшей, в отличии от Linux.

Что такого особенного умеет Linux?

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

Источник Wikipedia / Docker (Software)
Источник Wikipedia / Docker (Software)

namespaces (пространства имён)

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

  • идентификатор процессов (PID, process identifier) – изоляция идентификаторов процессов и информации о них;
  • сеть (NET, network) – изоляция сетевых интерфейсов, возможность отправки и получения сетевых запросов;
  • файловая система (MNT, mount) – изоляция файловых систем и точек монтирования;
  • хост (UTS, UNIX Time-Sharing) – изолирует хостнейм;
  • межпроцессное взаимодействие (IPC, Inter-process communication) – изоляция утилит межпроцессного взаимодействия (сегменты разделяемой памяти, семафоры);
  • пользовательские идентификаторы (USR, user) – изоляция ID пользователей и групп, необходима для контроля уровня доступа к файлам внутри контейнера и на подключенных томах;

(больше подробностей тут: Углубленное знакомство с пространствами имен Linux. Часть 1 на Habr)

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

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

cgroups (контрольные группы)

Представляют собой функцию ядра Linux, которая позволяет организовывать процессы в иерархические группы, использование которых для различных типов ресурсов может быть затем ограничено и отслежено. Интерфейс cgroup ядра предоставляется через псевдофайловую систему cgroupfs.

Cgroup реализована в основном коде ядра Linux, а отслеживание ресурсов и ограничения реализованы в наборе подсистем для каждого типа ресурса (память, процессор и т. д.).

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

time (время)

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

Если в сети используется множество хостов с Docker-сервером на борту собранные в кластер, то нужно обязательно настраивать на них NTP-клиенты, как раз для того чтобы не было рассогласования.

AppArmor, SELinux и т.д.

Подсистемы для обеспечения контроля безопасности запущенных приложений уровня ОС, часто применяется в связке с Docker Engine на проектах в которых требуется высокий уровень безопасности (о чём автор публикации "Исповедь docker хейтера" на Habr явно был не в курсе).

Базовая терминология

Теперь давайте поговорим про используемую терминологию.

Docker Engine

Движок Docker (команда dockerd) - это клиент-серверное приложение, взаимодействие с которым происходит через сокет интерфейс /var/run/docker.sock. Любопытной особенностью движка является возможность открыть доступ к сокету через сеть, что позволяет удалённо управлять контейнерами.

Ещё одной немаловажной особенностью является поддержка расширений для прямого доступа к графическим ускорителям Nvidia runtime, благодаря (сошедшему в наши дни на нет) майнинговому буму этот рантайм был существенно доработан и в настоящий момент представляет из себя удобнейшую среду для разработки нейросетей обучаемых/исполняемых на GPU.

Стоит заметить, что существует две версии Docker Engine:

  • Community Edition (CE)
  • Enterprise Edition (EE)

Отличаются они тем, что первая - полностью OpenSource с урезанным функционалом безопасности, а вторая - закрытая проприетарная версия с упором на безопасность. Однако, для решения большинства задач достаточно и CE версии.

Docker CLI

Приложение командной строки (команда docker) для взаимодействия с Docker сервером через сокет. По большому счёту данное приложение просто обёртка вокруг API, при помощи разнообразных команд и опций она позволяет управлять контейнерами.

Docker Compose

Интерфейс Docker CLI неподготовленным специалистам может показаться крайне сложным и неудобным. Чтобы решить эту проблему разработчиками Docker был предоставлен инструмент Compose (команда docker-compose или docker compose в более новых версиях Docker Engine). По сути своей это простая обёртка над Docker CLI, но она позволяет значительно упростить процесс описания настроек контейнера.

Описание конфигурации происходит при помощи файлов docker-compose.yml, в них указывается версия docker-compose, а так же как минимум один сервис (то есть Docker контейнер), у сервисов описываются какие порты куда пробрасывать, какие тома необходимо подмонтировать, переменные окружения, директории в которых находятся Dockerfile для сборки, сетевые настройки, логирование и так далее, в общем всё что можно передать через опции и аргументы Docker CLI, но в более удобном формате.

Docker Image

Образ контейнера - архив содержащий в себе исполняемое приложение, а так же все необходимые файлы для работы этого приложения. Образ может состоять из нескольких архивов, называемых слоями (overlays или оверлеи), каждый слой имеет уникальной sha-хеш в качестве названия.

Образы помечаются тегами, данные теги описывают название, которое в свою очередь можно указать на этапе сборки других, дочерних контейнеров.

Обычно название тега состоит из двух частей, разделённых двоеточием, например ubuntu:22.04, если после двоеточия ничего не указывать (включая и само двоеточие), то Docker подставит суффикс :latest

Docker Container

Принципиальным отличием контейнеров от образов является тот факт, что в момент запуска происходит распаковка архивов-слоёв образа в индивидуальные директории (производится единожды, после чего используется уже распакованные директории), затем файловая система overlay2 создает иллюзию слияния содержимого этих директорий воедино, без изменения исходных (физических) данных в оригинальных директориях.

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

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

Docker Registry

Реестр или просто хранилище Docker образов, каждый пользователь может при желании развернуть свой локальный реестр образов, либо воспользоваться такими площадками как Docker Hub.

Docker Hub

Это официальный Registry от разработчиков Docker, до недавнего времени была центром поддержки OpenSource, но с 2020 года сильно коммерциализировалась. Иными словами OpenSource активисты (навроде автора данной публикации) сделали данной площадке имя и обеспечили популярность, а когда хозяева решили, что пора "стричь бабосы" и OpenSource проекты были отодвинуты на второй план.

Docker Networks

Виртуальные сетевые интерфейсы подключаемые в Docker контейнеры в момент старта. По сути своей эти интерфейсы представляют из себя сетевые мосты (network bridges) через которые контейнеры общаются с внешним миром. Каждый сетевой интерфейс имеет свой уникальный (в пределах хостовой машины) IP-адрес. Обычно в Docker контейнерах имеется только один сетевой интерфейс, но при большом желании и капельке терпении данное ограничение возможно обойти.

Docker Volumes

Тома - это предпочтительный механизм для сохранения данных, созданных и используемых контейнерами Docker. В то время как монтирование при помощи биндинга (mount -o bind) зависит от структуры каталогов и ОС хост-компьютера, тома полностью управляются Docker.

Docker Swarm

Система оркестрации Docker контейнеров, ближайший аналог Kubenetes'а, но в отличии от последнего Swarm имеет урезанный функционал и очень медленно развивается. Про данную подсистему поговорим как-нибудь в другой раз, просто знайте что она есть.

Docker Desktop

Графический интерфейс для управления Docker образами, контейнерами, томами и так далее. Первый релиз данного приложения состоялся в декабре 2016 года.

С точки зрения автора данной публикации приложение Docker Desktop было создано только потому, что пользователи Windows и MacOS в массе своей не умеют и/или не хотят пользоваться командной строкой и просто не смогли бы оценить всю мощь которую представляет из себя Docker. Иными словами настольное графическое приложение было создано для популяризации продукта.

В июне 2022 года была выпущена версия 4.8.0, в которой, спустя шесть лет, разработчики Docker Desktop вспомнили и про пользователей ОС на базе ядра Linux.

Установка Docker Engine, CLI и Compose

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

Обновим список пакетов и установим все необходимые вспомогательные утилиты:

sudo apt-get update

sudo apt-get install ca-certificates curl gnupg lsb-release

Далее скачем GPG-ключи необходимые для работы с репозиторием Docker:

sudo mkdir -p /etc/apt/keyrings

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

Добавим в /etc/source.list.d конфигурацию для доступа в официальный репозиторий Docker:

echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Обновим список пакетов повторно и установим Docker Engine:

sudo apt-get update

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

Так же возможно поставить всё необходимое просто установив пакет docker-desktop.

Добавим текущего пользователя в группу docker:

sudo adduser $USER docker

После этого необходимо перезапустить графический интерфейс (или просто перезагрузиться), главное чтобы выдача команды id показывала, что текущий пользователь находится в группе docker.

Вывод команды id должен содержать группу docker
Вывод команды id должен содержать группу docker

Проверим, что всё работает исправно, для этого выполним команду docker run hello-world

Примерно так выглядит результат работы контейнера hello-world
Примерно так выглядит результат работы контейнера hello-world

Похоже всё работает и можно двигаться дальше.

Dockerfile

С общими вопросами разобрались, поэтому теперь приступим к самому важному, к процессу описания своих приложений запакованных в образы.

Для того чтобы создать свой Docker Image используется специальный файл, называемый Dockerfile. Он необходим для описания образа Docker, используется на этапе сборки приложения в образ, содержит в себе команды которые необходимо выполнить для того чтобы получить готовый образ.

Директивы Dockerfile

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

Вот список всех доступных директив и их краткое описание:

  • FROM - каждый образ должен быть основан на каком-то другом базовом образе, обычно в этом поле указывается название образа операционной, например debian, ubuntu, centos, alpine и так далее;
  • ENV - параметры окружения доступны как в момент сборки, так и после запуска контейнера;
  • ARG - аргументы сборки, передаются через CLI, опцией ---build-arg, работают так же как и переменные описанные в ENV, но доступны только на этапе сборки;
  • WORKDIR - указывает рабочую директорию, если указанная директория не существует, то она будет создана, после чего контекст будет переключен на неё;
  • COPY - на этапе сборки копирует файлы из папки в которой находится Dockerfile в корень контейнера;
  • ADD - делает тоже самое, что и COPY, но при этом поддерживает URL, а так же если в качестве аргумента указан архив, то производит его распаковку. Ещё одной возможность данной директории является функция клонирования Git репозитория ADD https://repo/project.git <folder>;
  • RUN - описывает команду которую необходимо выполнить на этапе сборки;
  • ENTRYPOINT - точка входа, директива которая описывает приложение которое необходимо запустить в контейнере;
  • CMD - тоже что и ENTRYPOINT, но с той лишь разницей, что идет после неё, возможно использовать комбинацию из этих двух директив для того чтобы добиться наиболее приемлемого результата. Контейнер же запускается при помощи ENRTYPOINT + ' ' + CMD, то есть директивой CMD предполагается передавать опции и аргументы для команды описанной в ENTRYPOINT;
  • USER - директива переключает пользовательский контекста из под которого выполняется дальнейшая работа, часто данную директиву применяют в контейнерах, приложения которых не должно запускаться из под пользователя root (контейнеры запускаются из под него, если не указано иное);
  • EXPOSE - в данной директиве перечисляется список портов которые предоставляет приложение, нужна скорее для удобства;
  • LABEL - используется для организации и быстрого поиска Docker образов в Registry;
  • STOPSIGNAL - сигнал операционной системы который необходимо отправить запущенному приложению для того чтобы корректно выполнить его остановку;
  • VOLUME - том который необходимо подключить к контейнеру в момент запуска, обычно в таких томах содержатся файлы настроек приложений, либо временны кеш;
  • ONBUILD - специальная директива, используется при создании базовых образов, описывает действия, которые необходимо выполнить первым делом на этапе сборки всех дочерних образов основанных на текущем (больше подробностей тут). Например вы хотите чтобы все дочерние образы первым делом обновляли список пакетов и устанавливали обновления;
  • HEALTHCHECK - описывает команду, которую необходимо выполнить для того чтобы понять не завис ли контейнер и находится ли он в рабочем состоянии. В случае если хелсчек выдаёт ошибку контейнер останавливается (либо выполняет перезапуск, если это явно указано в опциях CLI);
  • SHELL - какой интерпретатор использовать в процессе выполнения процедуры сборки, по умолчанию используется /bin/sh который как правило является символической ссылкой на интерпретатор по умолчанию, который зависит от выбранной базовой ОС. Кстати, вы можете указать любой исполняемый файл в качестве оболочки, даже если это файл интерпретатора PHP или JavaScript;

Директивы FROM, COPY/ADD, RUN и ENTRYPOINT скорее всего будут чаще всего попадаться вам на глаза в процессе изучения существующих Dockerfile.

Пишем Hello world

Для ознакомления попробуем создать простейший Dockerfile, который описывает образ выводящий сообщение hello world:

Содержимое файла Dockerfile
Содержимое файла Dockerfile

Далее произведём его сборку командой docker build . --tag=test

Результат работы команды docker build
Результат работы команды docker build

В логе сборщика видно, что был собран образ состоящий из двух слоёв, первый слой был основан на образе alpine:latest (точнее была создана ссылка на него), а во второй слой попала директива ENTRYPOINT, получившийся образ был помечен тегом test:latest

Запустим получившийся образ командой docker run test

Результат работы контейнера
Результат работы контейнера

Пример простейшего JavaScript приложения

Hello world это конечно хорошо, но что делать если необходимо собрать, ну скажем приложение на JavaScript в контейнер?

Дано: простенький скрипт, который выполняет операции вычисления последовательности Фибоначчи с последующим выведением результата на экан. На вход скрипту нужно передать число указывающее какое количество значений необходимо рассчитать и отобразить.

Создадим Dockerfile

Содержимое Dockerfile для проекта fibo.js
Содержимое Dockerfile для проекта fibo.js

Выполняем сборку командой docker build . --tag=fibo

Лог сборки контейнера
Лог сборки контейнера

Тут видно, что образ состоит из четырёх слоёв, сначала ссылаемся на базовый образ node:18.8.0-alpine3.16, потом меняем рабочую директорию на /app, после чего копируем в образ файл скрипта fibo.js и указываем его в качестве ENTRYPOINT.

Запускаем полученный образ docker run --rm fibo 30

Неправильный результат
Неправильный результат

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

Слегка исправленный Dockerfile
Слегка исправленный Dockerfile

Пересоберём образ и запустим его ещё раз.

Правильный результат работы скрипта
Правильный результат работы скрипта

Вот, теперь совсем другое дело!

Подытожим про Dockerfile

Если в двух словах описать процесс написания Dockerfile то всё сводится к тому, что нужно взять некий базовый образ, скопировать файлы, прописать переменные окружения, выполнить все необходимые шаги связанные с установкой зависимостей, сборкой и так далее при помощи RUN, затем выполнить чистку и прописать ENTRYPOINT и/или CMD (используемые директивы и последовательность зависят от собираемого в контейнер приложения).

Просто представляйте, что пишете скрипт автоматизации как если бы работа выполнялась на какой-нибудь чистой Linux системе.

Больше примеров Dockerfile созданных автором данной публикации смотрите тут, тут и тут (на самом деле я написал десятки, если даже не сотни Dockerfile, приводить ссылки на все не вижу смысла, поэтому добавил только на самые интересные), официальная документация проекта Docker с примерами тут.

Резюмируя тему Dockerfile могу сказать, что писать их вам нужно будет не так уж и часто, но понимать как их писать нужно обязательно!

В большинстве случаев придётся использовать уже собранные образы и собирать их, словно элементы пазла или конструктор Lego, в композиции при помощи Docker Compose, либо сразу в поды Kubernetes.

Про Docker Compose

Как уже было сказано ранее Docker Compose по сути своей это просто обёртка над Docker CLI, то есть всё что можно сделать через аргументы и опции CLI возможно описать при помощи конфигурационного файла docker-compose.yml после чего запустить утилитой docker-compose.

Существует два вариант установки Compose:

  1. В качестве расширения CLI, тогда команды Compose необходимо будет выполнять запуском docker compose;
  2. В качестве standalone приложения собранного в один небольшой файл.

Если вы устанавливали Docker Engine по инструкции описанной выше, то Compose у вас уже установлен.

Далее описывается процедура установки Compose в standalone режиме.

Скачаем файл docker-compose, положим его в директорию /usr/local/bin:

sudo curl -SL https://github.com/docker/compose/releases/download/v2.10.2/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose

Сделаем файл исполняемым:

sudo chmod +x /usr/local/bin/docker-compose

Проверим, что всё работает как надо:

Версия docker-compose
Версия docker-compose

Создание типового docker-compose.yml

Для того чтобы Compose понимал, что от него хотят необходимо описать файл docker-compose.yml, данный файл будет содержать в себе все необходимые настройки связанные с запускаемым приложением.

В качестве примера рассмотрим процесс создания композиции Nextcloud, состоящей из непосредственно сервера Nextcloud и базы данных MariaDB (это форк MySQL появившийся во времена когда Oracle купили Sun).

Кстати, про запуск Nextcloud в Docker я делал отдельный видеоролик на YouTube (не забудьте лайкнуть).

Создадим пустой файл docker-compose.yml в любой директории, например в docker-nextcloud:

mkdir docker-nextcloud && cd docker-nextcloud && touch docker-compose.yml

version

Первым делом пишем версию docker-compose, полный список доступных версий можно найти тут. Сразу замечу, что версия 2.4 последняя в которой разрешено использовать настройки связанные с ограничением объёма используемой памяти и процессорных ресурсов.

Но мы выберем последнюю на данный момент версию, а именно 3.8

Укажем версию 3.8
Укажем версию 3.8

services

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

services.nextcloud

Первым сервисом добавим Nextcloud:

Конфигурация сервиса Nextcloud
Конфигурация сервиса Nextcloud

Начнём с описания настроек сверху вниз.

В данном примере nextcloud это название сервиса, примечательна особенность заключается в том, что по этому названию можно будет обратиться из лобого другого сервиса.

services.nextcloud.restart

Далее идёт строка restart: unless-stopped, она означает что в случае падения контейнера Docker Engine будет пробовать перезапустить его, и будет он это делать до тех пор пока пользователь принудительно не остановит указанный контейнер.

Если не указывать параметр restart то будет выбран режим работы no, то есть контейнер не будет перезапущен в случае падения (то есть будет работать до первого падения).

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

services.nextcloud.image

Название используемого образа, а так же версия, разделённые двоеточием. Если не указывать версию то будет выбрана версия :latest.

Поскольку название образа указано без дополнительных префиксов (в смысле написано просто nextcloud) значит Docker Engine будет пытаться найти указанный образ на Docker Hub.

Полный список возможных версий можно найти на странице образа Nextcloud на Docker Hub во вкладке Tags.

services.nextcloud.ports

В данном блоке перечисляется список портов, пробрасываемых из контейнера в хостовую ОС.

Конструкция выглядит немного странно если не понимать её схему:

<ip-адрес_хоста>:<порт_хоста>:<порт_внутри_контейнера>

В нашем случае IP-адрес хоста это 127.0.0.1, то есть localhost, если его не указывать (а так же двоеточие после него), то Compose будет производить проброс указанного порта со всех интерфейсов хоста на указанный порт внутри контейнера.

С портом внутри и снаружи полагаю всё и так понятно.

Проброс портов происходит при помощи правил маскардинга netfilter уровня ядра Linux, вот так будет выглядить проброс 80го порта с localhost на 80й порт внутри контейнера:

-A POSTROUTING -s 172.21.0.2/32 -d 172.21.0.2/32 -p tcp -m tcp --dport 80 -j MASQUERADE

-A DOCKER -d 127.0.0.1/32 ! -i br-ea1b07c0ea67 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.21.0.2:80

services.nextcloud.volumes

В этом блоке перечисляется список монтируемых директорий (монитрование происходит при помощи mount -o bind).

В данном примере предполагается что директория nextcloud_data будет находиться в той же директории в которой находится файл docker-compose.yml, а подключается эта директория в /var/www/html внутри Docker-контейнера.

Подключение директорий возможно в двух режимах:

  • ro - режим монтирования только для чтения, удобно если нужно подключать конфигурационные файлы которые нельзя менять из контейнера;
  • rw - режим монитирования с правами на запись, если ничего не указывать в качестве режима монтирования, то этот режим будет выбран по умолчанию.

services.mariadb

Для работы Nextcloud необходима база данных, существует возможность использовать в качестве базы SQLite, PostgreSQL и MySQL. Последние две значительно быстрее в работе чем первая, так что выберем MySQL (а точнее MariaDB).

Конфигурация сервиса mariadb
Конфигурация сервиса mariadb

Тут видно что в блоке ports на localhost пробрасывается порт 3306, это стандартный порт для работы с MySQL.

service.mariadb.environment

Самый интересный блок это environments, в нём перечислен ряд параметров, которые управляют процессом первичной инициализации контейнера mariadb.

Многие контейнеры имеют подобрые переменные, поэтому читайте README каждого проекта и внимательно изучайте доступные переменные окружения, вполне возможно это сэкономит вам не один час времени.

Запуск композиции и другие команды

Теперь запустим получившуются конфигурацию командой:

docker-compose up -d

Данная команда в момент первого запуска скачает архивы образов (если их нет) с Docker Hub, а так же их распаковку, затем выполнит создание изолированной сети, после чего выполнит запуск и отправит контейнеры в фон.

Процесс загрузки образов
Процесс загрузки образов
Композиция загружена и запущена
Композиция загружена и запущена

В логе видно, что после того как образы были скачаны Compose создал виртуальную сеть docker-nextcloud_default, название это получилось не случайно, оно генерируется по названю директории в которой находится файл docker-compose.yml, но название сети можно легко изменить при помощи блока настроек networks.

Кстати, up -d можно выполнить и для одного или нескольких контейнеров (названия нужно разделять пробелом), например:

docker-compose up -d nextcloud

Просмотр списка запущенных контейнеров

Команда отобразит список контейнеров в текущей композиции, а так же их статус:

docker-compose ps

Статус контейнеров
Статус контейнеров

Просмотр процессов в контейнерах

docker-compose top

Список запущенных процессов
Список запущенных процессов

Просмотр логов работы

Логи из всех контейнеров:

docker-compose logs -f

Только сервиса nextcloud:

docker-compose logs -f netxcloud

Логи контейнера Nextcloud
Логи контейнера Nextcloud

Ключ -f означает follow, то есть docker-compose будет читать все логи не отпуская консоль (ближайший аналог это tail -f), если не указывать данный ключ то Compose вернёт все логи которые у него есть и отпустит консоль.

Обновление образов композиции

docker-compose pull

или только nextcloud:

docker-compose pull nextcloud

Данная команда читает значение из параметра image, после этого Docker выполняет запрос в Registry и ищет новые версии указанного образа, если таковые имеются, то Docker производит их загрузку.

Запустить новую версию контейнера возможно через docker-compose up -d.

Перезапуск композиции

Просто пишете:

docker-compose restart

Если нужно перезапустить только один или несколько контейнер (названия нужно разделять пробелом), например nextcloud то:

docker-compose restart nextcloud

Остановка композиции

docker-compose stop

или только nextcloud:

docker-compose stop nextcloud

Запуск композиции без пересоздания

docker-compose start

или только nextcloud:

docker-compose start nextcloud

Команда start отличается от up тем, что up выполняет перелинковку в случае если хеш сумма образа отличается от текущей залинкованной.

Удаление контейнеров композиции

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

docker-compose rm

или

docker-compose rm netxcloud

Создание docker-compose.yml для сборки

В данной главе обсудим вопрос сборки контейнеров описанных в docker-compose.yml

В одной из предыщих глав мы описывали файл Dockerfile для расчёта последовательности Фибоначчи, поэтому чтобы не изобретать ещё один велосипед попробуем собрать этот образ при помощи docker-compose.

Создадим файл docker-compose.yml со следующим содержимым:

Пример собираемого сервиса fibo
Пример собираемого сервиса fibo

services.fibo.build.context

Данная команда указывает путь до директории в которой находится необходимый Dockefile, в нашем случае это точка, то есть Dockerfile находится так же где и docker-compose.yml

services.fibo.command

В массиве command перечисляются аргументы которые будут передаваться на вход скрипта, прописанного в директиве ENTRYPOINT.

services.fibo.restart

В данном случае no говорит нам о том, что перезапуск в случае завершения выполнять не требуется.

Выполним сборку контейнера

Для этого воспользуемся командой docker-compose build

Результат работы команды build
Результат работы команды build

Запуск контейнера

Для этого воспользуемся командой docker-compose up fibo

Результат работы fibo
Результат работы fibo

Контейнер остановился сразу после того как скрипт выполнил свою работу, о чём нам и отрапортовал Compose.

Проекты похожие на Docker

Контейнеризация

  • LXC - сборка образов происходит из директории содержащей bootstrap (слепок операционной системы для chroot), а так же необходимое приложение, не поддерживает слои;
  • Podman - работает как с Dockerfile, так и с файлами Containerfile своего формата;
  • Buildah - полностью совместимая со стандартом описания Dockerfile.

Виртуализация

  • Vagrant - описание виртуальных машин, а так же то какое приложение эта ВМ должна запускать, происходит при помощи файлов Vagrantfile.

Оркестрация

  • Kubernetes - для управления контейнерами совместимыми со стандартом containerd.io;
  • Apache Mesos - может запускать как LXC так и Docker контейнеры.

Завершение

Понятное дело, что в данной статье было затронуто далеко не всё, мир DevOps вообще, да и Docker в частности просто необъятен, но надеюсь мне удалось донести до вас базовые вещи необходимые на самом начале пути.

Подписывайтесь на канал, вступайте в группу в Telegram.

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

Ну а я буду закругляться, спасибо!