Skip to content

Появление контейнеров

Почему контейнеры пришли на смену ВМ.

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

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

Изоляция и ограничение ресурсов

Одним из методов изоляции служб является использование системного вызова chroot() для изменения корневого каталога на что-либо другое, отличное от фактического системного корня. Программа может изменить свой корневой каталог на, к примеру, каталог /var/spool/my_service и больше не сможет получить доступ к чему-либо за его пределами.

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

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

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

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

  • У них есть свои собственные группы управления.
  • У них есть собственные устройства и файловая система.
  • Они не могут видеть любые другие процессы в системе или взаимодействовать с ними.
  • У них есть собственные сетевые интерфейсы.

Идея разделения процессов и ресурсов впервые возникла еще во времена мейнфреймов, которые изначально были ориентированы на многопользовательский конкурентный доступ в режиме пакетной обработки или в интерактивном режиме как тонкого клиента (терминала). В POSIX-совместимых операционных системах процессы обладали собственным объемом памяти, своим набором файловых дискрипторов (включая стандартные каналы для ввода-вывода информации на терминал) и запускались с правами пользователя, которые разграничивали доступ к файлам и устройствам для процесса. Также для обмена данными между процессами POSIX-совместимая система предлагала механизмы межпроцессного взаимодействия (IPC), а также способы создания вспомогательных процессов и получения от них результата выполнения. Но в реальном использовании эти механизмы успешно способствовали защите данных, но не исключали ситуацию захвата ресурсов (памяти, процессорного времени, сети или дисковой подсистемы) одним приложением, что осложняло работу других пользователей на том же оборудовании. Конечно, POSIX предлагал методы управление приоритетом выполнения заданий (nice), а также лимиты на файловые дескрипторы, стек и выделяемую память, но это не давало необходимой гибкости в распределении системных ресурсов между различными процессами. Кроме того, несмотря на поддержку изоляции файловой системы (механизм jail во FreeBSD или chroot в POSIX-совместимых системах), пространство процессов, IPC и сетевой стек был для всех пользователей единым и это значительно снижало безопасность системы в целом. Одним из решений стала идея пространств имен (namespaces) в Plan 9, которая предлагала делать различное отображение системных ресурсов для разных приложений, которая в дальнейшем получило развитие в Linux Namespaces.

Отчасти эта проблема перестала быть актуальной при появлении персональных компьютеров, но была значима для совместного запуска приложений на сервере. Основная сложность была в необходимости контроля использования ресурсов и изоляции процессов со стороны ядра операционной системы. Некоторые идеи были реализованы в операционных системах реального времени, стали появляться коммерческие реализации механизмов изоляции и ограничения ресурсов (Solaris Zones), но основной акцент был в дальнейшем сделан в сторону аппаратной виртуализации (благодаря возможностям процессоров Intel 486 и более новых), что значительно увеличивало накладные расходы из-за наличия гипервизора, запуска изолированных экземпляров операционной системы, нескольких уровней управления доступом к устройствам ввода-вывода и к оперативной памяти. Одним из решений стало использование паравиртуализации, когда гостевые операционные системы знают о существовании гипервизора и напрямую взаимодействуют с ним (это в большинстве случаев применимо только для одного семейства операционных систем).

Одним из важных шагов в гибком управлении распределением ресурсов стала реализация групп управления (control groups), абстракции для соотнесения процессов с виртуальными группами, каждая из которых могла иметь собственные ограничения на процессорное время, оперативную память, подсистему ввода-вывода, а также свой набор правил доступа к символьным и блочным устройствам системы (включая устройства хранения). Но cgroups не обеспечивают изоляцию ресурсов, поэтому контейнерами активно используются возможности поддержки пространств именования, которые появились в Linux Kernel 2.4.19.


Микросервисная архитектура

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

Ключевые характеристики микросервисной архитектуры:

  • Независимость: Каждый микросервис представляет собой отдельное приложение, которое может быть разработано, развернуто и масштабировано независимо от других сервисов.
  • Ограниченная область ответственности: Каждый микросервис выполняет определенные функции или обслуживает конкретную область бизнес-логики. Это позволяет сосредоточиться на решении конкретных задач.
  • Коммуникация: Микросервисы взаимодействуют друг с другом через сетевые вызовы, обычно по HTTP или с использованием сообщений. Это позволяет создавать более гибкие и расширяемые системы.
  • Индивидуальное развертывание: Благодаря независимости микросервисов, их можно обновлять и развертывать отдельно, минимизируя влияние изменений на всю систему.
  • Легковесность: Микросервисы обычно меньше по размеру и проще в управлении по сравнению с большими монолитами.
  • Легкая масштабируемость: Каждый микросервис может быть масштабирован независимо, в зависимости от нагрузки, что обеспечивает эффективное использование ресурсов.
  • Разнообразие технологий: Разные микросервисы могут быть написаны на разных языках программирования и использовать различные технологии, наиболее подходящие для их задач.

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