(java || kotlin) && devOps
366 subscribers
6 photos
1 video
6 files
307 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Всем привет!

Поговорим про микросервисы. Статей будет несколько, от плюсов и минусов до саги и прочих паттернов.
Для начала что такое микросервис. Я дам определение в виде набора обязательных признаков. Обязательны все из них.
1) одна команда
2) отдельная кодовая база
3) отдельная БД
4) отдельный pipeline
5) отдельный релизный цикл
6) небольшой объем кода
7) взаимодействие с другими сервисами через межпроцессное API. REST, gRPC, GraphQL, Kafka - это то, что сейчас на слуху, на самом деле технологий больше.

Для начала рассмотрим плюсы:
1) один сервис разрабатывает одна команда. При этом одна команда может делать несколько приложений, но главное здесь - у одного приложения один хозяин, определяющий вектор его развития. Такие ключевые решения, как: язык, на котором написан код, используемые библиотеки, утилита для сборки и архитектура в конце концов) Проще договорится, меньше согласований и зависимостей от внешних людей или команд. Этого же результата помогает достичь отдельный релизный цикл. Хотя в случае enterprise микросервисов зависимостей не избежать и это боль( Еще одно замечание - микросервис может релизиться независимо при условии использования фича-тогглов https://martinfowler.com/articles/feature-toggles.html и поддержки совместимости API. Альтернатива фича тогглов - писать код так, чтобы его неготовый кусок невозможно было вызывать снаружи до момента полной готовности вас и смежников.
2) МИКРОсервис означает, что кода не должно быть много. Сколько тысяч строк - явно не миллион и не сто тысяч, скорее несколько тысяч. А раз кода не много - в него проще погрузится. Меньше кривая обучения для новых членов команды. А если ты в целом понимаешь как работает приложение - с большей вероятностью доработки и рефакторинг будут сохранять его исходную архитектуру или менять ее единообразно. Как результат сервис будет оставаться понятным и далее.
3) проект быстрее компилируется и запускается, быстрее прогоняются тесты - быстрее обратная связь, быстрее процесс разработки. Проще делать маленькие Pull Request, ревьюверы меньше матерятся, т.к. не видят 100+ файлов в Pull Request, проще слияния. Вообще писать код нужно так, чтобы merge конфликтов не было, но это идеал, к которому надо стремится)
4) DevOps инструменты и практики будут работать "из коробки". На проектах с сотнями тысяч строк pipeline будет падать. И падать достаточно часто. SonarQube не может определить дельту Pull Request и падает. Checkmarx нужно дорабатывать у вендора, чтобы не падал. Джобы сборки оптимизировать, параллелить, чтобы добиться приемлемого времени сборки. Интеграционные тесты падают, т.к. контекст, необходимый для поднятия приложения, постоянно растет. База становится такой сложной, что DBA наотрез отказываются от Liquibase и накатывают скрипты руками пошагово. Число интеграций такое, что набор примочных тестов запущенный автоматически с большой вероятностью упадет и встанет вопрос - а точно нам нужны автотесты? НТшники работают по ночам. Bitbucket дико тормозит, когда размер репозитория превышает 2 Тб, а это вполне реально - ведь хранится вся история изменений. Да и в принципе по статистике чем дольше сборка и deploy, тем больше вероятность, что на jenkins slave кончится место или память и сборка упадет. И это все - только примеры из моего опыта. А суть в следующем: инструменты DevOps имеют свои ограничения по объему данных и нагрузке, и нужно либо допиливать их напильником, либо не делать таких больших монолитов. Второй путь проще)
5) если микросервис не оправдал ожиданий - его можно выкинуть и переписать. Я не говорю, что это типовая практика, я говорю, что такая опция есть. Решение принимается на уровне команды\отдела\кластера, а не вице-президента по ИТ в случае монолита)

Резюмирую - если кода меньше, его проще выводить в ПРОМ, поддерживать и если надо выкинуть.
О минусах - в следующем посте.

#microservices #сравнение
Всем привет!

Пару заблуждений про микросервисы.
1) Облачные технологии (k8s) не равно микросервисам. Да, если вы начинаете проект с микросервисами с нуля и есть возможность развернуть его в облаке - так и нужно делать.
Облако упрощает развертывание и масштабирование микросервисов, добавляет отказоустойчивость. Но микросервисы - это архитектура. А k8s - технологии.
Аналогия - REST обычно используют с JSON, для того же Spring RestController это настройка по умолчанию. Но никто не мешает в REST API возвращать XML.
Как я писал ранее: по большому счету микросервис - это про сознательное ограничение объема кода\функционала и связанные с этим плюшки.
2) трушный микросервис не обязан реализовывать строго одну операцию. Как именно выделять микросервисы - отдельная тема. Базовые варианты: по функционалу, по архитектурному слою - например, слой хранения - или по требованиям к масштабированию.
Но в любом из этих случаев в API, которое выставляет сервис, может быть несколько логически связанных методов. Ключевое слове здесь - логически связанных.

Если я что-то упустил, есть вопросы или возражения - пишите.

#microservices
Всем привет!

И снова микросервисы.
Из предыдущих постов видно, что разработчикам и команде в целом проще работать с микросервисами. Компании, которой принадлежит продукт, тоже, т.к. снижаются риски. Неужели так все радужно? Не совсем)
И основные трудности ложатся на плечи сопровождения и третьей линии поддержки. Причина проста - увеличивается число единиц деплоймента, увеличивается число интеграций и, следовательно, зависимостей между сервисами. Сложнее разворачивать, т.к. надо учитывать зависимости, сложнее разбирать ошибки, т.к. недостаточно посмотреть логи одного приложения как это было с монолитом. Также увеличивается риск каскадных сбоев, когда из-за сбоя одного ключевого сервиса "подвисают" его входящие сетевые соединения, переполняются пулы потоков и все падает большое количество серверов.
Что тут можно сделать?
1) с увеличением числа единиц деплоймента сделать ничего нельзя, но упросить сам процесс деплоя позволяет k8s
2) проблему сложных зависимостей между микросервисами надо решать через поддержку совместимости и версионирование API и фича тогглы. Т.е. поставщик данных при любых изменениях версионирует API какое-то время сохраняя старую версию, а потребитель включает новую фичу для клиентов по рубильнику по готовности поставщика. Это позволит избежать синхронных внедрений нескольких сервисов. Синхронные внедрения - "зло", я об этом уже писал: https://t.me/javaKotlinDevOps/24 Решение звучит просто, а реализуется исходя из моего опыта сложно) Как его добиться? Только дисциплиной, серебряной пули нет. Ну и учитывая версионирование API при проектировании сервиса, а также достаточным набором модульных и приемочных тестов чтобы не поломать API. Как определить достаточность? Очень просто: если есть чувство уверенности, что раз тесты прошли, то API не сломали - значит набор достаточен))) Для того, чтобы потребитель и поставщик одинаково представляли себе новую версию API она обязательно должна храниться в системе контроля версий (VCS). А убедиться в совместимости позволяет технология Consumer Driven Contract (CDC) https://martinfowler.com/articles/consumerDrivenContracts.html Самые известные фреймворки ее реализующие - Spring Cloud Contract и Pact.
3) проблему разбора багов и инцидентов помогают решить централизованный сбор логов и метрик, а главное - распределенная трассировка. Это технология для отслеживания всей цепочки запросов, включая список сервисов, через который прошел запрос, тайминги и информацию о возникающих ошибках. Для трассировки есть стандарт - OpenTelemetry https://opentelemetry.io/docs/concepts/what-is-opentelemetry/. Вот неплохое видео, описывающее архитектуру трассировки в целом и сравнивающее две его самые известные реализации - Zipkin и Jaeger. https://youtu.be/6PiThk3QHWw?t=4191 См. вторую часть видео, ссылка указывает на нее. А вот статья, описывающая проблемы текущих решений - https://habr.com/ru/company/flant/blog/460365/
4) решить проблему каскадных сбоев позволяют ограниченные и согласованные таймауты и реализация circuit breakers https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker Согласованность таймаутов можно обеспечить в ручном режиме, а можно передачей значений таймаута в API от клиента к серверу. Надо сказать, что данные паттерны важны и для монолита, но для микросервисов из-за увеличения числа интеграций их сложнее контролировать. Паттерны можно реализовать на клиенте, а можно с помощью Service Mesh, например, Istio - https://istio.io/latest/docs/concepts/traffic-management/#network-resilience-and-testing Последний вариант дает возможность централизованного контроля за настройкой таймаутов и circuit breaker.

#microservices #сравнение
Всем привет!

Всегда ли нужно распиливать систему на микросервисы? Не всегда.
Я вижу как минимум один кейс.

Если выполняется хотя бы одно условие:
1) у вас одна команда
2) сроки ограничены
3) деньги ограничены
3) речь про стартап
4) создается приложение для проверки гипотезы
- стоит рассмотреть вариант с монолитом.
Плюсы - потребуется меньше железа, проще настраивать пайплайн и инфраструктуру. В простейшем случае можно весь ПРОМ развернуть на одном сервере. В случае стартапа или проверки гипотезы когда ещё не ясно как правильно разделить систему на независимые контексты ещё не понятно - в случае монолита ее проще рефакторить. Пока он маленький)

Может возникнуть вопрос - а как дальше масштабировать? Ответ - делите систему на уровне модулей Maven\Gradle, контролируйте зависимости между уровнями приложения, избегайте циклических ссылок. Для всего этого есть утилиты, напишу о них далее. Ну и конечно нужен набор приемочных тестов, его можно реализовать в период разделения на микросервисы, запуская вначале на "монолите", потом на микросервисах. С точки зрения БД - храните данные в разных схемах или хотя бы таблицах. Первый вариант лучше и одновременно хуже тем, что между схемами невозможны транзакции. Проектирование с использованием DDD - Domain driven development - в частности концепция агрегата, тоже хороший инструмент для создания разделяемого монолита. Тогда из этих модулей можно будет в будущем сделать микросервисы. Да, в любом случае при разделении потребуется дополнительная работа, но зато значительно увеличивается время вывода в ПРОМ и получение обратной связи. А дополнительную работу предлагаю рассматривать как рефакторинг.

Какие риски у этого подхода - по большому счету один. Если маленький монолит, который задумывался временным решением, превратится в большой и станет постоянным. Тогда в какой-то момент технологии устареют, части монолита срастутся сильнее и возникнет вопрос - а стоит ли его вообще разделять? Или проще переписать заново)))

И главное, что стоит запомнить - у каждого микросервиса должна быть веская причина существования. Если есть сомнения - нужен ли еще микросервис, не надо его выделять. Микросервис - далека не бесплатная штука в плане разработки и поддержки

#microservices #сравнение
Всем привет!

Поговорим про разделение функционала между микросервисами.

Для начала я бы разделил два случая - распил монолита или создание нового приложения.

При распиле есть несколько путей, которые можно комбинировать:
1) отделять в первую очередь менее значительный функционал. Так можно будет набить шишки, настроить DevOps, инфраструктуру, а потом уже переходить к чему-то серьёзному
2) отделять функционал, который легче выделить. Например, он уже компилируется в отдельный war/ear, хотя и имеет общий релизный цикл с монолитом. Или он правильно спроектирован https://t.me/javaKotlinDevOps/59 и его отделение требует минимальных усилий.
3) делать новые фичи на микросервисах, закрывая монолит API Gateway для единообразия API.
Все это называется шаблоном Душитель https://microservices.io/patterns/refactoring/strangler-application.html Душим мы как вы понимаете монолит)

Если же мы делаем новый функционал - тоже есть разные варианты, размещу их в порядке приоритета
1) разделение по бизнес фичам. Где фича - это агрегат в терминах DDD. Или ресурс в REST. Как я уже говорил ранее - не операция, это слишком мелко https://t.me/javaKotlinDevOps/59
2) разделение по слоям. Например, можно выделить в отдельный сервис сохранение данных. Или шлюз к смежной системе
3) разделение по планируемой нагрузке. Я бы его применял как дополнительный фактор к первым двум. Более нагруженные сервисы выделяем отдельно.

Еще один важный фактор: возможность переиспользования сервиса. Имеет значение и при распиле монолита, и при разработке новых фичей. Пример: отправка уведомлений пользователю по-хорошему стандартизирована и такой сервис смогут переиспользовать все создаваемые микросервисы. Способствуем выполнению принципа DRY - https://ru.wikipedia.org/wiki/Don’t_repeat_yourself

И напоследок хотел бы напомнить простую истину - как нет серебряной пули или единственно верной архитектуры, так нет канонического способа разделения на микросервисы)

#microservices #сравнение
Всем привет!

На какие грабли можно наступить при распиле монолита на микросервисы?
Небольшой дисклеймер: я подразумеваю, что внедряются микросервисы + Agile, т.к по определению у микросервиса должен быть владелец - команда.

Примеры взяты из жизни)

1) Как я уже говорил https://t.me/javaKotlinDevOps/60 - начинать распил лучше с каких-то небольших частей существующего монолита или новых фичей для этого монолита. Тут важен вот какой момент - скорее всего монолит выполняет некую критическую функцию для компании и, следовательно, для него существуют некие требования. Например, 99,99-ая доступность. И первые "тренировочные" микросервисы не стоит делать для другого сервиса=приложения=монолита, например, потому, что у него более лояльные пользователи или там проще внедрение. Если так сделать, то при переходе от простого монолита к сложному появится куча неожиданных требований по надежности, масштабируемости, ИБ, удобству сопровождения, и архитектуру придется переделывать.

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

3) нужно время на обучение разработчиков, тестировщиков и сопровождения. Обучение может быть и должно быть на практике - на реальных сервисах, но оно должно быть учтено в roadmap перехода. Сроки перехода должны быть согласованы командами разработки и сопровождения, а не спущены сверху. Звучит очевидно, но по моему опыту не для всех)

4) нужно изменение релизных и контролирующих политик в компании, иначе Lead Time и сложность вывода в ПРОМ для микросервисов останется на уровне монолита. Это сложный момент, т.к. люди консервативны, они привыкают к правилам, и не всегда осознают, что цена ошибки в монолите и в микросервисе разная. Особенно консервативно сопровождение и DBA, работа у них такая, цена ошибки высока) С другой стороны, конечно же микросервисная система должна быть спроектирована так, чтобы падение одного сервиса не приводило к каскадным падениям других. Это снова про критичное влияние архитектуры.
Еще важный момент - в монолите процесс согласования был централизован, им занимались специально обученные люди. С микросервисами релизный процесс попадает в Agile команды. А правило 7 +-2 действует не только для людей, а и для команд. Ну и большое число внешних взаимодействий противоречит Agile-у.

5) к началу разработки нужен работающий и простой DevOps. Разрабочики монолита как правило занимаются только разработкой и если заставить их учиться DevOps - потеряем время. Даже если набирать новых людей - все равно не каждый разработчик готов разрабатывать пайплайны. Т.е. должна быть минимальная конфигурация pipeline в декларативном стиле и работающие CI и CD pipeline. Опять же релизы микросервисов происходят намного чаще, для них это важно.
Также не должно быть невнятных инструкций и десятков настроечных файлов, необходимых для работы pipeline. Конфигурация - это не код, писать его не интересно, разбираться в ней тем более. И это не DevOps на самом деле) Должны быть - генераторы конфигураций, принцип convention over configuration.

6) микросервисам нужно больше инфраструктуры по сравнению с монолитом: сервера, репозитории - и больше доступов. Если в монолите как правило получением инфраструктуры занималась выделенная команда(ы), то при микросервисной архитекторе этим занимается каждая команда. Следовательно, процесс выдачи должен быть максимально автоматизирован и упрощен. Снова работает правило 7+-2. Разработчики должны заниматься тем, что у них лучше всего получается, а не заявками.

#microservices #fuckup #conv_over_conf