Переходимо з моноліту на мікросервіси ефективно: власний досвід і план дій
Вітаю! Мене звати Дмитро Соляніченко, я Technical Project Manager у сервісній компанії Glorium Technologies. Оскільки ми працюємо зі стартапами на різних етапах — від валідації ідеї і розробки MVP до запуску на ринок готового продукту, — наше робоче середовище є досить динамічним.
Нещодавно ми з командою закінчили розділяти великий моноліт на мікросервіси. Знаю, що це дуже поширена задача, з якою стикаються багато команд. Тож хочу поділитися практичними висновками, і таким чином «підстелити соломку» тим колегам, хто тільки планує поділ моноліту на мікросервіси.
Першочергове завдання для команди
Німецька real-estate компанія звернулася до нас із запитом перевести їхній десктопний застосунок, розроблений 20 років тому, у веб. Історія типова: часу обмаль, оскільки потрібно було встигнути розробити MVP до важливої виставки. Відповідно, ми з нуля створили моноліт з базовими функціями за шаблоном десктоп-версії. В процесі ми частково використовували бібліотеки клієнта, де зберігалася частина бізнес-логіки. Проте суттєву складову усієї бізнес-логіки застосунку мали надавати ми з нашого боку.
Виставка пройшла успішно, тому нам, знову ж таки, дуже швидко, потрібно було з MVP зробити готовий до запуску продукт, який клієнт почне монетизувати. Після цього ми продовжували працювати над усім новим функціоналом і фічами продукту.
Власне, наш проєкт складався з двох монолітів (API) — основного і субпроєкта. Для вирішення завдання ми обрали стек .NET + Azure DevOps. Однією з початкових технічних вимог клієнта до проєкту була можливість перевикористання коду (щось на зразок конструктора) та гнучкість у сетапі.
Оскільки ми орієнтувалися на підхід data-driven decision, за яким рішення приймаються на основі аналізу масиву інформації, безліч компонентів мали налаштовуватися через базу даних.
Коли почали з’являтися архітектурні проблеми, нарощувати фічі ставало все складніше. Проєкт розрісся, перформанс був далеко не завжди найкращий.
З огляду на всі ці фактори, ми прийшли до рішення — розділити проєкт на мікросервіси. Серед наших цілей було:
- отримати можливість скейлити критичні компоненти (було практично досягнуто максимум можливостей в Azure DevOps з погляду інфраструктури);
- поліпшити швидкодію і спростити підтримку;
- за кожним мікросервісом закріпити окремого Principal Engineer, що відповідатиме за свій репозиторій та контролюватиме стабільність роботи застосунку.
Міграція. З якими проблемами ми стикнулися і як їх вирішували
Зважаючи на початкову складність проєкту, ми з командою і не очікували, що переходити на мікросервісну архітектуру буде просто. Почасти ми були готові до ускладнень й технологічних головоломок. Отже, розкажу про основні виклики, з якими ми зіткнулися в організації міграції, адже це досить типові проблеми для таких випадків.
1. Кросзалежності між елементами архітектури, які складно розділити
Кожен мікросервіс під собою намагався викликати інший мікросервіс, ми не могли розділити, що за чим використовується. Нам довелося провести рефакторинг величезної кількості коду. І головною метою був саме поділ, а спрощення та покращення — завданням другорядним. На це пішла велика кількість зусиль, оскільки через загальний підхід із перевикористанням взаємозв’язки були абсолютно скрізь.
Ми почали розносити всю логіку по NuGet-пакетах. У процесі цього виявилося, що деякі з них попадають у циклічні залежності, і тому проєкт не може піднятися. При цьому відстежити ланцюжки викликів було дуже складно — вони тяглися через
Вирішити цю ситуацію нам вдалося за допомогою написання generic-клієнта — так ми хоч трохи уніфікувати виклики.
2. Кілька ендпоінтів і логіка — поза мікросервісами
Деякі частини неможливо було віднести до жодного з мікросервісів. Такі endpoints ми вирішили виділити в окрему структуру. Ми об’єднали їх у не надто навантаженому мікросервісі і зробили його умовно макросервісом. Надалі ми відділили невелику частину від загального проєкту, створивши окремий мікросервіс.
Чому взагалі так сталося? У проєкті було понад пів мільйона рядків коду. З точки зору техзавдання архітектури все виглядало логічно. Але коли ми почали розбиратися з тим, що на своїй ділянці зробив кожний з команди у 20+ девелоперів, то виявили величезну кількість кросзалежностей.
Однією з причин стало також те, що вони активно використовували наші базові принципи проєкту: що більше елементів, які можуть повторно чи інакше використовуватися, то краще.
3. Семантичні проблеми при тестуванні
Для підвищення якості коду та спрощення перевірок, у тому числі на когнітивну складність, ми застосовуємо автоматичний тул SonarQube. При поділі на мікросервіси нам довелося залазити у старі модулі, які писалися за принципом швидкої видачі у продакшен, і реалізовували складну бізнес-логіку. Очевидно, що при винесенні коду нам довелося зачепити ці модулі й умовно «влити» по-новому.
Згідно з вимогами цього інструменту ми мали переписати код і покращити його краще, що відтягувало термін реалізації десь на пів року чи навіть рік. Вольовим рішенням, ми відключили SonarQube на час рознесення по мікросервісах.
4. Merge-конфлікти
Поділом на мікросервіси ми займалися паралельно з основною розробкою. Та згодом стало зрозуміло, що завершити перехід на мікросервіси швидко, як ми планували, не вийде, і врешті ми виконуватимемо подвійну роботу. Через конфлікти злиття розробку, яка велася паралельно з розділенням на мікросервіси, треба було б переробляти знову.
Щоб не програти самим собі, ми вирішили «вливати» зміни з основного проєкту один раз на два тижні. Це дозволило нам значно заощадити час наприкінці всієї операції при розгортанні.
Lessons learned
З огляду на отриманий досвід, хочу підсумувати, які уроки засвоїла всяк команда під час роботи на цьому проєкті в процесі реалізації міграції. А також наголошу на тому, що, на мою думку, варто було зробити, перш ніж розпочинати перехід:
- виявити очікування клієнта від поділу на мікросервіси — зрозуміти, що це справді потрібно робити на цьому конкретному проєкті;
- провести детальне попереднє дослідження ланцюгових залежностей — займе багато часу архітектора, але дуже спростить майбутню роботу всій команді;
- оцінити, який функціонал і яким чином повинен опрацьовуватись, тобто створити карту переходу;
- зрозуміти, як такий підхід полегшить підтримку проєкту в майбутньому (аби зусилля не виявилися взагалі марними);
- залучити всю команду до поділу і тимчасово зупинити нову розробку;
- помножити естімейти вдвічі, а краще втричі.
Попри допущені помилки і перешкоди, маємо і позитивний досвід. Наприклад, покращився перформанс між сервісами, стало легше керувати певними репозиторіями. Також ми досягли більш гнучкого масштабування інфраструктури (скейлебіліті) і, відповідно, потенційно знизили кост за інфраструктуру для клієнта.
Якщо було корисно, залишайте коментарі та діліться власним досвідом розв’язання різних задач і проблем, що виникають при зміні архітектури.
36 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів