Каждый модуль-микросервис реализуется и работает как малая полностью независимая система [1], предоставляющая доступ к своей внутренней логике и данным с помощью определенного сетевого интерфейса [2]. Это способствует повышению гибкости ПО, поскольку каждый микросервис является независимой единицей в плане разработки, развертывания, эксплуатации, управления версиями и масштабирования.

Будучи независимыми сервисами с четкими границами, микросервисы похожи на традиционную сервисную архитектуру (service-oriented architecture, SOA) [3]. Можно даже сказать, что микросервисы — это подвид SOA. Но в то время как SOA сильно полагается на специальные средства (сервисную шину предприятия и иное «тяжеловесное» связующее ПО), микросервисы используют исключительно малоресурсоемкие технологии.

Кроме того, SOA обычно ассоциируют с протоколами веб-сервисов, форматами вроде SOAP, WSDL и семейством стандартов WS-*. Микросервисы же обычно полагаются на REST, HTTP и другие менее ресурсоемкие форматы, которые принято считать «нативными» для веб-разработки. Наконец, SOA обычно применяют как интеграционное решение, тогда как микросервисы преимущественно используются для «сборки» индивидуальных приложений.

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

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

Архитектуры микросервисов могут различаться, но все они создаются, чтобы ускорить выпуск — то есть обеспечить максимально быстрое превращение первоначальной идеи в функцию, выполняющуюся в рабочей среде. Во многих организациях в ИТ сегодня видят средство улучшения способности адаптироваться к рыночным изменениям, а уже не центр затрат, которые лучше ограничивать. Для целей повышения адаптивности микросервисы обычно упаковывают и развертывают в облаке с помощью облегченных контейнерных технологий, полагаясь на практики DevOps и полностью автоматизированные механизмы интеграции и выпуска ПО. Таким образом обеспечивается быстрота развертывания микросервисов в различных средах выполнения — например, в среде тестирования, подготовки (staging) и предварительного выпуска (canary release) по произвольным графикам при минимуме централизованного управления.

Что касается термина «масштабируемость», то он неоднозначен. Он может означать масштабируемость системы в период выполнения — например, способность адаптироваться по разумной цене к изменению числа пользователей. Он также может относиться к возможности параллельной разработки системы многими разработчиками.

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

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

Эволюция микросервисов

Термин «микросервисы» впервые обсуждался на семинаре по архитектуре ПО, проходившем в 2011 году [2], а до этого аналогичные идеи уже выдвигались видными участниками отрасли. Например, в Amazon описывали применяемую в компании архитектуру как «инкапсулирующую данные и бизнес-логику для их обработки с доступом только через задокументированный сервисный интерфейс», а в Netflix рассуждали о «слабосвязанной сервисной архитектуре с ограниченными контекстами». Среди других описаний, упоминавшихся в отрасли, — «мелкодисперсная SOA» и «правильно реализованная SOA».

Другими словами, корни микросервисов — в сервисной архитектуре, которая не вполне устраивала рынок, о чем свидетельствует массовый переход от SOAP к REST, менее ресурсоемкому и более простому протоколу вызова сервисов.

В появлении микросервисов сыграли роль несколько важных концепций разработки ПО, особенно проблемно-ориентированное проектирование — разработка на основе моделей и принципов ограниченных контекстов и непрерывной интеграции. Большое влияние также оказали методы проектирования с расчетом на отказы, изоляции данных, автоматизации инфраструктуры, масштабной agile-разработки, формирования кросс-функциональных команд и ответственности за весь жизненный цикл продукта. Эти методы используются для решения многих задач, связанных с разработкой распределенных веб-приложений (Facebook, Spotify и т. д.), а также организационных проблем, с которыми сталкиваются крупные компании.

Рассмотрим эволюцию микросервисов с технической и архитектурной точек зрения.

Технологии

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

На рис. 1 показана хронология появления десяти «волн» развития программных технологий и соответствующих инструментов, которые повлияли на развитие, развертывание и эксплуатацию микросервисных приложений на протяжении последнего десятилетия.

Микросервисы: пройденный путь и дальнейшие цели

Рис. 1. Хронология развития технологий микросервисов

 

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

Третья волна — технологии мониторинга (Graphite, Sensa), обеспечивающие мониторинг и анализ поведения микросервисных ресурсов с разной степенью детализации в период выполнения. Четвертая — технологии оркестровки (Mesos, Kubernetes), которые автоматизируют задачи резервирования и управления контейнерами, абстрагируя низкоуровневую физическую или виртуальную инфраструктуру и тем самым упрощая разработку. Пятая волна — библиотеки функций связи, обеспечивающие устойчивость к отказам и задержке (Finagle, Hystrix); они позволяют сервисам обмениваться данными более эффективно и надежно.

Следующие пять волн появились как реакция на растущую популярность микросервисов. В шестую волну входят технологии непрерывного выпуска (Ansible, Drone), реализующие интеграционные решения для автоматизации процедур DevOps, широко используемых в средах разработки микросервисов. Седьмая волна — технологии «хаос-инжиниринга» (Simian Army, Chaos Toolkit), которые автоматизируют выполнение общесистемных тестов надежности и безопасности, в том числе внесение неиправностей и имитацию атак (attack injection).

Восьмая волна — технологии посредников (Prana, Envoy), которые инкапсулируют функции связи — например, обнаружение сервисов, коммуникационные библиотеки для конкретных протоколов и средства обеспечения отказоустойчивости, — чтобы абстрагировать их для разработчиков. Девятая волна — это технологии «бессерверных» вычислений (AWS Lambda, OpenWhisk), которые реализуют облачную модель функции в виде сервиса. Она дает пользователям возможность разрабатывать, развертывать и запускать в рабочем режиме различные мелкие функции без сложностей, связанных с созданием и управлением инфраструктурными ресурсами, необходимыми для выполнения этих функций (в частности, не нужно принимать мер, связанных с неоднородностью трафика). И наконец, десятая волна — это технологии сервисной mesh-сети (Linkerd, Istio), развивающие идеи посредников (sidecar) и предоставляющие полностью интегрированные средства мониторинга межсервисных коммуникаций и среду управления.

Подавляющее большинство инструментов, представленных на рис. 1, разработано участниками рынка. Единственное исключение — Mesos, система, родившаяся из прототипа, созданного исследователями в Калифорнийском университете Беркли. При этом большинство инструментов разрабатывается в открытых кодах.

Архитектурная точка зрения

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

Рис. 2. Четыре поколения микросервисных архитектур: а — оркестровка контейнеров; б — обнаружение сервисов и отказоустойчивость; в — посредники и сервисная mesh-сеть; г — бессерверная архитектура

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

Для решения этих проблем в архитектурах второго поколения (рис. 2, б) появились сервисы обнаружения и многократно используемые библиотеки отказоустойчивой связи. Сервисы регистрировали предоставляемые функции в единой службе обнаружения, например Consul. Затем клиентские сервисы могли динамически обнаруживать эти функции и обращаться к ним, не указывая местонахождение самих вызываемых сервисов. В процессе вызова все функции, касающиеся протоколов и обработки отказов, возлагались на соответствующую коммуникационную библиотеку, например Finagle. Эта стратегия не только упрощала реализацию и тестирование сервисов, но и позволяла многократно использовать шаблоны коммуникационного кода в различных сервисах.

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

Реакцией стало появление архитектур третьего поколения (рис. 2, в), для которого характерны стандартные сервисные посредники (sidecar) наподобие Envoy. Идея была в том, чтобы расширить возможности многократного использования, инкапсулировав в sidecar все коммуникационные функции и механизмы обнаружения сервисов. Поскольку sidecar — сам по себе полноценный сервис, такое решение сразу же обеспечивало все преимущества независимости существующих библиотек от любого нового языка программирования и таким образом предоставляло больше свободы выбора программистам.

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

Четвертое поколение (рис. 2, в) переносит микросервисные приложения в новую сферу. Идея в том, чтобы использовать современные технологии FaaS (function as a service) и бессерверных вычислений наподобие AWS Lambda с целью еще больше упростить разработку и выпуск микросервисов. В рамках бессерверной архтектуры микросервисные приложения по сути превращаются в набор «эфемерных» функций, каждая из которых может быстро создаваться, обновляться, заменяться и удаляться по мере необходимости.

С переходом на бессерверную архитектуру возникает вопрос нужности коммуникационно-ориентированных технологий наподобие посредников и mesh-сетей. Нынешние платформы FaaS еще не обеспечивают всех функций связи и управления трафиком, но можно представить сценарий, в котором реализуются sidecar-подобные функции, служащие посредниками для всех межфункциональных взаимодействий в бессерверном приложении (рис. 2, г). Высокоуровневая функция управляющей плоскости при этом могла бы выполнять мониторинг и управлять поведением этих посреднических функций, став новым видом сервисной (или функциональной) mesh-сети.

Дальнейшие цели

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

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

Модуляризация и рефакторизация сервисов

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

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

Гранулярность сервисов

Еще одна проблема — отсутствие консенсуса по поводу оптимального размера микросервисов. Хотя даже само их название вроде бы говорит «чем меньше, тем лучше», оценки проектных команд сильно разнятся. В некоторых случаях используются микросервисы размером в единицы строк кода. В других микросервис может инкапсулировать несколько десятков строк кода плюс десятки классов и объектов базы данных. Связь между подобными сервисами среднего масштаба может быть синхронной или асинхронной. А при реализации самодостаточной системы — одного из вариантов микросервисов — рекомендуют по мере возможности включать в сервис пользовательский веб-интерфейс.

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

Интеграция пользовательского интерфейса

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

Модуляризация пользовательских интерфейсов разных типов: веб-интерфейсов, нативных, гибридных — необходима для реализации полного стека микросервисной среды.

Мониторинг и управление ресурсами

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

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

Отказы, восстановление и самокоррекция

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

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

Организационная культура и координация

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

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

Решение проблем

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

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

***

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

Литература

  1. O. Zimmermann. Microservices Tenets: Agile Approach to Service Development and Deployment // Computer Science — Research and Development. — 2017. Vol. 32, N. 3–4. — P. 301–310.
  2. J. Lewis, M. Fowler. Microservices. 25 Mar. 2014. URL: martinfowler.com/articles/microservices.html (дата обращения: 10.08.2018).
  3. N.M. Josuttis. SOA in Practice: The Art of Distributed System Design. — O´ Reilly, 2007.

Пуян Джамшиди ( pjamshid@cs.cmu.edu )  —  научный сотрудник Университета Карнеги  — Меллона,  Джеймс Льюис ( jalewis@thoughtworks.com )  —  главный консультант ThoughtWorks, Клаус Паль ( cpahl@unibz.it )  —  профессор Свободного университета Бозен-Больцано, Набор Мендонча ( nabor@unifor.br )  —  профессор Университета Форталезы, Стефан Тилков ( stefan.tilkov@innoq.com )  —  сооснователь, главный консультант INNOQ.

Pooyan Jamshidi, Claus Pahl, Nabor Mendonca, James Lewis, Stefan Tilkov, Microservices The Journey So Far and Challenges Ahead. IEEE Software, May/June 2018, IEEE Computer Society. All rights reserved. Reprinted with permission.