Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

У гэтым артыкуле я раскажу пра тое, як праект, у якім я працую, ператвараўся з вялікага маналіта ў набор мікрасэрвісаў.

Праект пачаў сваю гісторыю даўнавата, у пачатку 2000. Першыя версіі былі напісаныя на Visual Basic 6. З цягам часу стала зразумела, што распрацоўку на гэтай мове ў будучыні будзе складана падтрымліваць, бо IDE і сама мова развіваюцца слаба. У канцы 2000-х было вырашана пераходзіць на больш перспектыўны C#. Новая версія пісалася паралельна з дапрацоўкай старой, паступова ўсё больш кода было на. NET. Backend на C# першапачаткова арыентаваўся на сэрвісную архітэктуру, аднак пры распрацоўцы выкарыстоўваліся агульныя бібліятэкі з логікай, ды і запускаліся сэрвісы ў адзіным працэсе. Атрымалася дадатак, якое мы называлі "сэрвісны маналіт".

Адным з нямногіх пераваг такой звязкі з'яўлялася магчымасць сэрвісаў выклікаць адзін аднаго праз вонкавы API. Былі відавочныя перадумовы да пераходу на больш правільную сэрвісную, а ў перспектыве і мікрасэрвісную архітэктуру.

Сваю працу па дэкампазіцыі мы пачалі прыкладна ў 2015 годзе. Пакуль яшчэ мы не дасягнулі ідэальнага стану – засталіся часткі вялікага праекту, якія ўжо цяжка назваць маналітамі, але і на мікрасэрвісы яны не падобныя. Тым не менш, прагрэс істотны.
Пра яго я і раскажу ў артыкуле.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

Змест

Архітэктура і праблемы існуючага рашэння


Першапачаткова архітэктура выглядала наступным чынам: UI - асобнае прыкладанне, маналітная частка напісана на Visual Basic 6, дадатак на. NET ўяўляла сабой набор звязаных сэрвісаў, які працуе з досыць вялікай базай дадзеных.

Недахопы ранейшага рашэння

Адзіная кропка адмовы
У нас была адзіная кропка адмовы: дадатак на. NET запускалася ў адным працэсе. Калі ў якім-небудзь з модуляў адбываўся збой, адмаўляла ўсё прыкладанне, і яго даводзілася перазапускаць. Бо ў нас аўтаматызуецца вялікая колькасць працэсаў для розных карыстачоў, з-за збою ў адным з іх некаторы час працаваць не маглі ўсё. А пры праграмнай памылцы не дапамагала і рэзерваванне.

Чарга дапрацовак
Гэты недахоп хутчэй за арганізацыйны. У нашым дадатку мноства заказчыкаў, і ўсе яны хочуць дапрацаваць яго як мага хутчэй. Раней зрабіць гэта паралельна было немагчыма, і ўсе замоўцы ўставалі ў чаргу. Гэты працэс выклікаў негатыў у бізнэсу, бо ім трэба было давесці, што іх задача нясе каштоўнасць. А каманда распрацоўкі марнавала час на тое, каб гэтую чаргу арганізаваць. Гэта адымала шмат часу і сіл, а прадукт у выніку не мог мяняцца так хутка, як гэтага б ад яго хацелі.

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

Цяжка ўкараняць сучасныя тэхналогіі
Знаёмая ўсім распрацоўшчыкам праблема: ёсць жаданне ўкараніць у праект сучасныя тэхналогіі, але няма магчымасці. Пры вялікім маналітным рашэнні любое абнаўленне бягучай бібліятэкі, не кажучы аб пераходзе на новую, ператвараецца ў дастаткова нетрывіяльную задачу. Трэба доўга даказваць тымлід, што гэта прынясе больш бонусаў, чым выдаткаваных нерваў.

Складанасць выдачы змен
Гэта была самая сур'ёзная праблема - мы выдаваліся рэлізамі кожныя два месяцы.
Кожны рэліз ператвараўся ў сапраўдную катастрофу для банка, нягледзячы на ​​тэсціраванне і намаганні распрацоўшчыкаў. Бізнэс разумеў, што ў яго ў пачатку тыдня не будзе працаваць частка функцыянальнасці. А распрацоўшчыкі разумелі, што іх чакае тыдзень сур'ёзных інцыдэнтаў.
Жаданне змяніць сітуацыю было ва ўсіх.

Чаканні ад мікрасэрвісаў


Выдача кампанентаў па гатоўнасці. Выдача кампанентаў па меры гатоўнасці дзякуючы дэкампазіцыі рашэння і аддзяленні розных працэсаў.

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

Ізаляцыя сэрвісаў у асобных працэсах. У ідэале жадалася ізаляваць у кантэйнерах, але вялікая колькасць сэрвісаў, напісаных на .NET Framework, запускаецца толькі пад Windows. Цяпер з'яўляюцца сэрвісы на. NET Core, але іх пакуль мала.

Гнуткасць разгортвання. Жадалася бы камбінаваць сэрвісы так, як гэта неабходна нам, а не так, як прымушае код.

Выкарыстанне новых тэхналогій. Гэта цікава любому праграмісту.

Праблемы пераходу


Вядома, калі б разбіць маналіт на мікрасэрвісы было проста, пра гэта не трэба было б размаўляць на канферэнцыях і пісаць артыкулы. У гэтым працэсе шмат падводных камянёў, апішу асноўныя, якія мяшалі нам.

першая праблема тыповая для большасці маналітаў: складнасць бізнес-логікі. Калі мы пішам маналіт, то жадаем перавыкарыстоўваць нашы класы, каб не пісаць лішні код. А пры пераходзе на мікрасэрвісы гэта становіцца праблемай: увесь код дастаткова жорстка звязаны, і складана падзяліць сэрвісы.

На момант пачатку работ у рэпазітары было больш за 500 праектаў і больш за 700 тыс. радкоў кода. Гэта дастаткова вялікае рашэнне і другая праблема. Проста ўзяць і падзяліць яго на мікрасэрвісы не ўяўлялася магчымым.

Трэцяя праблема - Адсутнасць неабходнай інфраструктуры. Фактычна мы займаліся ручным капіраваннем зыходнага кода на серверы.

Як перайсці ад маналіта да мікрасэрвісаў


Вылучэнне мікрасэрвісаў

Па-першае, мы для сябе адразу вызначылі, што падзел мікрасэрвісаў – працэс ітэрацыйны. Ад нас заўсёды патрабавалі раўналежна весткі распрацоўку бізнэс-задач. Як мы будзем ажыццяўляць гэта тэхнічна - ужо наша праблема. Таму мы рыхтаваліся да ітэрацыйнага працэсу. Па-іншаму не атрымаецца, калі ў вас вялікі дадатак, і ён першапачаткова не гатовы да таго, каб яго перапісвалі.

Якія мы выкарыстоўваем спосабы для вылучэння мікрасэрвісаў?

першы спосаб - выносіць існуючыя модулі як сэрвісы. У гэтым плане нам павезла: ужо былі аформленыя службы, якія працавалі па пратаколе WCF. Яны былі разнесены па асобных зборках. Мы пераносілі іх асобна, дадаючы да кожнай зборкі невялікі модуль запуску. Ён быў напісаны з дапамогай выдатнай бібліятэкі Topshelf, якая дазваляе запускаць дадатак і як сэрвіс, і як кансоль. Гэта зручна для адладкі, бо не патрабуецца дадатковых праектаў у рашэнні.

Службы былі звязаны па бізнес-логіцы, бо выкарыстоўвалі агульныя зборкі і працавалі з агульнай БД. Іх цяжка было назваць мікрасэрвісамі ў чыстым выглядзе. Тым не менш, мы маглі гэтыя сервісы выдаваць асобна, у розных працэсах. Ужо гэта дазваляла зменшыць уплыў іх сябар на сябра, паменшыўшы праблему з раўналежнай распрацоўкай і адзіным пунктам адмовы.

Зборка з хастом - гэта ўсяго адзін радок кода ў класе Program. Працу з Topshelf мы схавалі ў дапаможны клас.

namespace RBA.Services.Accounts.Host
{
   internal class Program
   {
      private static void Main(string[] args)
      {
        HostRunner<Accounts>.Run("RBA.Services.Accounts.Host");

       }
    }
}

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

Мы, як і многія, пачыналі з сервісаў аўтэнтыфікацыі і аўтарызацыі. Яны ідэальна для гэтага падыходзяць. Яны незалежныя, як правіла, у іх адасобленая мадэль дадзеных. Яны самі не ўзаемадзейнічаюць з маналітам, толькі ён звяртаецца да іх для рашэння нейкіх задач. На гэтых сэрвісах можна пачаць пераход на новую архітэктуру, адладзіць на іх інфраструктуру, паспрабаваць нейкія падыходы, злучаныя з сеткавымі бібліятэкамі, і т.д. У нас у арганізацыі няма каманд, у якіх не атрымалася б зрабіць сервіс аўтэнтыфікацыі.

Трэці спосаб вылучэння мікрасэрвісаў, Якім мы карыстаемся, крыху спецыфічны для нас. Гэта вынясенне бізнес-логікі з UI-пласта. У нас асноўнае UI-дадатак дэсктопнае, яно, як і backend, напісана на C#. Распрацоўнікі перыядычна памыляліся і выносілі на UI часткі логікі, якія павінны былі існаваць у backend і перавыкарыстоўвацца.

Калі паглядзець рэальны прыклад з кода UI-часткі, тое відаць, што вялікая частка гэтага рашэння ўтрымоўвае ў сабе сапраўдную бізнэс-логіку, якая карысная ў іншых працэсах, не толькі для пабудовы UI-формы.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

Рэальнай логікі UI там толькі апошняя пара радкоў. Мы яе пераносілі на сервер для таго, каб можна было перавыкарыстоўваць, тым самым памяншаючы UI і дамагаючыся правільнай архітэктуры.

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

Аддзяленне сэрвісаў з перапрацоўкай непарыўна звязана з паняццем абмежаванага кантэксту. Гэта паняцце з прадметна-арыентаванага праектавання. Яно азначае ўчастак даменнай мадэлі, у якім усе тэрміны адзінай мовы адназначна вызначаны. Разгледзім на прыкладзе кантэксту страховак і рахункаў. У нас маналітнае дадатак, і неабходна ў страхоўках папрацаваць з лікам. Мы чакаем, што распрацоўшчык знойдзе ў іншай зборцы існуючы клас "Рахунак", зробіць спасылку на яго з класа "Страхоўка", і мы атрымаем працоўны код. Прынцып DRY будзе выкананы, задача за кошт выкарыстання існуючага кода будзе зроблена хутчэй.

У выніку аказваецца, што кантэксты рахункаў і страховак звязаныя. Калі з'явяцца новыя патрабаванні, гэтая сувязь будзе мяшаць распрацоўцы, павялічваючы складанасць і без таго складанай бізнэс-логікі. Для вырашэння гэтай праблемы трэба ў кодзе знаходзіць межы паміж кантэкстамі і прыбіраць іх парушэнні. Напрыклад, кантэксту страховак, цалкам магчыма, будзе дастаткова 20-значнага нумара рахунку ЦБ і даты адкрыцця рахунку.

Каб гэтыя абмежаваныя кантэксты адлучаць сябар ад сябра і пачаць працэс вылучэння мікрасэрвісаў з маналітнага рашэння, мы выкарысталі такі падыход, як стварэнне ўсярэдзіне прыкладання вонкавых API. Калі мы ведалі, што нейкі модуль павінен стаць мікрасэрвісам, неяк перайначыцца ў рамках працэсу, то мы адразу ж рабілі выклікі логікі, якая належыць іншаму абмежаванаму кантэксту, праз знешнія выклікі. Напрыклад, праз REST ці WCF.

Мы для сябе цвёрда вырашылі, што не будзем пазбягаць кода, які запатрабуе рабіць размеркаваныя транзакцыі. У нашым выпадку аказалася досыць лёгка выконваць гэтае правіла. У нас да гэтага часу не ўзнікла такіх сітуацый, калі рэальна патрэбныя жорсткія размеркаваныя транзакцыі - цалкам дастаткова выніковай ўзгодненасці паміж модулямі.

Разгледзім пэўны прыклад. У нас ёсць паняцце аркестратара - канвеера, які апрацоўвае сутнасць "заяўкі". Ён па чарзе стварае кліента, рахунак і банкаўскую карту. Калі кліент і рахунак створаны паспяхова, а стварэнне карты правалілася, заяўка не пераходзіць у статус "паспяхова" і застаецца ў статусе "карта не створана". У будучыні фонавая актыўнасць падхопіць яе і скончыць. Сістэма некаторы час знаходзіцца ў стане няўзгодненасці, але нас гэта, у цэлым, задавальняе.

У выпадку, калі ўсё ж з'явіцца сітуацыя, калі трэба будзе ўзгоднена захаваць частку дадзеных, мы, хутчэй за ўсё, пойдзем на ўзбуйненне сэрвісу, каб апрацаваць гэта ў адным працэсе.

Разгледзім прыклад вылучэння мікрасэрвісу. Якім чынам можна адносна бяспечна давесці яго да прадакшн? У гэтым прыкладзе ў нас ёсць асобная частка сістэмы - модуль зарплатнага абслугоўвання, адзін з участкаў кода якога мы хацелі б зрабіць мікрасэрвісным.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

Перш за ўсё ствараем мікрасэрвіс, перапісваючы код. Паляпшаем некаторыя моманты, якія нас не задавальнялі. Рэалізуем новыя бізнес-патрабаванні ад заказчыка. Дадаем у звязак паміж UI і бэкэндам API Gateway, які будзе забяспечваць пракід выклікаў.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

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

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

Пры паспяховым пілоту мы разумеем, што новая канфігурацыя сапраўды працаздольная, можам прыбраць з раўнання стары маналіт і пакінуць новую канфігурацыю на месцы старога рашэння.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

Разам мы выкарыстоўваем практычна ўсе існуючыя метады падзелу зыходнага кода маналіта. Усе яны дазваляюць нам памяншаць памер частак прыкладання і пераводзіць іх на новыя бібліятэкі, робячы больш якасны зыходны код.

Праца з БД


БД паддаецца падзелу горш, чым зыходны код, бо змяшчае не толькі бягучую схему, але і назапашаныя гістарычныя дадзеныя.

У нашай БД, як і ў шматлікіх іншых, быў яшчэ адзін важны недахоп - велізарны памер. Гэтую БД праектавалі ў адпаведнасці з заблытанай бізнэс-логікай маналіта, і паміж табліцамі розных абмежаваных кантэкстаў назапасіліся сувязі.

У нашым выпадку ў давяршэнне ўсіх бед (вялікая база дадзеных, мноства сувязяў, часам незразумелыя межы паміж табліцамі) узнікла праблема, якая сустракаецца ў шматлікіх буйных праектах: выкарыстанне шаблону shared database. Дадзеныя браліся з табліц праз view, праз рэплікацыю і адгружаліся ў іншыя сістэмы, дзе патрэбная гэтая рэплікацыя. У выніку мы не маглі выносіць табліцы ў асобную схему, бо яны актыўна выкарыстоўваліся.

У падзеле нам дапамагае тое самае разбіццё на абмежаваныя кантэксты ў кодзе. Яно, як правіла, дае нам дастаткова добрае ўяўленне аб тым, як мы разбіваем дадзеныя на ўзроўні базы дадзеных. Мы разумеем, якія табліцы адносяцца да аднаго абмежаванага кантэксту, а якія да іншага.

Мы прымянілі два глабальныя спосабы падзелу базы дадзеных: аддзяленне існуючых табліц і аддзяленне з перапрацоўкай.

Аддзяленне існуючых табліц - гэта метад, які добра прымяняць тады, калі структура дадзеных якасная, задавальняе бізнес-патрабаванням і ўсіх задавальняе. У гэтым выпадку мы можам выдзяляць у асобную схему існуючыя табліцы.

Аддзяленне з перапрацоўкай трэба тады, калі бізнэс-мадэль моцна памянялася, і табліцы ўжо зусім нас не задавальняюць.

Аддзяленне існуючых табліц. Нам трэба вызначыць, што мы будзем адлучаць. Без гэтых ведаў нічога не атрымаецца, і тут нам дапаможа падзел абмежаваных кантэкстаў у кодзе. Як правіла, калі атрымліваецца зразумець межы кантэкстаў у зыходным кодзе, становіцца зразумела, якія табліцы павінны патрапіць у пералік на аддзяленне.

Уявім, што ў нас ёсць рашэнне, у якім два модулі маналіта ўзаемадзейнічаюць з адной базай дадзеных. Нам трэба зрабіць так, каб з участкам адлучаных табліц узаемадзейнічаў толькі адзін модуль, а іншы пачаў узаемадзейнічаць з ім праз API. Для пачатку дастаткова, каб праз API вялася толькі запіс. Гэта неабходная ўмова, каб мы маглі казаць аб незалежнасці мікрасэрвісаў. Сувязі на чытанне могуць заставацца пакуль у гэтым няма вялікай праблемы.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

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

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

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

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

Далей вылучым з агульнай БД табліцы, з якімі працуе толькі новы мікрасэрвіс. Мы можам вынесці табліцы ў асобную схему ці нават у асобную фізічную БД. Засталася сувязь на чытанне паміж мікрасэрвісам і БД маналіта, але ў гэтым няма нічога страшнага, у такой канфігурацыі ён можа жыць дастаткова доўга.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

Апошні крок - цалкам прыбраць усе сувязі. У гэтым выпадку нам, магчыма, запатрабуецца міграцыя дадзеных ад асноўнай базы. Часам мы захочам перавыкарыстоўваць у некалькіх базах нейкія дадзеныя або даведнікі, якія рэплікуюцца са знешніх сістэм. У нас гэта пэрыядычна сустракаецца.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

Аддзяленне з перапрацоўкай. Гэты метад вельмі падобны да першага, толькі ідзе ў зваротным парадку. У нас адразу ж вылучаецца новая база дадзеных і новы мікрасэрвіс, які ўзаемадзейнічае з маналітам праз API. Але пры гэтым застаецца набор табліц БД, якія мы жадаем у будучыні выдаліць. Ён нам больш не запатрабуецца, у новай мадэлі мы яго замянілі.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

Каб гэтая схема зарабіла, нам, хутчэй за ўсё, спатрэбіцца пераходны перыяд.

Далей ёсць два магчымыя падыходы.

Першы: мы дублюем усе дадзеныя ў новай і старой базах. У гэтым выпадку ў нас узнікае надмернасць дадзеных, могуць узнікаць праблемы з сінхранізацыяй. Але затое мы можам узяць два розныя кліенты. Адзін будзе працаваць з новай версіяй, іншы са старой.

Другі: падзяляем даныя па нейкім бізнес-прыкмете. Напрыклад, у нас у сістэме было 5 прадуктаў, якія захоўваюцца ў старой базе даных. Шосты ў рамках новай бізнес-задачы мы змяшчаем у новую БД. Але нам спатрэбіцца API Gateway, які сінхранізуе гэтыя дадзеныя і пакажа кліенту, адкуль і што купляць.

Абодва падыходу працоўныя, выбірайце ў залежнасці ад сітуацыі.

Пасля таго, як мы пераканаемся, што ўсё працуе, частку маналіта, якая працуе са старымі структурамі БД, можна адключыць.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

Апошнім крокам будзе выдаленне старых структур даных.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

Падводзячы вынік можна сказаць, што ў нас існуюць праблемы з БД: складана з ёй працаваць у параўнанні з зыходным кодам, падзяляць складаней, але рабіць гэта можна і трэба. Мы знайшлі некаторыя спосабы, якія дазваляюць гэта рабіць дастаткова бяспечна, усё ж з дадзенымі дапусціць памылку прасцей, чым з зыходным кодам.

Праца з зыходным кодам


Вось так выглядала схема зыходнага кода, калі мы пачалі аналізаваць маналітны праект.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

Яе ўмоўна можна падзяліць яе на тры пласта. Гэта пласт запускаемых модуляў, убудоў, сэрвісаў і асобных актыўнасцяў. Фактычна, гэта былі ўваходныя кропкі ўнутры маналітнага рашэння. Усе яны былі намёртва змацаваныя пластом Common. У ім была бізнэс-логіка, якая выкарыстоўвалася сэрвісамі сумесна, і мноства сувязяў. Кожны сэрвіс і ўбудова выкарыстоўваў да 10 і больш common-зборак, у залежнасці ад іх памеру і сумлення распрацоўшчыкаў.

Нам пашанцавала, у нас былі інфраструктурныя бібліятэкі, якія можна было выкарыстоўваць асобна.

Часам узнікала сітуацыя, калі некаторыя Сommon-аб'екты насамрэч не ставіліся да гэтага пласта, а былі інфраструктурнымі бібліятэкамі. Гэта вырашалася перайменаваннем.

Больш за ўсё турботы выклікалі абмежаваныя кантэксты. Бывала, што 3/4 кантэксту змешваліся ў адной зборцы Common і выкарыстоўвалі адзін аднаго ў рамках адных бізнес-функцый. Неабходна было зразумець, дзе гэта можна падзяліць і па якіх межах, і што далей рабіць з мапінгам гэтага падзелу на зборкі зыходнага кода.

Мы сфармулявалі некалькі правіл для працэсу падзелу кода.

Першае: мы больш не хацелі сумеснага выкарыстання бізнес-логікі паміж сэрвісамі, актыўнасцямі і плягінамі. Хацелі зрабіць бізнэс-логіку незалежнай у рамках мікрасэрвісаў. З іншага боку, мікрасэрвісы, у ідэальным выпадку, успрымаюцца як сэрвісы, якія існуюць зусім незалежна. Я лічу, што гэты падыход некалькі марнатраўны, ды і дасягнуць яго складана, бо, напрыклад, сэрвісы на C# будуць у любым выпадку злучаныя стандартнай бібліятэкай. Наша сістэма напісана на З#, іншыя тэхналогіі пакуль выкарыстоўваць не прыходзілася. Таму мы вырашылі, што можам сабе дазволіць выкарыстоўваць агульныя тэхнічныя зборкі. Галоўнае, каб у іх не было ніякіх фрагментаў бізнэс-логікі. Калі ў вас ёсць зручная абгортка над ORM, які вы карыстаецеся, то скапіяваць яе з сэрвісу ў сэрвіс вельмі дорага.

Наша каманда з'яўляецца прыхільнікамі прадметна-арыентаванага праектавання, таму «цыбульная архітэктура» нам выдатна падышла. Асновай у нашых сэрвісах стаў не data access layer, а зборка з даменнай логікай, якая змяшчае толькі бізнэс-логіку і пазбаўлена сувязяў з інфраструктурай. Пры гэтым мы можам незалежна дапрацоўваць даменную зборку для рашэння праблем, злучаных з фреймворками.

На гэтым этапе мы сустрэлі першую сур'ёзную праблему. Сэрвіс павінен быў спасылацца на адну даменную зборку, логіку мы жадалі зрабіць незалежнай, і нам тут моцна мяшаў прынцып DRY. Распрацоўнікі жадалі для пазбягання дубліравання перавыкарыстоўваць класы з суседніх зборак, і ў выніку дамены зноў пачалі звязвацца паміж сабой. Мы прааналізавалі вынікі і вырашылі, што, магчыма, праблема ляжыць яшчэ і ў вобласці прылады сховішча зыходнага кода. У нас меўся вялікі рэпазітар, у якім ляжалі ўсе зыходныя коды. Solution для ўсяго праекту вельмі цяжка было сабраць на лакальнай машыне. Таму для частак праекту ствараліся асобныя маленькія solution, і ніхто не забараняў дадаць у іх якую-небудзь Сommon- або даменную зборку і перавыкарыстоўваць. Адзіная прылада, які не дазваляў нам гэтага рабіць, гэта код рэўю. Але часам і ён даваў збоі.

Тады мы пачалі пераходзіць на мадэль з асобнымі рэпазітарамі. Бізнес-логіка перастала выцякаць з сэрвісу ў сэрвіс, дамены сапраўды сталі незалежнымі. Абмежаваныя кантэксты падтрымліваюцца больш ясна. Як пры гэтым мы перавыкарыстоўваем інфраструктурныя бібліятэкі? Мы вылучылі іх у асобны рэпазітар, затым змясцілі ў Nuget-пакеты, якія паклалі ў Artifactory. Пры любой змене зборка і публікацыя адбываецца аўтаматычна.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

Нашы сэрвісы сталі спасылацца на ўнутраныя інфраструктурныя пакеты сапраўды гэтак жа, як і на вонкавыя. Вонкавыя бібліятэкі мы спампоўваем з Nuget. Для працы з Artifactory, куды мы змяшчалі гэтыя пакеты, мы ўжылі два пакетных мэнэджара. У маленькіх рэпазітарах мы таксама выкарыстоўвалі Nuget. У рэпазітарах з некалькімі сэрвісамі мы выкарыстоўвалі Paket, які забяспечвае больш узгодненасці версій паміж модулямі.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

Такім чынам, працуючы над зыходным кодам, крыху змяніўшы архітэктуру і падзяляючы рэпазітары, мы робім нашы сэрвісы больш незалежнымі.

Праблемы інфраструктуры


Большасць недахопаў пры пераходзе на мікрасэрвісы звязана з інфраструктурай. Вам спатрэбіцца аўтаматызаванае разгортванне, спатрэбяцца новыя бібліятэкі для працы інфраструктуры.

Ручная ўстаноўка ў асяроддзі

Першапачаткова рашэнне на асяроддзі мы ўсталёўвалі ўручную. Каб аўтаматызаваць гэты працэс, мы стварылі CI/CD-канвеер. Выбралі працэс continuous delivery, таму што continuous deployment для нас пакуль непрымальны з пункту гледжання бізнес-працэсаў. Таму адпраўка ў эксплуатацыю ажыццяўляюцца па кнопцы, а на тэсціраванне - аўтаматычна.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

Мы выкарыстоўваем Atlassian, Bitbucket для захоўвання зыходных кодаў і Bamboo для зборкі. Нам падабаецца пісаць зборачныя скрыпты на Cake, таму што гэта той жа C#. У Artifactory прыходзяць ужо гатовыя пакеты, і Ansible аўтаматычна пападае на тэставыя серверы, пасля чаго іх можна адразу тэставаць.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

Паасобнае лагіраванне


У свой час адной з ідэй маналіта было забеспячэнне сумеснага лагіравання. Нам таксама трэба было зразумець, што рабіць з асобнымі логамі, якія ляжаць на дысках. Логі ў нас пішуцца ў тэкставыя файлы. Мы вырашылі выкарыстоўваць стандартны ELK-стэк. Не сталі пісаць у ELK напрамую праз правайдэры, а вырашылі, што дапрацуем тэкставыя логі і будзем запісваць у іх ID трасіроўкі ў выглядзе ідэнтыфікатара, дадаючы імя сэрвісу, каб гэтыя логі потым можна было разбіраць.

Пераход ад маналіта да мікрасэрвісаў: гісторыя і практыка

З дапамогай Filebeat мы атрымліваем магчымасць збіраць нашыя логі з сервераў, затым іх пераўтвараць, з дапамогай Kibana будаваць запыты ў UI і глядзець, як ішоў выклік паміж сэрвісамі. У гэтым моцна дапамагае ID трасіроўкі.

Тэставанне і адладка звязаных сэрвісаў


Першапачаткова мы не да канца разумелі, як нам адладжваць сэрвісы, якія распрацоўваюцца. З маналітам усё было проста, мы запускалі яго на лакальнай машыне. Спачатку гэтак жа спрабавалі рабіць і з мікрасэрвісамі, але часам для паўнавартаснага запуску аднаго мікрасэрвісу трэба запускаць і некалькі іншых, а гэта няёмка. Мы зразумелі, што трэба пераходзіць да мадэлі, калі мы пакідаем на лакальнай машыне толькі сэрвіс або паслугі, які жадаем адладзіць. Астатнія сэрвісы выкарыстоўваюцца з сервераў, якія супадаюць па канфігурацыі з prod. Пасля адладкі, пры тэсціраванні, для кожнай задачы на ​​тэставы сервер выдаюцца толькі змененыя сэрвісы. Такім чынам, тэстуецца рашэнне ў такім выглядзе, у якім яно ў будучыні апынецца на продзе.

Ёсць серверы, на якіх стаяць толькі production-версіі сэрвісаў. Гэтыя серверы патрэбны на выпадак інцыдэнтаў, для праверкі пастаўкі перад дэплоем і для ўнутраных навучанняў.

У нас дадаўся працэс аўтаматычнага тэсціравання з дапамогай папулярнай бібліятэкі Specflow. Тэсты запускаюцца аўтаматычна з дапамогай NUnit адразу пасля разгортвання з Ansible. Калі пакрыццё задачы цалкам аўтаматычнае, то няма неабходнасці ў ручным тэсціраванні. Хоць часам усё ж такі патрабуецца дадатковае ручное тэсціраванне. Для вызначэння, якія тэсты запускаць для канкрэтнай задачы, мы выкарыстоўваем тэгі ў Jira.

Дадаткова вырасла патрэбнасць у нагрузачным тэсціраванні, раней яно праводзілася толькі ў рэдкіх выпадках. Для запуску тэстаў мы выкарыстоўваем JMeter, для іх захоўвання – InfluxDB, а для пабудовы графікаў працэсу – Grafana.

Чаго мы дабіліся?


Па-першае, мы пазбавіліся паняцця «рэліз». Зніклі двухмесячныя монструозныя рэлізы, калі гэтая махіна разгортвалася ў production-асяроддзі, ламаючы на ​​час бізнэс-працэсы. Цяпер мы разгортваем сэрвісы ў сярэднім кожныя 1,5 дня, групуючы іх, таму што ў эксплуатацыю яны выходзяць пасля ўзгаднення.

У нашай сістэме няма фатальных збояў. Калі мы выпусцілі мікрасэрвіс з памылкай, то злучаная з ёй функцыянальнасць будзе зламаная, а ўся астатняя функцыянальнасць не папакутуе. Гэта значна паляпшае карыстацкі досвед.

Мы можам кіраваць схемай разгортвання. Можна выдзяляць групы сэрвісаў асобна ад астатняга рашэння, калі ў гэтым ёсць неабходнасць.

Акрамя таго, мы істотна зменшылі праблему з вялікай чаргой дапрацовак. У нас з'явіліся асобныя прадуктовыя каманды, якія працуюць з часткай сервісаў незалежна. Тут ужо нядрэнна падыходзіць Scrum-працэс. У канкрэтнай каманды можа быць асобны ўладальнік прадукта, які ставіць ёй задачы.

Рэзюмэ

  • Мікрасэрвісы добра падыходзяць для дэкампазіцыі складаных сістэм. У працэсе мы пачынаем разумець, што ёсць у нашай сістэме, якія ёсць абмежаваныя кантэксты, дзе праходзяць іх межы. Гэта дазваляе правільна размяркоўваць дапрацоўкі па модулях і не дапусціць заблытвання кода.
  • Мікрасэрвісы даюць арганізацыйныя перавагі. Аб іх часта кажуць толькі як аб архітэктуры, але любая архітэктура патрэбна для рашэння запатрабаванняў бізнэсу, а не сама па сабе. Таму мы можам сказаць, што мікрасэрвісы добра падыходзяць для рашэння задач невялікімі камандамі, улічваючы, што зараз вельмі папулярны Scrum.
  • Падзел - гэта ітэратыўны працэс. Нельга ўзяць дадатак і проста падзяліць на мікрасэрвісы. Атрыманы прадукт ці наўрад будзе працаздольным. Пры вылучэнні мікрасэрвісаў выгадна перапісваць існуючае legacy, гэта значыць ператвараць яго ў код, які нам падабаецца і лепш задавальняе запатрабаванням бізнэсу па функцыянальнасці і хуткасці.

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

    PS Больш эмацыйнае апавяданне (і як быццам асабіста вам) – па спасылцы.
    Тут поўная версія даклада.

Крыніца: habr.com

Дадаць каментар