Создание микросервисов в 2019 году и в будущем

Микросервисная архитектура постоянно развивается. Изучите современные рекомендации, чтобы применять ее правильно.

Sten Pittet Sten Pittet

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

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

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

Как создавать микросервисы

Шаг 1. Начните с монолита

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

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

Пользователь Pizzup, который говорит: «Как пользователь, я могу заказать пиццу онлайн».

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

Иллюстрация, показывающая разницу между конечным пользователем и администратором в приложении Pizzup.

То, что в самом начале было простой потребностью, быстро превращается в список возможностей, которые необходимо реализовать.

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

Тем не менее нужно помнить о том, что создание монолита может быстро привести к усложнению кода, который затем будет очень сложно разбить на части. Старайтесь делать все возможное для формирования понятных модулей, чтобы позже их можно было извлечь из монолита. Вы также можете начать с отделения логики от пользовательского веб-интерфейса и предусмотреть его взаимодействие с серверной частью через API RESTful по протоколу HTTP. Это облегчит переход на микросервисную архитектуру в будущем, когда вы начнете переводить некоторые API-ресурсы в другие сервисы.

Шаг 2. Правильно организуйте команды

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

Любая организация, проектирующая какую-либо систему (в широком смысле), предложит в итоге проект, структура которого будет копией коммуникационной структуры организации.

- Conway's Law

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

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

Шаг 3. Разделите монолит, чтобы построить микросервисную архитектуру

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

Обеспечьте простое взаимодействие между сервисами с помощью API RESTful

Если вы еще не используете технологию RESTful API, сейчас самое время применить ее в своей системе. Как советует Мартин Фаулер, нужно иметь «умные точки подключения и глупые каналы». Это означает, что протокол коммуникации между сервисами должен быть максимально простым и отвечать только за передачу данных без какого-либо преобразования. Вся магия будет происходить в адресах подключения: они получают запрос, обрабатывают его и выдают ответ.

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

Разделите свою структуру данных

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

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

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

Выстраивайте микросервисную архитектуру с расчетом на отказ

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

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

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

Усильте мониторинг, чтобы упростить тестирование микросервисов

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

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

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

Используйте непрерывную поставку, чтобы упростить процесс развертывания

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

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

Использование микросервисов — это не спринт

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

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

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

Резюме

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