Serverless vs монолит: что выбрать в среде AWS?
Serverless vs монолит: что выбрать в среде AWS?
Скажу сразу – оба архитектурных подхода имеют место быть. И монолит и Serverless, конечно, имеют свои плюсы и минусы. Но как подобрать лучший подход для конкретного ИТ-продукта? Я изучил этот вопрос и решил поделиться своими мыслями. Может быть, получится сэкономить кому-то время в поисках лучшего решения.
Меня зовут Василий Широченко, я backend-разработчик в Konig Labs. Когда я в очередной раз столкнулся с темой выбора архитектурного подхода для приложения в среде Amazon Web Services (про это тоже поговорим), я работал над мобильным приложением для обучения игре на фортепиано. Так что, все выводы в статье будут основаны на реальной практике. Поехали!
Начинаем с требований
Для начала, давайте разберемся, почему вопрос архитектуры так важен. Планирование архитектуры ИТ-продукта всегда проводится перед стартом разработки. Это критически важно, т.к. как и архитектура здания, архитектура ИТ-продукта не должна “посыпаться” при малейших изменениях, и в перспективе должна выдержать масштабирование, увеличение нагрузки и возрастающую сложность. Стоимость внесения изменений при этом должна оставаться адекватной.
Также, верно подобранная архитектура – гарантия того, что реализация продукта будет учитывать нюансы всех требований (бизнесовых, нефункциональных, процессных и т.д.).
Теперь немного контекста о проекте, на примере которого будем рассуждать об архитектуре, и его предметной области. Заказчик поставил задачу разработать мобильное приложение, которым люди, интересующиеся игрой на фортепиано, могут пользоваться на платформах iOS и Android. Приложение должно уметь сканировать с листа произведения, записанные в классической музыкальной нотации, и далее распознавать нотный стан, сами ноты, знаки альтерации и т.д. (рис. 1, рис. 2).
Затем, на основании т.н. эталонной записи, пользователь исполняет свой вариант музыкального произведения на фортепиано. После игры система производит поиск ошибок и неточностей в игре учащегося (пользователя). Для каждого пользователя хранится история его попыток, показывается процент точности попадания. Также есть экран статистики в разрезе дня/недели/месяца/года. В принципе, получается несложная предметная область.
Конечно, мы понимаем, что приложение само по себе не будет работать, потому как где-то необходимо сохранять прогресс их игры, статистику и другие важные данные. По этим причинам необходимо разработать backend-часть для приложения.
Про выбор архитектуры
Как я уже говорил, мы будем сравнивать монолитный и Serverless подходы. На самом деле, еще затронем микросервисный, но вскользь. Далее поймете, почему.
Сперва предлагаю кратко пройтись по всем трем подходам, посмотреть на “профпригодность” каждого из них и уже более детально рассмотреть то, что мы выбрали.
Монолитное решение
Кратко напомним, что подразумевается под монолитным решением. Прежде всего, все слои приложения находятся в рамках единой кодовой базы, и, как следствие, публикация такого решения производится не частично (например, поменяли только визуальную составляющую), а целиком на все приложение.
Так исторически сложилось, и более того, подтверждено различными опросами среди разработчиков, что монолитная архитектура – самый распространенный способ построения программного обеспечения. Почему так? Все благодаря своей простоте в разработке и поддержке по сравнению с более сложными подходами (если рассматривать те же микросервисы). Простота заключается в едином развертывании, когда у нас бизнес-логика всего приложения, UI (то, что видит конечный пользователь), хранение данных (база данных) и возможно другие дополнительные компоненты находятся в общем репозитории, разрабатываются совместно и публикуются на сервер в рамках одного процесса.
Конечно, у любого решения есть и минусы. Эволюция любого приложения – довольно непредсказуемый процесс, потому как появляются новые требования от заказчика. Эти требования необходимо стараться как-то “вписать” в существующее рабочее решение. В случае с монолитом, у разработчиков очень часто появляется соблазн нарушить определенные рамки и конвенции, разработанные на этапе проектирования, вследствие чего система начинает сильно вязнуть, и вносить все новые и новые изменения становится труднее.
Микросервисы
Архитектура, построенная на основе микросервисов – подход, набравший огромную популярность последние лет десять. Весьма распространен в больших проектах с долгим жизненным циклом, когда разработкой занимается несколько команд, каждая из которых отвечает за свои несколько микросервисов. В нашем блоге уже была статья про микросервисы, если интересно вникнуть в детали, читайте здесь: Микросервисы и CQRS: оптимизируем затраты на продукт.
Среди явных плюсов: система разбита на модули, каждый из которых поддерживается определенной командой разработки, независимые публикации отдельно взятых микросервисов (допустим, в системе развернуто два микросервиса A и B, мы можем обновить микросервис B, не затронув при этом микросервис A). В то же время, приходится мириться с определенными ограничениями и иметь в виду, например, что для качественной разработки требуется высокая квалификация специалистов. Плюс ко всему, не стоит забывать и о том, что, несмотря на кажущуюся гибкость в разделении бизнес-процессов на так называемые ограниченные контексты, стоит обязательно позаботиться о системе логирования и мониторинга (о записи действий приложения в специальный файл и о сборе, хранении метрик по инфраструктуре приложения). Более того, микросервисы должны уметь общаться друг с другом – реализуется это посредством различных протоколов, таких как http, amqp и других. Т.е., в отличие от монолита, здесь дополнительно нужно продумать и реализовать API.
Serverless
Serverless – данный подход стал применяться сравнительно недавно (последние несколько лет). Его суть в том, что облачный провайдер (по примеру AWS, Azure и др.) предлагает воспользоваться т. н. бессерверными вычислениями – пользователям теперь не надо заботиться о конфигурировании и поддержке сервера – за вас все делает провайдер (сколько ресурсов потребуется – столько провайдер и предоставит).
Этот подход достаточно новый. С одной стороны, простота – главное его преимущество перед остальными подходами. Здесь мы имеем в виду удобство работы с инфраструктурой (сервисы, службы, логирование, мониторинг, вертикальное и горизонтальное масштабирование).
С другой стороны, есть палка о двух концах – это управление бюджетом. Провайдер предлагает определенные расценки за использование ресурсов, которые можно посчитать в калькуляторе на сайте. Вот только есть нюанс динамически выделяемых ресурсов – к этому вопросу стоит относиться крайне внимательно – об этом будет рассказано более подробно ниже. Так что советуем не закрывать статью, вдруг дальше будет написано, где находится Янтарная комната… 🙂
Наши мысли
Итак, что мы имеем? Монолит как самый популярный вариант, микросервисы, как пример слабо связанной архитектуры и serverless – эдакий многообещающий вариант построения приложений. Ах, да, чуть не забыли – команду backend-разработчиков из двух человек 🙂
Посовещавшись, пришли к выводу, что микросервисы мы отметаем по причине того, что бизнес-модель не является сложной (даже если забегать вперед с возможными дополнениями) в связи с четко поставленными требованиями к конечному продукту для пользователей. Да, зарекаться не стоит, но взвесив все “за” и “против” решили оставить для более детального рассмотрения два варианта: монолитное решение и serverless.
Использование монолита и serverless: детально
Прежде всего, для наглядности приведем сводную таблицу со списком основных сервисов, которые предоставляет AWS. Каждый из этих сервисов может быть использован в любом из предложенных архитектурных решений. Почему AWS – спросите вы? Все дело в том, что во-первых, заказчик был заинтересован в использовании инфраструктуры AWS, во-вторых, действительно AWS является лидером среди облачных провайдеров во всем мире.
Рис. 3. Сервисы AWS, необходимые в backend-части
Позже мы более детально посмотрим на их использование и какое место каждый из этих элементов занимает в построении архитектуры приложения.
Монолитное решение
Инфраструктурный вопрос
Рис. 4. Предложенный вариант архитектуры backend’а, построенной на базе монолита
Из схемы на рис. 4 видно, что в системе есть мобильный клиент. Далее из мобильного приложения отправляется запрос в облачную инфраструктуру AWS, а конкретно,в виртуальную машину Ec2 instance. Запрос первым делом попадает на сервер nginx, который отвечает за перенаправление запроса в наше приложение. Что собой представляет приложение?
Это приложение, написанное на Python, со всеми необходимыми нам endpoint’ами, бизнес-логикой и т.д., запущенное и работающее в Docker-контейнере. Все остальные необходимые сервисы – это просто компоненты, созданные однажды. Хранилище файлов можно удобным образом выделить в S3. В качестве сервера БД выступает RDS (Relational Database Service). Так как в системе предусмотрена отправка email-уведомлений, то можно использовать либо Sendgrid, как на схеме, либо AWS SES (Simple Email Service).
P.S. Забегая вперед, хочется отметить, что на схеме не учтен момент с алгоритмом нахождения ошибок в игре пользователя (SMR). Предполагалось, что этот блок будет являться частью API, но после замеров нагрузки стало ясно, что такие тяжелые вычисления необходимо выносить в специальный сервис. Поэтому дальше в оценке можно будет увидеть этот дополнительный пункт.
Оценка трудозатрат на разработку (с учетом на одного разработчика)
Рис. 5. Трудозатраты на разработку монолитного решения
Пока что эти цифры нам ни о чем не говорят, чуть позже сравним их со значениями для serverless-решения.
Затраты на хостинг
* Исходим из расчета на 1000 активных пользователей системы
Рис. 6. Затраты на хостинг
Итого: ~ 265$ / месяц
Плюсы
- Традиционная кодовая база. Можем сменить хоста в любой момент и практически ничего менять не придется
- Упрощенная схема инфраструктуры (общий сервер почти для всего, если не используем выделенные сегменты инфраструктуры)
- Ec2 запущен в режиме 24/7, поэтому нет проблемы «холодного старта»
- Довольно предсказуемая ценовая политика, что весьма важно!
Минусы
- Повышенная ответственность со стороны разработки: настройка веб-сервера nginx, настройка логирования, настройка мониторинга сервисов приложения. Плюс необходима соответствующая квалификация разработчиков.
- Масштабирование. Нет гарантии, что однажды не упремся в пороговое значение. Для горизонтального масштабирования необходима настройка, что само по себе непросто (стоимость будет увеличиваться в разы).
- Если захотелось бесплатно поднять БД, то придется настроить бэкапы и мониторинг самостоятельно. Это сложнее, чем просто «выставить галочку» в RDS «мне нужны бэкапы каждый день в 24:00», а значит и стоимость работы все равно будет недешевой.
- При изменении чего-либо в конфигурации сервера не всегда получается понять, что пошло не так
- Авторизацию в серверной части, например, придется реализовывать самостоятельно
Serverless
Инфраструктурный вопрос
Рис. 7. Архитектура serverless backend’а приложения
В данном случае система выглядит немного интересней 🙂
Нет привычных nginx’а, Docker’а, вместо них появились какие-то Lambda с нумерацией от 1 до N, Step Function, Cognito… На самом деле все не очень сложно. Если вспомнить, что serverless тесно связано с понятием FaaS, то можно увидеть, что вместо единого API у нас получились разрозненные lambda-функции, каждая из которых отвечает за свой endpoint. Например, чтобы создать пользователя в системе (выполнить POST-запрос /users) мы вызываем определенную lambda-функцию, задача которой создавать пользователей – ничего более!
Тот блок, который помечен как Step Function – долгая задача, выполняющаяся асинхронно (дело в том, что мы не можем блокировать выполнение http-запроса до момента получения результата, потому как алгоритм нахождения ошибок может выполняться десятки секунд, а пользователь не очень хочет видеть заблокированный экран приложения).
Cognito – мощный сервис авторизации пользователей в системе.
Оценка трудозатрат на разработку (с учетом одного разработчика)
Рис. 8. Трудозатраты на разработку
Чуть позже сравним 🙂
Затраты на хостинг
* Исходим из расчета на 1000 активных пользователей системы
Рис. 9. Затраты на хостинг – serverless
Итого: ~ 250$ / месяц
Плюсы
- Подход развертывания конфигурации через template.yaml, который, имеет очевидные достоинства в виде четко структурированного файла с описанием AWS инфраструктуры + любые изменения отслеживаются Git’ом (можно выявить проблемы, связанные с изменением конфигурации) + хорошо документировано на официальном сайте.
- Lambda-функции являются изолированными, удобно тестируются, для каждой из них создана своя группа для мониторинга.
- Cloud Watch – очень удобный инструмент для сбора логов и мониторинга сервисов AWS. Для Python есть мощная библиотека boto3, которая позволяет выполнять любые операции с сервисами AWS.
- Удобный CI/CD (с использованием environment-переменных): dev-stack/prod-stack/etc.
- Мы не заботимся об обслуживании lambda-функций, мы лишь пишем код и точка. Хотя нет, не точка – еще платим деньги, сколько – не известно – вот такая простая математика, все зависит от ситуации. Тогда почему этот пункт в плюсах?..
Минусы
- Высокий порог вхождения при отсутствии опыта.
- «Холодный старт» у lambda-функций – специфическое ограничение, для нашего проекта некритично.
- Грамотная настройка IAM-политик и их полное соблюдение требуют соответствующей квалификации специалистов, что есть не всегда.
- Кодовая база достаточно плотно завязана на AWS-провайдере.
- Зачастую, невозможно предсказать нагрузку и предотвратить автомасштабирование (пример – черная пятница), что ведет к непредсказуемым затратам.
Подведение итогов
Рис. 10. Сравнение стоимостей
Итак, как можно видеть из таблицы (рис. 10), стоимость инфраструктуры не сильно разнится, если смотреть со стороны стоимости разработки. Зато стоимость работ оценена практически в два раза больше. Это весомый аргумент в пользу монолитного решения. Плюс ко всему, как говорилось ранее, это и простота, и меньший порог вхождения и т.д. Тем не менее, есть несколько пунктов, которые перевесили наше решение в сторону выбора serverless:
- Заказчик имеет положительный опыт разработки с использованием инфраструктуры AWS и настаивает на serverless;
- решение выглядит масштабируемым, т. е. со стороны разработки «практически» не потребуется ничего предпринимать с ростом нагрузки. Те затраты, которые понес заказчик в начале, с лихвой окупятся при масштабировании. А это значительный плюс по сравнению с монолитным решением;
- SAM – об этом будет рассказано более подробно в следующей в статье;
- Со стороны команды разработки, действительно хотелось опробовать современный подход в разработке, поэтому остановились на serverless 🙂