Руководство по Docker для чайников от А до Я.

Docker

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

Начнем с того, что опишем базовый функционал, который пригодится в дальнейших статьях цикла, и кратко напомним об архитектуре Docker. Docker использует клиент-серверную архитектуру и состоит из клиента – утилиты docker, которая обращается к серверу при помощи RESTful API, и демона в операционной системе Linux (см. рис. 1). Хотя Docker работает и в отличных от Linux ОС, в этой статье они не рассматриваются.

структура docker

Основные компоненты Docker:
    • Контейнеры – изолированные при помощи технологий операционной системы пользовательские окружения, в которых выполняются приложения. Проще всего дать определение контейнеру Docker как запущенному из образа приложению. Кстати, именно этим идеологически и отличается Docker, например, от LXC (Linux Containers), хотя они используют одни и те же технологии ядра Linux. Разработчики проекта Docker исповедует принцип: один контейнер – это одно приложение.
    • Образы – доступные только для чтения шаблоны приложений. Поверх существующих образов могут добавляться новые уровни, которые совместно представляют файловую систему, изменяя или дополняя предыдущий уровень. Обычно новый образ создается либо при помощи сохранения уже запущенного контейнера в новый образ поверх существующего, либо при помощи специальных инструкций для утилиты dockerfile. Для разделения различных уровней контейнера на уровне файловой системы могут использоваться AUFS, btrfs, vfs и Device Mapper. Если предполагается использование Docker совместно с SELinux, то требуется Device Mapper.
    • Реестры (registry), содержащие репозитории (repository) образов, – сетевые хранилища образов. Могут быть как приватными, так и общедоступными. Самым известным реестром является Docker Hub.

 

Картинки по запросу docker

Для изоляции контейнеров в операционных системах GNU/Linux используются стандартные технологии ядра Linux, такие как:
  • Пространства имен (Linux Namespaces).
  • Контрольные группы (Cgroups).
  • Средства управления привилегиями (Linux Capabilities).
  • Дополнительные, мандатные системы обеспечения безопасности, такие как AppArmor или SELinux.

Рассмотрим перечисленные технологии чуть более подробно.

Механизм контрольных групп (Cgroups) предоставляет инструмент для тонкого контроля над распределением, приоритизацией и управлением системными ресурсами. Контрольные группы реализованы в ядре Linux. В современных дистрибутивах управление контрольными группами реализовано через systemd, однако сохраняется возможность управления при помощи библиотеки libcgroup и утилиты cgconfig. Основные иерархии контрольных групп (их также называют контроллерами) перечислены ниже:

  • blkio – задает лимиты на операции ввода-вывода и на доступ к блочным устройствам;
  • cpu – используя планировщик процессов, распределяет процессорное время между задачами;
  • cpuacct – создает автоматические отчеты по использованию ресурсов центрального процессора. Работает совместно с контроллером cpu, описанным выше;
  • cpuset – закрепляет за задачами определенные процессоры и узлы памяти;
  • devices – регулирует доступ задачам к определенным устройствам;
  • freezer – приостанавливает или возобновляет задачи;
  • memory – устанавливает лимиты и генерирует отчеты об использовании памяти задачами контрольной группы;
  • net_cls – осуществляет тегирование сетевых пакеты идентификатором класса (classid). Это позволяет контроллеру трафика (команда tc) и брандмауэру (iptables) учитывать эти тэги при обработке трафика;
  • perf_event – позволяет производить мониторинг контрольных групп при помощи утилиты perf;
  • hugetlb – позволяет использовать виртуальные страницы памяти большого размера и применять к ним лимиты.

Картинки по запросу docker

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

Примеры пространств имен, используемых Docker:
  • PID, Process ID – изоляция иерархии процессов.
  • NET, Networking – изоляция сетевых интерфейсов.
  • PC, InterProcess Communication – управление взаимодействием между процессами.
  • MNT, Mount – управление точками монтирования.
  • UTS, Unix Timesharing System – изоляция ядра и идентификаторов версии.

Механизм под названием Capabilities позволяет разбить привилегии пользователя root на небольшие группы привилегий и назначать их по отдельности. Данный функционал в GNU/Linux появился начиная с версии ядра 2.2. Изначально контейнеры запускаются уже с ограниченным набором привилегий.

При помощи опций команды docker можете разрешать и запрещать:
  • операции монтирования;
  • доступ к сокетам;
  • выполнение части операций с файловой системой, например изменение атрибутов файлов или владельца.

Подробнее ознакомиться с привилегиями можно при помощи man-страницы CAPABILITIES(7).

Установка Docker

Рассмотрим установку Docker на примере CentOS. При работе с CentOS у вас есть выбор: использовать последнюю версию из upstream или версию, собранную проектом CentOS с дополнениями Red Hat. Описание изменений доступно на странице.

В основном это обратное портирование исправлений из новых версий upstream и изменения, предложенные разработчиками Red Hat, но пока не принятые в основной код. Наиболее заметным различием на момент написания статьи было то, что в новых версиях сервис docker был разделен на три части: демон docker, containerd и runc. Red Hat пока не считает, что это изменение стабильно, и поставляет монолитный исполнимый файл версии 1.10.

Настройки репозитория для установки upstream-версии, как и инструкции для инсталляции в других дистрибутивах и ОС, приведены в руководстве по инсталляции на официальном сайте Docker. В частности, настройки для репозитория CentOS 7:

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

Проверяем статус сервиса:

Также можно посмотреть системную информацию о Docker и окружении:

При запуске аналогичной команды в случае установки Docker из репозиториев CentOS увидите незначительные отличия, обусловленные использованием более старой версии программного обеспечения. Из вывода docker info можем узнать, что в качестве драйвера для хранения данных используется Device Mapper, а в качестве хранилища – файл в /var/lib/docker/:

Опции запуска демона, как это обычно бывает в CentOS, хранятся в /etc/sysconfig/. В данном случае имя файла docker. Соответствующая строчка /etc/sysconfig/docker, описывающая опции:

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

Если бы вы запустили команду docker не пользователем root и не пользователем, входящим в группу docker, вы бы увидели подобную ошибку:

Warning: failed to get default registry endpoint from daemon (Cannot connect to the Docker daemon. Is the docker daemon running on this host?). Using system default: https://index. docker.io/v1/

Cannot connect to the Docker daemon. Is the docker daemon running on this host?

Обратите внимание, что фактически включение пользователя в группу docker равносильно включению этого пользователя в группу root.

У разработчиков RHEL/CentOS несколько иной подход к безопасности демона Docker, чем у разработчиков самого Docker из upstream. Подробнее о подходе Red Hat написано в статье разработчика дистрибутива RHEL Дэна Уолша.

Если же вы хотите «стандартное» поведение Docker, установленного из репозиториев CentOS (т.е. описанное в официальной документации), то необходимо создать группу docker и добавить в опции запуска демона:

После чего рестартуем сервис и проверяем, что файл сокета docker принадлежит группе docker, а не root:

Поиск образов и тэги Docker

Попробуем найти контейнер на Docker Hub.

В данном выводе мы получили список ряда образов HA Proxy. Самый верхний элемент списка – это HA Proxy из официального репозитория. Такие образы отличаются тем, что в имени отсутствует символ «/», отделяющий имя репозитория пользователя от имени самого контейнера. В примере за официальным показаны два образа haproxy из открытых репозиториев пользователей eeacms и million12.

Образы, подобные двум нижним, можете создать сами, зарегистрировавшись на Docker Hub. Официальные же поддерживаются специальной командой, спонсируемой Docker, Inc. Особенности официального репозитория:

  • Это рекомендованные к использованию образы, созданные с учетом лучших рекомендаций и практик.
  • Они представляют собой базовые образы, которые могут стать отправной точкой для более тонкой настройки. Например, базовые образы Ubuntu, CentOS или библиотек и сред разработки.
  • Содержат последние версии программного обеспечения с устраненными уязвимостями.
  • Это официальный канал распространения продуктов. Чтобы искать только официальные образы, можете использовать опцию –filter “is-official=true” команды docker search.

Число звезд в выводе команды docker search соответствует популярности образа. Это аналог кнопки Like в социальных сетях или закладок для других пользователей. Automated означает, что образ собирается автоматически из специального сценария Dockerfile средствами Docker Hub. Обычно следует отдавать предпочтение автоматически собираемым образам вследствие того, что его содержимое может быть проверено знакомством с соответствующим файлом Dockerfile.

Скачаем официальный образ HA Proxy:

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

Полное имя образа может выглядеть следующим образом:

[URI репозитория][имя пользователя]имя образа[:тэг]

Просмотреть список скаченных образов можно командой docker images:

REPOSITORY            TAG       IMAGE ID        CREATED        SIZE
docker.io/haproxy  latest  c0a3eb080707    13 days ago   138 MB

Команда docker images с опцией -q вернет только идентификаторы образов, что может быть полезным для использования идентификаторов в качестве входных значений для другой команды. Например, команды docker rmi, которая удаляет образ. Для того чтобы удалить все имеющиеся в локальном кэше образы, можно воспользоваться такой командой:

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

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

Помимо команды run, мы указали две опции: -i – контейнер должен запуститься в интерактивном режиме и -t – должен быть выделен псевдотерминал. Как видно из вывода, в контейнере мы имеем привилегии пользователя root, а в качестве имени узла отображается идентификатор контейнера. Последнее может быть справедливо не для всех контейнеров и зависит от разработчика контейнера. Проверим, что это действительно окружение Ubuntu:

Команду uname -a для подобных целей использовать не получится, поскольку контейнер работает с ядром хоста.

В качестве одной из опций можно было бы задать уникальное имя контейнера, на которое можно для удобства ссылаться, помимо ID-контейнера. Она задается как –name <имя>. В случае если опция опущена, имя генерируется автоматически.

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

Посмотреть список запущенных контейнеров можно командой docker ps. Для этого откроем второй терминал:

В случае если необходимо запустить контейнер с процессом, не предполагающим интерактивное взаимодействие, например с демоном, используется опция -d. Так и поступим со следующим контейнером:

Однако если отдать команду docker ps, контейнера, созданного из образа mysql, мы не обнаружим. Воспользуемся опцией -a, которая показывает все контейнеры, а не только запущенные:

В качестве статуса значится Exited. Для того чтобы разобраться с причиной, можно обратиться к журналу:

Очевидно, что при запуске контейнера не были указаны обязательные параметры. Ознакомиться с описанием переменных среды, необходимых для запуска контейнера, можно, найдя официальный образ MySQL на Docker Hub. Повторим попытку, используя опцию -e, которая задает переменные окружения в контейнере:

При помощи следующей команды можно подключиться к работающему контейнеру:

Последним параметром выступает команда, которую мы хотим исполнить внутри контейнера. В данном случае это командный интерпретатор Bash. Опции -it аналогичны по назначению использованным ранее в команде docker run.

Фактически после запуска этой команды в контейнер mysql-test добавляется еще один процесс – bash. Это можно наглядно увидеть при помощи команды pstree. Сокращенный вывод до команды docker exec:

И после команды docker exec:

Также можно воспользоваться командой docker top, поскольку из полученного вывода pstree не очевидно, что процессы mysqld и bash принадлежат одному и тому же контейнеру:

Вывод после запуска команды docker exec:

Теперь посмотрим на вывод команды docker ps, которая покажет список запущенных контейнеров:


Мы видим, что в качестве команды, запущенной при старте контейнера, использовался скрипт docker-entrypoint.sh. Это достаточно распространенный способ инициализации программного обеспечения в контейнере.

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

После чего можно было бы изучить стартовый скрипт:

Обратите внимание на одинаковый для двух контейнеров вывод в столбце COMMAND команды docker ps:

Однако команда pstree, запущенная в контейнере, покажет нам разницу в запуске контейнеров. Для mysql-test:

и для mysql-test2:

Отсюда мы видим, что во втором случае база данных MySQL запущена не была.

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

Рейтинг материала
[Голосов: 1 Рейтинг: 5]
11 апреля 2017, 03:43

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

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