Docker где хранятся контейнеры
Перейти к содержимому

Docker где хранятся контейнеры

  • автор:

Основы контейнеризации (обзор Docker и Podman)

К 2022 году о контейнеризации не слышал только ленивый. Большинство специалистов, так или иначе имеющих отношение к ИТ, хотя бы раз в жизни запускали программное обеспечение в контейнерах. Однако так ли эта технология проста и понятна? Давайте разбираться вместе!

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

История

Идея изоляции пользовательских пространств берет свое начало в 1979 году, когда в ядре UNIX появился системный вызов chroot. Он позволял изменить путь каталога корня / для группы процессов на новую локацию в файловой системе, то есть фактически создавал новый корневой каталог, который был изолирован от первого. Следующим шагом и логическим продолжением chroot стало создание в 2000 году FreeBSD jails («тюрем»), в которых изначально появилась частичная изоляция сетевых интерфейсов. В первой половине нулевых технологии виртуализации на уровне ОС активно развивались – появились Linux VServer (2001), Solaris Containers (2004) и OpenVZ (2005).

В операционной системе Linux технологии изоляции и виртуализации ресурсов вышли на новый этап в 2002 году, когда в ядро было добавлено первое пространство имен для изоляции файловой системы – mount. В 2006-2007 годах компанией Google был разработан механизм Process Containers (позднее переименованный в cgroups), который позволил ограничить и изолировать использование группой процессов ЦПУ, ОЗУ и др. аппаратных ресурсов. В 2008 году функционал cgroups был добавлен в ядро Linux. Достаточная функциональность для полной изоляции и безопасной работы контейнеров была завершена в 2013 году с добавлением в ядро пространства имен пользователей – user.

В 2008 году была представлена система LXC (LinuX Containers), которая позволила запускать несколько изолированных Linux систем (контейнеров) на одном сервере. LXC использовала для работы механизмы изоляции ядра – namespaces и cgroups. В 2013 году на свет появилась платформа Docker, невиданно популяризовавшая контейнерные технологии за счет простоты использования и широкого функционала. Изначально Docker использовал LXC для запуска контейнеров, однако позднее перешел на собственную библиотеку libcontainer, также завязанную на функционал ядра Linux. Наконец, в 2015 появился проект Open Container Initiative (OCI), который регламентирует и стандартизирует развитие контейнерных технологий по сей день.

Что такое контейнеры?

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

Процесс, запущенный в контейнере, выполняется внутри операционной системы хостовой машины, но при этом он изолирован от остальных процессов. Для самого процесса это вылядит так, будто он единственный работает в системе.

Механизмы изоляции контейнеров

Изоляция процессов в контейнерах осуществляется благодаря двум механизмам ядра Linux – пространствам имен (namespaces) и контрольным группам (cgroups).

Пространства имен гарантируют, что процесс будет работать с собственным представлением системы. Существует несколько типов пространств имен:

файловая система (mount, mnt) – изолирует файловую систему

UTS (UNIX Time-Sharing, uts) – изолирует хостнейм и доменное имя

идентификатор процессов (process identifier, pid) – изолирует процессы

сеть (network, net) – изолирует сетевые интерфейсы

межпроцессное взаимодействие (ipc) – изолирует конкурирующее взаимодействие процессами

пользовательские идентификаторы (user) – изолирует ID пользователей и групп

Процесс принадлежит не одному пространству имен, а одному пространству имен каждого типа.

Контрольные группы гарантируют, что процесс не будет конкурировать за ресурсы, зарезервированные за другими процессами. Они ограничивают (контролируют) объем ресурсов, который процесс может потреблять – ЦПУ, ОЗУ, пропускную способность сети и др.

Основные понятия

Container image (образ) – файл, в который упаковано приложение и его среда. Он содержит файловую систему, которая будет доступна приложению, и другие метаданные (например команды, которые должны быть выполнены при запуске контейнера). Образы контейнеров состоят из слоев (как правило один слой – одна инструкция). Разные образы могут содержать одни и те же слои, поскольку каждый слой надстроен поверх другого образа, а два разных образа могут использовать один и тот же родительский образ в качестве основы. Образы хранятся в Registry Server (реестре) и версионируются с помощью tag (тегов). Если тег не указан, то по умолчанию используется latest. Примеры: Ubuntu, Postgres, NGINX.

Registry Server (реестр, хранилище) – это репозиторий, в котором хранятся образы. После создания образа на локальном компьютере его можно отправить (push) в хранилище, а затем извлечь (pull) на другом компьютере и запустить его там. Существуют общедоступные и закрытые реестры образов. Примеры: Docker Hub (репозитории docker.io), RedHat Quay.io (репозитории quay.io).

Container (контейнер) – это экземпляр образа контейнера. Выполняемый контейнер – это запущенный процесс, изолированный от других процессов на сервере и ограниченный выделенным объемом ресурсов (ЦПУ, ОЗУ, диска и др.). Выполняемый контейнер сохраняет все слои образа с доступом на чтение и формирует сверху свой исполняемый слой с доступом на запись.

Container Engine (движок контейнеризации) – это программная платформа для упаковки, распространения и выполнения приложений, которая скачивает образы и с пользовательской точки зрения запускает контейнеры (на самом деле за создание и запуск контейнеров отвечает Container Runtime). Примеры: Docker, Podman.

Container Runtime (среда выполнения контейнеров) – программный компонент для создания и запуска контейнеров. Примеры: runc (инструмент командной строки, основанный на упоминавшейся выше библиотеке libcontainer), crun.

Host (хост) – сервер, на котором запущен Container Engine и выполняются контейнеры.

Open Container Initiative (OCI) – это проект Linux Foundation, основанный в 2015 году компанией Docker, Inc, целью которого является разработка стандартов контейнеризации. В настоящее время в проекте участвуют такие компании, как Google, RedHat, Microsoft и др. OCI поддерживает спецификации image-spec (формат образов) и runtime-speс (Container Runtime).

Подсказки перед практикой

На практике при работе с контейнерами могут быть полезны следующие советы:

Простейший сценарий – скачать образ, создать контейнер и запустить его (выполнить команду внутри)

Документацию по запуску контейнера (путь к образу и необходимые команды с ключами) как правило можно найти в реестре образов (например, у Docker Hub есть очень удобный поисковик) или в ReadMe репозитория с исходным кодом проекта. Создать образ и сохранить его в публичный реестр может практически каждый, поэтому старайтесь пользоваться только официальной документацией и проверенными образами! Примеры: Docker Hub/nginx, Docker Hub/debian, GitHub Readme/prometheus

Для скачивания образов используется команда pull, однако в целом она необязательна – при выполнении большинства команд (create, run и др.) образ скачается автоматически, если не будет обнаружен локально

При выполнении команд pull, create, run и др. следует указывать репозиторий и тег образа. Если этого не делать, то будут использоваться значения по умолчанию – репозиторий как правило docker.io, а тег latest

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

Работа с Docker

Docker – это открытая платформа для разработки, доставки и запуска приложений. Состоит из утилиты командной строки docker, которая вызывает одноименный сервис (сервис является потенциальной единой точкой отказа) и требует права доступа root. По умолчанию использует в качестве Container Runtime runc. Все файлы Docker (образы, контейнеры и др.) по умолчанию хранятся в каталоге /var/lib/docker.

Для установки необходимо воспользоваться официальным руководством – Download and install Docker, которое содержит подробные инструкции для Linux, Windows и Mac. Стоит сразу отметить, что контейнерам для работы необходимы функции ядра Linux, поэтому они работают нативно под Linux, почти нативно в последних версиях Windows благодаря WSL2 (через Docker Desktop или Linux диструбутив) и не нативно под Mac (используется виртуализация). Автор рекомендует использовать в тестовой и особенно в промышленной эксплуатации только Linux.

Основные команды

Ниже приведены примеры наиболее распространенных команд:

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

Хранение данных

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

Рассмотрим два способа хранения данных контейнеров:

named volumes – именованные тома хранения данных
Позволяет сохранять данные в именованный том, который располагается в каталоге в /var/lib/docker/volumes и не удаляется при удалении контейнера. Том может быть подключен к нескольким контейнерам

bind mount – монтирование каталога с хоста
Позволяет монтировать файл или каталог с хоста в контейнер. На практике используется для проброса конфигурационных файлов или каталога БД внутрь контейнера

Ниже приведены примеры использования named volume и bind mount:

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

Создание образа (Dockerfile)

Создание и распространение образов – одна из основных задач Docker. Рассмотрим два способа создания образа:

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

декларативное описание через Dockerfile
Основной способ создания образов. Необходимо создать файл Dockerfile с декларативным описанием в формате yaml через текстовый редактор и запустить сборку образа командой build

Ниже приведены примеры использования commit и build:

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

Мультиконтейнерные приложения (Docker Compose)

Docker Compose – это инструмент для декларативного описания и запуска приложений, состоящих из нескольких контейнеров. Он использует yaml файл для настройки сервисов приложения и выполняет процесс создания и запуска всех контейнеров с помощью одной команды. Утилита docker-compose позволяет выполнять команды на нескольких контейнерах одновременно – создавать образы, масштабировать контейнеры, запускать остановленные контейнеры и др.

Работа с Podman

Podman – это инструмент с открытым исходным кодом для поиска, сборки, передачи и запуска приложений. Является утилитой командной строки с аналогичными docker командами, однако не требует дополнительный сервис для работы и может работать без прав доступа root. По умолчанию использует в качестве Container Runtime crun (ранее runc).

Возможность работать с контейнерами без прав root приводит к нескольким особенностям:

все файлы Podman (образы, контейнеры и др.) пользователей с правами доступа root хранятся в каталоге /var/lib/containers, без прав доступа root – в

пользователи без root прав по умолчанию не могут использовать привилегированные порты и полноценно использовать некоторые команды

Для установки необходимо воспользоваться официальным руководством – Podman Installation Instructions, которое содержит инструкции для Linux, Windows и Mac. Стоит сразу отметить, что контейнерам для работы необходимы функции ядра Linux, поэтому они работают нативно под Linux, почти нативно в последних версиях Windows благодаря WSL2 (через Linux дистрибутив – не забудьте про wsl —set-default-version 2) и не нативно под Mac (используется виртуализация). Автор рекомендует использовать в тестовой и особенно в промышленной эксплуатации только Linux.

Основные команды

Основные команды для docker идентичны командам для podman, однако есть и приятные доработки (например, ключ —all для команд start, stop, rm, rmi). Формат образов также совместим благодаря спецификации OCI.

Ниже приведены примеры наиболее распространенных команд:

Хранение данных

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

Рассмотрим два способа хранения данных контейнеров:

named volumes – именованные тома хранения данных
Позволяет сохранять данные в именованный том, который располагается в каталоге в /var/lib/containers/storage/volumes или

/.local/share/containers/storage/volumes и не удаляется при удалении контейнера. Том может быть подключен к нескольким контейнерам

bind mount – монтирование каталога с хоста
Позволяет монтировать файл или каталог с хоста в контейнер. На практике используется для проброса конфигурационных файлов или каталога БД внутрь контейнера

Ниже приведены примеры использования named volume и bind mount:

Создание образов (Containerfile)

Создание и распространение образов – одна из основных задач Podman. Рассмотрим три способа создания образа:

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

декларативное описание через Containerfile
Необходимо создать файл Containerfile с декларативным описанием в формате yaml через текстовый редактор и запустить сборку образа командой build. Containerfile и Dockerfile полностью идентичны и взаимозаменяемы

Ниже приведены примеры использования commit и build:

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

Мультиконтейнерные приложения (Podman Compose и Podman Pod)

Podman Compose – это инструмент для декларативного описания и запуска приложений, состоящих из нескольких контейнеров. Фактически Podman Compose есть ни что иное, как реализация Docker Compose для Podman с учетом его особенностей (например, возможности работать с контейнерами без прав доступа root). Он использует yaml файл для настройки сервисов приложения и выполняет процесс создания и запуска всех контейнеров с помощью одной команды.

Podman Pod – это группа из одного или нескольких контейнеров с общим хранилищем и сетевыми ресурсами, а также спецификацией для запуска контейнеров. Концепция подов появилась и реализуется в Kubernetes.

Подсказки после практики

На практике при работе с контейнерами могут быть полезны следующие советы:

Для администрирования приложений в контейнерах следует использовать функционал systemd unit
Управлять приложениями в контейнерах как обычными сервисами Linux очень удобно – настройка, запуск, остановка, восстановление при сбоях и др. действия становятся простыми и прозрачными
Читать подробнее: Как запустить Docker / Podman контейнеры в качестве службы Systemd

Docker или Podman?
Как определить, что лучше использовать – Docker или Podman? Критериев много, однозначного ответа нет, да и разница на сегодняшний день не так велика. Однако автор рекомендует использовать Podman во всех дистрибутивах, где приложила руку RedHat. Ubuntu, Debian и др. – Docker, RHEL, Fedora – Podman

За помощь в подготовке статьи автор выражает искреннюю благодарность @novikov0805, @Eviil и @KoPashka

Где docker хранит контейнеры?

У меня redmine установлен на сервере в двух контейнерах:

  • postgresql-redmine
  • redmine

Хочу перенести их, запустить в другом месте, и сделать резервную копию. В документации ясно написано, что контейнеры создаются в том числе для переноса, однако инструкций как это делается я не нашёл. Нашёл, что docker хранит свои файлы в директории /var/lib/docker однако там у меня много тысяч файлов на 4 GB, а мои контейнеры — намного меньше, не хотелось бы таскать всё что есть в этой директории

В приведённой вами инструкции видно, что контейнер запускается с опцией

,где <путь до директории с данными на хосте>:<путь до директории с данными в контейнере>

В момент монтирования происходит затирание данных в контейнере, если те присутствовали по этому пути.

Если опция —volume не была указана, то docker автоматически создаёт volumes исходя из параметров указанных в конфигурационном файле Dockerfile.

Если обратиться к исходникам образа для PostgreSQL, то можно заметить, что это два volumes: /var/lib/postgresql и /run/postgresql . По первому пути расположены данные postgres. Собственно, они нас и интересуют.

Узнать всю информацию про volumes отдельно взятого контейнера можно командой

Данные возвращаются в json-формате, а потому предусмотрена возможность фильтрации/поиска через опцию —format

Все volumes для которых не указан путь расположения на хосте (левая часть /host/path/to/data:/container/to/data ) хранятся в директории /var/lib/docker/volumes/<хэш volume>/_data/ . Таким образом, можно просто исследовав все директории в /var/lib/docker/volumes/ и найти необходимый.

Если левая часть указана, а правая совпадает с volumes, которые указаны в Dockerfile (volumes by default), то происходит переопределение директории на хосте. Зачем нам два волиума с одинковыми данными на хосте ( /var/lib/docker/volumes/<хэш volume>/_data/ и /host/path/to/data ), правда?

Как уже было отмечено @dmitrz, пока не существует возможности управлять volumes уже на поднятых контейнерах, также как и линковать. Первая проблема должна уже очень скоро решиться.

Команда commit

Volume является отдельной сущностью и потому не попадает в commit. Вот что говорит официальная документация.

The commit operation will not include any data contained in volumes mounted inside the container.

Вообще смущает наличие двух методов создания образов: файлами конфигурации и коммитами.

Файл конфигурации задаёт изначальную конфигурацию контейнера в момент запуска, а коммит сохраняет состояние на момент коммита. Если, к примеру, волиумы не задавать (не в конфиге и не при запуске), то состояние контейнера будет меняться (писаться данные будут именно в него). Возможно, у вас может возникнуть ситуация, когда нужно подправить конфиг postgres или ещё что-то сделать внутри контейнера, то с помощью команды docker exec -it postgresql bash вы можете зайти внутрь. Далее, все сделанные изменения вы можете закоммитить в образ, чтобы запуская контейнер где-нибудь ещё из этого образа не повторять все эти действия. Но такой подход сомнительный. Лучше сделать правки в основном конфиге Dockerfile .

Ошибка в версии Ruby

В docker существует такое понятие, как entrypoint. Обычно это shell-скрипт, который дёргается при запуске docker run или docker start . Если посмотреть листинг entrypoint.sh для redmine, то можно заметить установку плагинов (bundles) для redmine. Далее, если файла $/tmp/plugins.sha1 не существует, то происходит установка плагинов. Обратите внимание на переменную $ и посмотрите в Dockerfile для redmine, т.е. REDMINE_DATA_DIR входит в волиум. Вспоминаем, что при коммите данные волиума не заносятся в образ. Когда закоммиченый контейнер запускается на новой машине видимо возникает конфликт старой версии redmine и устанавливаемых вновь плагинов.

Если данные redmine критичны ( /srv/docker/redmine/redmine:/home/redmine/data ), то их обязательно нужно перенести на новую машину. Далее, поднять либо закоммиченый образ, либо загрузить контейнер с уже новой версией redmine по той инструкции.

Можете ознакомится со всеми доступными версиями redmine образов от sameersbn.

Что касается PostgreSQL контейнера, то его данные ( /srv/docker/redmine/postgresql:/var/lib/postgresql ) обязательно необходимо перенести.

Загрузка образов

Загрузка образов из registry (https://hub.docker.com/, https://quay.io/, можно даже поднять локальный) осуществляется с помощь команды

, где тег — это, как правило, версия софта, который находится в этом образе.

Запуск контейнера происходит командой

Если образа с таким именем и тегом на локальной машине нет, то прозрачно срабатывает команда docker pull , т.е. образ ищется на удалённых registry.

Name already in use

Контейнер (Container) — стандартная единица ПО, в которую упаковывается приложение со всеми необходимыми для его полноценной работы зависимостями (кодом, средой запуска, библиотеками и настройками).

Сравнение контейнера и виртуальной машины

Будем называть хост-машиной (host machine) компьютер (сервер), ресурсы которого выделяются под контейнер или виртуальную машину.

Контейнерпроцесс или сервис, который напрямую запущен на хост-машине.

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

Виртуальная машина (Virtual Machine) — изолированная операционная подсистема на хост-машине.

С помощью виртуальной машины можно внутри Windows ОС запустить Linux и наоборот. Существует множество инструментов, чтобы работать с виртуальными машинами (например, Virtual Box).

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

Docker использует клиент-серверную архитектуру.

С клиента, который называется Docker-клиент (Docker client), поступают CLI-команды.

Клиент при помощи REST API передаёт команды серверу, который называется Docker-демон (Docker daemon).

Docker-демон собирает, запускает и раздаёт (distribute) контейнеры.

Этапы докеризации приложения

Dockerfile содержит инструкции (instructions) — последовательность действий, которые нужно выполнить, чтобы построить образ.

Простой пример Dockerfile для NodeJS-приложения.

Конкретные инструкции Dockerfile разобраны здесь.

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

Один образ может расширять другой.

Базовый образ (Base Image) — образ, который не имеет родительского образа.

Для построения образа используется команда docker build , которая принимает контекст (context) — путь к папке, с которой будет происходить работа в Dockerfile.

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

Каждая инструкция в Dockerfile создаёт новый слой (layer) в образе.

Если Dockerfile лежит не в корне контекста, то можно явно указать путь к файлу.

Можно задать явное название образа.

Найти созданный образ можно среди других образов при помощи команды docker images .

Образы хранятся в Docker-реестре (Docker registry).

Одним из публичных реестров является Docker Hub. Он используется по умолчанию.

Создание и запуск контейнера

Контейнер (Container) — запускаемый экземпляр образа.

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

Флаг -d используется для запуска контейнера в фоновом режиме (in background), таким текущая консоль не будет занята контейнером и можно будет вводить в неё другие команды.

Можно также задать явное имя контейнеру при создании.

Команда docker run объединяет в себе две команды: docker create и docker start .

Для просмотра списка всех запущенных контейнеров и информации о них используется команда docker ps .

Для просмотра всех контейнеров (в том числе и незапущенных) используется флаг -a .

Если есть необходимость посмотреть, что лежит внутри запущенного контейнера, можно зайти в него при помощи команды docker exec .

Пример работы в интерактивном режиме.

Флаг -i отвечает за переход в интерактивный режим, флаг -t позволяет эмулировать терминал.

Для остановки контейнера используется команда docker stop .

Композиция нескольких контейнеров

Для написания приложения чаще всего не достаточно одного контейнера.

Если есть необходимость иметь несколько контейнеров в одном приложении, то нужно каждый из них настроить, как указано в этапах выше.

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

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

Хранение данных в Volume

Volume — предпочитительный механизм для хранения данных (persisting data), используемых в Docker-контейнере.

Volume даёт контейнеру доступ к какой-то локальной папке на хост-машине, на которой этот контейнер запущен. Файлы из Volume нельзя использовать на этапе сборки (build-time), то есть в Dockerfile нельзя использовать файлы из Volume, они доступны лишь во время выполнения (run-time).

Почему следует использовать Volume

  • Volume хранится вне контейнера, поэтому он не увеличивает размер контейнера и не подвержен влиянию жизненного цикла контейнера.

Использование образов с помощью FROM

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

Валидный Dockerfile должен содержать как минимум одну инструкцию FROM и она должна быть первой инструкцией в файле.

Для создания базового образа используется инструкция FROM scratch .

Копирование файлов с помощью ADD И COPY

Инструкция COPY позволяет скопировать локальный файл или папку с хост-машины в образ. Она принимает два параметра: относительный путь на хост-машине, откуда копировать, и абсолютный путь, по которому данные будут доступны в контейнере.

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

Если нет явной необходимости в ADD, лучше использовать COPY.

Запуск команд с помощью RUN и CMD

Инструкция RUN позволяет запускать команды внутри образа (image). Эти команды запускаются один раз во время сборки (build) и записываются в образ как новый слой (layer).

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

Таким образом, несмотря на то, что CMD является инструкцией Dockerfile, он запускается не во время сборки, а уже в запущенном контейнере. Чаще всего командой в CMD выступает запуск сервера.

Рабочая директория WORKDIR

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

По умолчанию используется WORKDIR / .

Инструкция WORKDIR может быть использована несколько раз.

Инструкция ARG определяет переменную, которую можно передать во время сборки (build-time) контейнера.

  • Объявление аргументов в Dockerfile.
  • Использование аргументов в Dockerfile.
  • Передача аргументов в команду сборки контейнера.

Переменные окружения ENV

Инструкция ENV сохраняет переменную внутри контейнера. Таким образом переменная в контейнере доступна во время выполнения (run-time).

  • Объявление переменных окружения в Dockerfile.
  • Передача в команду запуска контейнера.

Если есть необходимость передать аргумент как переменную окружения, то можно сделать это следующим образом.

Порты и инструкция EXPOSE

Ранее уже отмечалось, контейнеры достаточно изолированы от окружающего мира, но иногда это можно контролировать.

Если внутри контейнера запущено приложение на каком-то порте (например, 3000 ), то оно будет доступно только внутри контейнера. Проверить, что оно действительно запущено в контейнере можно, сделав запрос на URL из консоли контейнера.

При этом у хост-машины нет доступа к приложению, запущенному в контейнере.

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

Если порт контейнера выставляется наружу, то он называется выставленным (exposed).

Можно также вместо инструкции EXPOSE выставить порт при помощи флага —expose .

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

Чтобы предоставить доступ хост-машине, необходимо опубликовать (publish) порт. В таком случае порт называют опубликованным (published).

При создании контейнера у него по умолчанию нет опубликованных портов.

Для публикации порта используется флаг —publish , -p . Флаг принимает порт хост-машины и порт контейнера в формате hostPort:containerPort .

В примере выше приложение, которое запущено внутри контейнера на порте 3000 , также доступно и на хост-машине на порте 4000 ( http://localhost:4000 ).

Можно также опубликовать сразу все выставленные порты контейнера на случайные порты хост-машины при помощи флага —publish-all , -P .

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

Несколько портов лучше публиковать следующим образом.

Пример Dockerfile для NodeJS

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

Docker Compose — инструмент, позволяющий составлять композицию контейнеров (запускать приложения, состоящие из нескольких контейнеров).

Docker Compose использует файлы формала YAML ( .yml ).

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

Есть два способа запустить сервис.

  • Можно указать готовый образ в поле image (можно скачать его с Docker Hub или создать самому).
  • Можно настроить этап построения в поле build , указав там путь к Dockerfile, по которому должен быть построен образ.

Порядок запуска сервисов

Docker Compose запускает и останавливает контейнеры в порядке их зависимостей.

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

Настроить зависимость можно при помощи поля depends_on .

В примере ниже запуск сервиса server произойдёт раньше, чем запуск client .

В случае остановки происходит обратная ситуация: client останавливается раньше, чем server .

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

Можно указать порты на хост-машине и внутри контейнера, по которым будет доступно приложение.

Сервис foo будет доступен на порте 3000 внутри контейнера.

Сервис bar будет доступен на порте 4000 внутри контейнера и на порте 3001 на хост-машине.

Конфигурация сервиса baz эквивалетна конфигурации сервиса foo .

Пример композиции контейнеров трёхуровнего приложения

Трёхуровневое (3-tier) приложение состоит из клиента, сервера и базы данных. Для каждого уровня необходим отдельный контейнер, а поскольку они связаны друг с другом, создаётся их композиция.

Первым подключается база данных (сервис db ), поскольку её может использовать сервер. Вторым подключается сервер (сервис server ), поскольку его может использовать клиент. Последним подключается клиент (сервис client ).

В docker-compose может указываться уже собранный образ (builded image) вместе с командой, которая должна быть запущена в контейнере; или Dockerfile , по котому образ будет создаваться.

Переменные ENVIRONMENT и ARGS

Перееменные окружения ENVIRONMENT передаются в уже запущенные контейнеры.

Переменные ARGS доступны во время построения образа (build image).

Передача и использование аргументов в Docker Compose

  • Передача любых аргументов осуществляется при запуске Docker Compose в формате argument=value .
  • Переданные аргументы доступны для использования в YAML-файле в формате $ . Есть возможность задать значение по умолчанию: $ . Без некоторых значений по умолчанию (например, для портов) может возникать ошибка приведения типов.

Можно проверить правильность настройки, а также посмотреть все установленные переменные при помощи следующей команды.

Руководство по Docker. Часть 1: образ, контейнер, сопоставление портов и основные команды

Docker — платформа с открытым исходным кодом для создания, развертывания и управления контейнеризированными приложениями.

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

Содержание:

  1. Образы и контейнеры.
  2. Запуск первой программы в Docker.
  3. Основные команды контейнеров Docker.
  4. Командная строка Docker-контейнера.
  5. Пользовательские образы Docker.
  6. Запуск веб-приложения в Docker.
  7. Docker и сопоставление портов.
  8. Выводы.

1. Образы и контейнеры

Самые важные концепции Docker!

  • Что такое “образ” или “image”?

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

  • Что такое “контейнер”?

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

Количество контейнеров не ограничено, все они работают на общей инфраструктуре хоста и общей операционной системе. Посмотреть на список контейнеров можно с помощью команды:

2. Запуск первой программы в Docker

Первым делом нужно вывести на экран фразу “Hello, world!”.

В Docker каждому контейнеру нужен образ, поэтому, чтобы запустить hello-world , введите в консоль или терминал команду:

  • Что делаетdocker run?

Попытка создания и запуска контейнера. Согласно документации API-клиента Docker, в установленном порядке выполняются следующие действия.

  1. Консоль: выполняет команду docker run hello-world .
  2. Docker client (CLI): получает команду и выполняет действия на ее основе.
  3. Сервер Docker: связывается с CLI, чтобы выдать клиенту из кэша образов (Image Cache) нужный образ с названием hello-world . Если такого образа нет, то сервер Docker связывается с хабом Docker, где скачивает образ с указанным именем.
  4. Docker Hub: хаб Docker получает запрос от сервера Docker на загрузку конкретного файла образа из общедоступного хранилища.
  5. Сервер Docker: снова ищет образ в разделе “Image Cache”, затем создает контейнер — экземпляр образа hello-world .
  6. Контейнер Docker: согласно первоначальным установкам из образа, в контейнере запускается простая программа hello-world .
  7. Консоль: в результате успешного выполнения всех вышеизложенных этапов отобразит информацию со скриншота.

3. Основные команды контейнеров Docker

Прочитайте перечень команд управления поведением контейнеров без изменения образов.

  • Удалить все неактивные контейнеры Docker:
  • Вывести на экран stderr и stdout от запущенных в контейнере программ, логирование контейнера Docker:
  • Индекс активных контейнеров:
  • Индекс всех контейнеров, включая неактивные:
  • Индекс образов, установленных в системе на данный момент:
  • Деактивировать Docker-контейнер по идентификатору:
  • Удалить неактивный контейнер по идентификатору:
  • Удалить конкретный образ по его названию:

4. Командная строка Docker-контейнера

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

  • Как выполнить команду внутри контейнера Docker?

Здесь exec позволяет выполнять команды в контейнере, а флаг -it разрешает прием ввода от пользователя к процессу, а также вывод в терминал как результата выполнения процесса, так и сообщений об ошибках.

  • Как получить доступ к командной строке контейнера Docker?

Иногда удобно напрямую взаимодействовать с командной строкой контейнера. Для поиска ошибок и отладки, или вы можете запустить там все команды Linux одновременно. Убедитесь, что нужный контейнер запущен, а потом введите следующую команду:

sh выдаст разрешение на ввод подсказок (prompt), и вы сможете выполнять внутри контейнера различные задачи, например:

  • Как разрешить ввод при запуске контейнера из образа Docker?

Допустим, что вы хотите сразу после скачивания из хаба Docker запустить образ под названием busybox . Что вы сделаете?

Теперь, чтобы ответить на вышеуказанный вопрос, выполните следующую команду:

Теперь все знания о полезных командах из двух пунктов руководства помогут вам подготовиться к следующему шагу.

5. Пользовательские образы Docker

Как добавить файлы в контейнер и какую операционную систему выбрать для образа?

  • Dockerfile

Основной файл, хранящий все инструкции. Каждый dockerfile одинаково структурирован. Чтобы создать dockerfile для пользовательского образа необходимо указать:

  1. Базовый образ.
  2. Все зависимости и условия запуска программы.
  3. Команду для запуска программы при старте контейнера.
  • Docker Client

После создания dockerfile вы сразу попытаетесь собрать образ. Следовательно, Dockerfile отправляется клиенту Docker.

  • Docker Server

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

  • Как же создать образ Docker?
  1. Сначала напишите dockerfile .
  2. Затем выполните команду docker build .
  3. Теперь запустите образ командой docker run <image-id> .

Рассмотрим простой dockerfile , в котором установлен сервер базы данных Redis :

Теперь, глядя на Dockerfile, у вас наверняка возникли вопросы! Ответим на них сейчас:

  • Что такое базовый образ?

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

  • Почему Alpine?

Alpine Linux — это как Windows и macOS, где можно установить почти все, что вам нужно. Alpine занимает мало памяти, что хорошо подходит для установки Redis. Базовый образ загружается через инструкцию FROM .

  • Как установить зависимости?

Посмотрите, в dockerfile написана инструкция ‘RUN’, которая выполняет команды внутри контейнера. APK означает менеджер пакетов Alpine Linux . Команды apk применяются в любой работающей системе на базе Alpine Linux для удаления, установки, обновления программного обеспечения.

  • Как указать команду для запуска приложения в контейнере?

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

Теперь поговорим детальнее о команде docker build . Что происходит перед тем, как результат ее выполнения появляется на экране?

  1. Загружается контейнер с образом alpine .
  2. Файловая система из образа alpine переносится во временный контейнер, в нем и выполняются инструкции по установке базы данных Redis. После завершения установки временный контейнер удаляется, а файловая система переносится обратно в образ alpine , тем самым обновляя его. Теперь в новом образе установлен Redis.
  3. По аналогии создается еще один временный контейнер для установки команды запуска сервера Redis при каждом запуске образа в контейнере. Следом временный контейнер удаляется, а обновленная файловая система устанавливается в конечном пользовательском образе.
  4. Наконец, для запуска контейнера по шаблону пользовательского образа выполняется команда docker run <container-id> .

Примечание: если вы обновите dockerfile образа и попытаетесь пересобрать его, то Docker получит кэш из предыдущего образа, чтобы пропустить тот же процесс. Он обновит только вновь добавленный раздел в Dockerfile. Порядок в Dockerfile также важен. Если вы измените порядок команд, то кэш обнулится!

  • Как установить специфическое имя для образа?

docker build -t <имя>/<репозиторий/имя проекта>:версия .

6. Запуск веб-приложения в Docker

В этом разделе создадим и докеризируем простое приложение на Node.js на основе пользовательского образа. Не беспокойтесь, вам не нужны знания о Node.js для выполнения пунктов руководства.

  • Шаг 1
    Напишите файл package.json , ведь Express.js — это бэкенд-фреймворк для веб-приложений на Node.js. Укажите в нем последнюю версию при помощи символа звездочки * , а затем укажите запуск по умолчанию для команды node index.js , где index.js — это основной веб-файл.
  • Шаг 2
    Напишите простой файл index.json . Планируется, что фреймворк Express.js через / получает запросы и отправляет обратно ответ. Чтобы получить запрос и ответ, необходимо установить порт для прослушивания.
  • Шаг 3
    Напишите dockerfile . Помните все ключевые шаги? Если забыли, то поднимитесь вверх по разделам и освежите знания. Тем не менее совершенно очевидно, что для запуска приложения понадобятся установленные, готовые к работе Node.js и NPM.
  • Шаг 4
    Постройте пользовательский образ, присвойте ему теги.
  • Шаг 5
    Запустите контейнер с приложением, запомните прослушиваемый порт.
  • Шаг 5
    Проверьте, можно ли получить доступ к приложению из браузера, действительно ли оно работает?
    О, нет! Что-то пошло не так? Пришло время выяснить.

Возможно, у вас сейчас на уме один вопрос: зачем в dockerfile написана команда WORKDIR /usr/app ?

Основная цель — разделение рабочих файлов, чтобы строго необходимые для работы приложения файлы не смешивались с другими директориями, а легко отделялись от остальных при необходимости, если возникнут проблемы. Следовательно, внутри контейнера файлы веб-приложения сохраняются в каталоге /usr/app .

7. Docker и сопоставление портов

Рассмотрим, как разобраться в настройках и правильно установить сопоставление портов!

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

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

Однако по умолчанию Docker также позволяет исходящие запросы. Интересно, как это сделать? Проверьте зависимости в dockerfile , где NPM из контейнера напрямую обращается к интернету.

  • В команде запуска контейнера первый порт — это порт хост-машины или локальной машины, а второй порт — это порт контейнера:

Выводы

Веб-приложение запущено и доступно при обращении к порту 8080 локальной машины.

Несмотря на успех, стоит упомянуть три распространенные ошибки, допускаемые в процессе работы с Docker Server и Docker Client.

  1. Неправильный выбор базового образа.
  2. Отсутствие в dockerfile команд ADD или COPY для переноса файлов хост-машины в контейнер.
  3. Неправильное сопоставление портов при создании контейнера.

Далее прилагаются скриншоты правильно запущенного в Docker веб-приложения Node.js, чтобы вы могли с ними свериться.

Самостоятельно ознакомьтесь с файлом dockerfile для построения пользовательского образа и запуска веб-приложения на Node.js.

Проанализируйте изображенный на скриншоте файл index.js с простым примером веб-приложения на Node.js. Данный файл запускается внутри контейнера Docker при помощи соответствующей команды из dockerfile .

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

Добавить комментарий

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