Контейнерные технологии имеют длительную историю. Еще в 1979 году для ОС Unix была разработана утилита chroot (change root), при помощи которой можно было создавать отдельную изолированную ОС внутри основной. В ней могли работать приложения, причем доступ к их файлам был возможен только внутри пространства этой ОС. Это было очень полезно, например, для тестирования вновь написанного программного кода.

На рубеже 2000-х годов была создана Unix-подобная ОС под названием FreeBSD Jails, которая также использовала принцип chroot, но подняла его на новый уровень, обеспечив изоляцию пользователей, файловых систем, сетевых интерфейсов и др.

В 2004 году компания Sun представила новую ОС Solaris, которая называлась Solaris Containers. В ней были реализованы практически те же сервисы, что и в FreeBSD Jails, то есть она обеспечивала изоляцию отдельных сегментов, называемых «зонами» (zones).

В 2008 году была разработана ОС с открытым кодом Linux Containers (LXC). Именно эта ОС заложила основы контейнерной технологии Docker, которая появилась в 2013 году. Docker – это также название компании, которая придала импульс контейнерным технологиям. Вначале эта технология базировалась на LXC, затем компания Docker заменила LXC собственной библиотекой программ под названием libcontainer.

Docker достигла значительных успехов в создании экосистемы контейнеров, добавив такие сервисы, как интерфейс программирования приложений API (application programming interface), система командной строки CLI (command line interface), эффективная модель образов данных, а также средства управления кластером, обеспечивающие простое масштабирование.

Может создаться впечатление, что контейнерные технологии работают только в среде Linux, однако это не так. Например, в ОС Windows Server 2016 и Windows 10 контейнеры Docker поддерживаются для приложений Windows.

Виртуальные машины, контейнеры и «голое железо» (Bare Metal)

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

Рассмотрим основные особенности архитектуры: традиционной (Bare Metal) и виртуализованной на базе гипервизора.

Различия традиционной и виртуализованной архитектуры

Различия традиционной и виртуализованной архитектуры

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

Может возникнуть вопрос, зачем нужен гипервизор, а также все эти сложности? Разве не проще обходиться отдельными физическими серверами? Зачем их нужно нарезать, как батон хлеба? Ответ очень прост. В любом сервере традиционной архитектуры мощность процессора используется на 5–15 %. То есть оборудование любого дата-центра без виртуализации простаивает на 85–95 %. Почему это так – вопрос отдельный, но это так. В отдельные моменты какие-то тяжелые приложения иногда могут загрузить процессор сервера процентов на 50 %, но во временнóм усреднении процессор любого физического сервера без виртуализации оказывается загружен не более, чем на 15 %.

А вот при виртуализации использование (или «утилизация», как говорят ИТ-профессионалы) оборудования сервера можно довести практически до 100 %.

Почему при виртуализованной архитектуре утилизация оборудования во много раз выше

Почему при виртуализованной архитектуре утилизация оборудования во много раз выше

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

Каждая ВМ требует наличия отдельной ОС. Каждая ОС требует доступа к оперативной памяти RAM, системе хранения (СХД) и ресурсам процессора. Горизонтальное масштабирование такой вычислительной среды, т. е. развертывание все большего числа виртуальных машин, неизбежно приведет к тому, что все аппаратные ресурсы сервера займут экземпляры операционных систем. Более того, если посмотреть, чем занимаются сами виртуальные машины, то можно будет обнаружить, что многие из них не полностью используют назначенные им ресурсы (а многие и вообще их не используют, простаивают).

Кроме того, при перемещении виртуальных машин с сервера одного вендора на сервер другого вендора также могут возникнуть проблемы. Например, ВМ на базе vSphere невозможно переместить в среду Amazon EC2 или Hyper-V. То есть перемещаемость (portability) ограничена рамками гипервизора: в средах, где гипервизор, на котором лежит ОС виртуальной машины, работает, ВМ могут перемещаться, а в средах, где используется другой гипервизор, такая перемещаемость не гарантирована.

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

Архитектуры на базе контейнеров

Архитектуры на базе контейнеров

Если сравнить рисунки 1 и 3, то видно, что в архитектуре появляется новый слой абстрагирования – движок контейнера (container engine). Что это дает? Во-первых, даже в традиционной архитектуре Bare Metal теперь стало можно создавать изолированные рабочие нагрузки. Ранее такое было возможно только в виртуализованных средах на базе гипервизора. При этом не создается «толпа» операционных систем, которая быстро съедает аппаратные ресурсы.

Во-вторых, даже в виртуализованной среде при использовании гипервизора можно получить преимущества. Преимущества виртуализации не ограничиваются при этом только лишь увеличением коэффициента использования аппаратных ресурсов сервера или кластера из однородных серверов. С такой архитектурой можно управлять виртуализованными нагрузками гораздо более эффективно, чем в классической архитектуре виртуализации. Это приводит к росту доступности приложений, возможностям реализации эффективных схем катастрофоустойчивости DR (Disaster Recovery) и к росту общей управляемости рабочими нагрузками.

Вывод таков: контейнеры можно запускать где угодно: хоть в традиционной архитектуре Bare Metal, хоть в виртуализованной среде дата-центра, хоть в облаке. Работать будет все везде одинаково. Управлять работой приложений на контейнерах можно легко и наглядно. Кроме того, контейнеры дают новые возможности к повышению доступности и защищенности приложений, причем без таких огромных затрат, как строительство резервных дата-центров катастрофоустойчивости, основная задача которых – дублирование нагрузки с основным дата-центром и переключение в активный режим при аварии в основном дата-центре.

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

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

Архитектура на базе Docker Engine

Рассмотрим контейнеры Docker, где в качестве движка используется Docker Engine, который состоит из следующих основных элементов:

  • Docker host: это сервер, на котором постоянно работает Docker Engine как фоновый процесс (демон), обеспечивающий работу операционной системы.
  • API (Application programming interface): интерфейс программирования приложений определяет, как приложение будет взаимодействовать с демоном ОС.
  • CLI (Command line interface): интерфейс командной строки для клиента Docker, где администратор может взаимодействовать с API, который, в свою очередь, взаимодействует с сервером.
  • Dockerfile: список команд, позволяющий сгенерировать образ Docker image.
  • Docker image: образ Docker, система файлов, обеспечивающая корректную работы приложения. Образ хранится в СХД до тех пор, пока не понадобится запустить его для точного определения рабочей нагрузки того или иного приложения. Образы могут быть многослойными, отображающими изменения в файловой системе, инициированные со стороны Dockerfile.
  • Docker Registry: регистр, который хранит образы файлов Docker. Регистр напоминает книжную полку. В нем хранятся образы, которые можно извлекать для создания контейнеров, необходимых для запуска служб или веб-приложений. Частные регистры Docker могут храниться в локальной среде или в общедоступном облаке. Публичный регистр Docker называется Docker Hub, его могут использовать любые пользователи Docker. При использовании команд «docker pull» или «docker run» требуемый образ контейнера извлекается из заданного в конфигурации регистра. При использовании команды «docker push» образ помещается обратно в регистр.
  • Docker objects: объекты Docker, образы, контейнеры, сети, тома, плагины и другие объекты.
Архитектура Docker (источник: docs.docker.com)

Архитектура Docker (источник: docs.docker.com)

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

В Docker используется архитектура «клиент-сервер». Клиент Docker коммуницирует через REST API с демоном (Docker Daemon), который выполняет работу по созданию, запуску, управлению и распространению контейнеров Docker. Клиент и демон могут работать внутри единой системы, или же быть разнесенными в разные системы. В первом случае для API используются сокеты UNIX, во втором – сетевой интерфейс.

Соотношения между регистрами, образами и контейнерами

Соотношения между регистрами, образами и контейнерами

Файловые системы Union

Файловые системы Union, или UnionFS, предназначены для создания уровневой структуры образов, что делает их небольшими по объему и быстрыми в создании контейнеров. Docker Engine использует UnionFS в качестве склада образов для создания контейнеров и может использовать разные варианты UnionFS, включая AUFS, btrfs, vfs, и DeviceMapper.

Kubernetes

Часто можно встретить упоминание контейнеров Docker в связке с Kubernetes. Может создаться впечатление, что это две разных технологии контейнеров. Однако это не так, Kubernetes – это просто одно из инструментальных средств управления контейнерами Docker.

Kubernetes по-гречески означает «рулевой», «штурман». Исходный код Kubernetes был предоставлен для общего доступа компанией Google в 2014 году.

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

Kubernetes в основном занимается масштабированием и обработкой ошибок в приложении, предоставляет шаблоны развертывания и др. Например, Kubernetes может легко управлять поэтапным развертыванием системы в производственной среде («канареечное развертывание»).

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

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

Преимущества контейнеров

Развертывание приложений исторически прошло три основных этапа

Традиционное развертывание: запуск приложений на физических серверах. Границы ресурсов для приложений на физическом сервере было очень сложно определить и это вызвало проблемы с распределением ресурсов между приложениями. Решением этой проблемы традиционно был запуск каждого приложения на отдельном физическом сервере. Однако при таком способе использование вычислительных ресурсов было очень неоптимальным. Большие парки серверов также очень дорого обходились.

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

Развертывание при помощи контейнеров: Контейнеры похожи на виртуальные машины, однако, у они могут совместно использовать операционные системы (ОС) для приложений. Поэтому контейнеры считаются «легкими». Подобно виртуальной машине, контейнер имеет свою собственную файловую систему, процессор, память, пространство процесса и многое другое. Но в отличие от ВМ, контейнеры не связаны с базовой физической инфраструктурой и могут легко переноситься между различными вычислительными средами.

Преимущества контейнеров:

  • Простота и эффективность создания образа контейнера по сравнению с использованием образа виртуальной машины.
  • Непрерывная разработка, интеграция и развертывание обеспечивает надежную и частую сборку и развертывание образа контейнера с быстрым и простым откатом благодаря неизменности образа (принцип DevOps).
  • В контейнерах можно отслеживать не только информацию и метрики на уровне ОС, но также информацию о работоспособности приложений и другие параметры.
  • Идентичная окружающая среда при разработке, тестировании и релизе: на ноутбуке работает так же, как и в облаке.
  • Универсальность и переносимость между операционными системами: работает на Ubuntu, RHEL, CoreOS, Google Kubernetes Engine и в других средах.
  • Распределенные выделенные микросервисы: вместо монолитного стека на одной большой выделенной машине, приложения могут быть разбиты на более мелкие независимые части, которые можно динамически развертывать и управлять.
  • Высокая эффективность и компактность использования ресурсов.