Разработка и производство приложения – довольно трудоёмкий и непредсказуемый процесс, в котором зачастую встречается множество трудностей и препятствий. Помимо рутинных задач по разработке могут возникнуть проблемы, связанные с отслеживанием зависимостей, масштабированием, обновлением компонентов и т.п.
Система контейнеризации и сервис-ориентированное проектирования Docker предназначена для решения большинства этих проблем. С её помощью приложение можно разделить на отдельные компоненты, которые, в свою очередь, можно упаковать вместе со всеми зависимостями. Такие компоненты в дальнейшем легко развёртываются на любой архитектуре. Такая технология значительно упрощает процессы масштабирования и обновления компонентов.
Данная статья поможет разобраться в основах контейнеризации и понять преимущества такого подхода. Docker позволяет легко и быстро масштабировать и управлять распределёнными контейнерами.
Краткая история контейнеризации Linux
Контейнеризация и изоляция компонентов – далеко не новые технологии в мире компьютерных вычислений. Некоторые Unix-подобные операционные системы пользуются подобными технологиями в течение нескольких десятков лет.
В Linux основой для развития контейнеризации является система LXC, добавленная в ядро в 2008 году. Для обеспечения изоляции процессов система LXC скомбинировала cgroups (позволяет изолировать и отслеживать использование ресурсов) и пространства имён (разделяют группы таким образом, что они не пересекаются друг с другом).
Позже был представлен инструмент Docker, предназначенный для упрощения процессов создания и управления контейнерами. Система Docker изначально использовала LXC в качестве драйвера по умолчанию (для этого была разработана библиотека libcontainer). На момент своего появления Docker не предлагал большого количества инновационных идей, однако благодаря стандартизации интерфейса и упрощению процессов контейнеризации эта технология стала доступной для обычных системных администраторов и разработчиков.
Преимущества контейнеризации
Контейнеры предоставляют массу заманчивых преимуществ как разработчикам приложений, так и системным администраторам. Рассмотрим основные преимущества.
Разделение хост-системы и контейнеризованного приложения
Контейнеры несут в себе полную стандартизацию. Это значит, что контейнер подключается к хосту и ко внешним сервисам с помощью заранее определенных интерфейсов. Контейнеризованные приложения не должны каким-либо образом зависеть от ресурсов или архитектуры хост-системы. Это упрощает требования, предъявляемые к операционному окружению при разработке приложения. Аналогично, хост воспринимает контейнеры как «чёрные ящики», никак не влияющие на его работу (вне зависимости от того, какое приложение помещено в контейнер).
Высокая масштабируемость
Данное преимущество тесно связано с предыдущим. Благодаря разделению хост-системы и контейнеров компоненты приложения легко масштабируются. Сервис-ориентированная архитектура (речь о которой пойдёт далее) вместе с контейнеризованными приложениями – надёжная основа для быстрого масштабирования.
При таком подходе разработчики могут запускать на рабочей машине несколько контейнеров, при этом ту же систему можно горизонтально масштабировать. Контейнеры можно масштабировать при их выходе в производство.
Простое управление зависимостями и версиями приложения
Контейнеры позволяют разработчикам связывать приложение (или его компоненты) вместе с его зависимостями в единое целое. Таким образом, хост-системе не нужно устанавливать дополнительные пакеты, чтобы запустить такое приложение. Пока система запускает Docker, она может запустить любой контейнер.
Это значительно упрощает управление зависимостями и версиями приложения. Хост больше не несёт ответственности за зависимости, поскольку всё необходимое уже содержится в самом контейнере (за исключением тех случаев, когда один контейнер зависит от другого).
Легковесная изолированная среда выполнения
Конечно, контейнеры не в состоянии обеспечить изоляцию и управление ресурсами на уровне технологии виртуализации, однако, они предоставляют очень лёгкую среду выполнения. Контейнеры изолируются на уровне процесса, используя ядро хост-системы. Это значит, что сам контейнер не включает в себя полноценной операционной системы, благодаря чему он запускается в считанные секунды. Разработчики могут без труда запускать сотни контейнеров.
Совместно используемые слои
Контейнеры могут состоять из так называемых слоёв. Такие слои могут использоваться несколькими контейнерами. К примеру, если один слой лежит в основе нескольких контейнеров, то такой слой может использоваться этими контейнерами без дублирования в качестве базового слоя. Это уменьшает загрузку дискового пространства.
Компонуемость и предсказуемость
Файлы Docker позволяют задавать конкретный ряд действий, необходимый для создания нового образа. Это позволяет настраивать среду выполнения (как код) и при необходимости сохранять эти настройки в системе контроля версий. Один и тот же файл Docker, собранный в одном и том же окружении, всегда создаст идентичный образ.
Использование Docker-файлов
Образы контейнеров можно создавать при помощи интерактивного процесса. Однако обычно для этого удобнее использовать Docker-файл. Просто поместите в такой файл последовательность необходимых действий.
Примечание: Docker-файл – это разновидность build-файла, в котором хранится описание процесса создания образов.
Docker-файлы очень удобны, полезны и просты в написании. Основные их преимущества:
- Простое управление версиями: Docker-файлы можно сохранять в системе контроля версий для отслеживания изменений и отмены ошибок.
- Предсказуемость: Сборка образов с помощью Docker-файла устраняет ошибки пользователя в процессе создания образов.
- Управление: Чтобы поделиться созданным образом, пользователь обычно предоставляет Docker-файл, с помощью которого можно создать данный образ; тогда другие пользователи могут проконтролировать процесс создания образа. По сути, выкладывая Docker-файл в открытый доступ, вы предоставляете историю действий, выполненных для построения образа.
- Гибкость: Создавая образы из Docker-файла, вы можете переопределить настройки по умолчанию в интерактивном режиме.
Архитектура контейнеризованных приложений
При разработке приложений, которые будут разворачиваться в контейнерах, одним из важнейших вопросов является архитектура приложения. Как правило, контейнеризованные приложения лучше всего работают на сервис-ориентированной архитектуре.
В сервис-ориентированных приложениях функциональность разделена на отдельные компоненты, которые могут взаимодействовать через четко заданные интерфейсы. Технология контейнеризации поощряет такой тип проектирования, поскольку он позволяет независимо масштабировать или обновлять каждый из компонентов.
Спроектированные таким образом приложения должны обладать следующими характеристиками:
- Они не должны учитывать особенности хост-системы или полагаться на них.
- Каждый компонент должен иметь API, с помощью которого пользователи могут получить доступ к сервису.
- При начальной настройке каждый сервис должен принимать во внимание переменные окружения.
- Приложения должны хранить свои данные вне контейнера (в смонтированных томах или отдельных специальных контейнерах).
Такой подход позволяет индивидуально обновлять или заменять каждый компонент, пока поддерживается его API. Кроме того, это позволяет сфокусироваться на горизонтальном масштабировании.
Вместо жёсткого кодирования значений каждый компонент позволяет определить необходимые значения по умолчанию. Компонент может использовать эти настройки в качестве резервных, поскольку значения из окружения имеют более высокий приоритет. Параметры окружения можно получить с помощью инструментов обнаружения сервисов, которые компонент опрашивает во время запуска.
Помещение конфигураций контейнера в окружение позволяет легко управлять поведением приложения, при этом никак не изменяя содержимое контейнера. Кроме того, это позволяет влиять на несколько экземпляров компонента при помощи одной настройки. Сервис-ориентированное проектирование приложений продуктивно взаимодействует со стратегиями внешнего конфигурирования, поскольку оба подхода делают развёртывание и масштабирование более гибким и понятным.
Управление контейнерами с помощью реестров Docker
После того как приложение было разделено на функциональные компоненты и настроено для взаимодействия с остальными контейнерами и окружением, можно предоставить к нему открытый доступ. Для этого приложение нужно поместить в реестр. Загрузка образов в реестр позволяет Docker-хостам загружать и запускать экземпляры контейнера, просто указав имя образа.
Для этого существует огромное количество Docker-реестров. Некоторые из них являются публичными, в таких реестрах каждый пользователь может видеть и использовать доступные образы. Бывают и приватные реестры. Для простоты поиска к образам можно добавлять теги.
Заключение
Docker – надёжная основа для распределённого развёртывания контейнеров. Благодаря возможности упаковывать компоненты приложения в отдельные контейнеры горизонтальное масштабирование становится довольно простым процессом. Docker предоставляет огромное множество инструментов для создания контейнеров, для управления ими и совместного их использования с другими пользователями или хостами.
В целом, контейнеризованные приложения обеспечивают необходимый уровень изоляции процессов и упрощают их развертывание. Однако существует множество компонентов, необходимых для управления контейнерами и масштабирования в распределенном кластере хостов. Подробную информацию о системах обнаружения сервисов и распределённых хранилищах конфигураций можно прочитать в следующей статье данной серии.