Gjatë kalimit nga një aplikacion monolit në një arkitekturë mikroshërbimesh, ne përballemi me probleme të reja.
Në një aplikacion monolit, zakonisht mjafton të përcaktohet se në cilën pjesë të sistemit ndodhi gabimi. Me shumë gjasë, problemi është në kodin e vetë monolit ose në bazën e të dhënave. Por kur fillojmë të kërkojmë problemin në arkitekturën e mikroshërbimeve, gjithçka nuk është kaq e qartë. Duhet të gjejmë të gjithë rrugën që ka kaluar kërkesa nga fillimi në fund, duke e ndarë atë nga qindra mikroshërbime. Për më tepër, shumica prej tyre kanë edhe depozita të tyre, ku mund të ndodhin gabime logjike ose probleme me performancën dhe qëndrueshmërinë.

Kam kërkuar gjatë për një mjet që do të ndihmonte në zgjidhjen e këtyre problemeve (kam shkruar për këtë në Habr: , ), por përfundimisht krijova një zgjidhje të vetme me kod të hapur. Në këtë artikull, flas për përfitimet e qasjes me mesh shërbimesh dhe ndaj një mjet të ri për implementimin e saj.
Tranzita e shpërndarë është një zgjidhje e zakonshme për problemin e gjetjes së gabimeve në sistemet e shpërndara. Por çfarë ndodh nëse në sistem nuk është implementuar ky qasje për mbledhjen e informacionit mbi ndërveprimet rrjetore, ose, më keq akoma, në një pjesë të sistemit ajo funksionon mirë, kurse në një pjesë tjetër jo, pasi nuk është shtuar në shërbimet e vjetra? Për të përcaktuar shkakun e saktë të problemit, është e nevojshme të kemi një pamje të plotë të asaj që po ndodh në sistem. Sidomos është e rëndësishme të kuptojmë se cilat mikroshërbime janë të angazhuara në rrugët kryesore duke qenë kritik për biznesin.
Këtu mund të na ndihmojë qasja e mesh-shërbimeve, e cila do të merret me të gjithë makinerinë për mbledhjen e informacionit rrjetor në një nivel më të ulët sesa ata shërbime vetë. Kjo qasje na lejon të kapim të gjithë trafikun dhe ta analizojmë atë në kohë reale. Për më tepër, aplikacionet nuk duhet të dinë asgjë rreth saj.
Qasja e mesh-shërbimeve
Ideja kryesore e qasjes së mesh-shërbimeve është shtimi i një shtrese të re infrastrukturore mbi rrjetin, e cila na lejon të bëjmë çdo lloj gjëje me ndërveprimin ndërmjet shërbimeve. Shumica e implementimeve funksionojnë si më poshtë: për secilin mikroshërbim shtohet një kontejner sidecar shtesë me një proxy transparent, përmes të cilit kalon të gjithë trafikun hyrës dhe dalës të shërbimit. Dhe kjo është pikërisht ajo vend ku mund të bëjmë balancimin e klientit, të aplikojmë politika sigurie, të vendosim kufij në numrin e kërkesave dhe të mbledhim informacion të rëndësishëm për ndërveprimin e shërbimeve në prodhim.

Zgjidhjet
Që tani ka disa implementime të kësaj qasjeje: dhe . Ato ofrojnë shumë mundësi nga paketa. Por, njëkohësisht, kjo sjell edhe një overhead të madh në burime. Sa më i madh të jetë klasteri në të cilin funksionon një sistem i tillë, aq më shumë burime do të nevojiten për mbështetje të infrastrukturës së re. Në Avito, ne operojmë me klasterët kubernetes, në të cilat ndodhen mijëra kopje shërbimesh (dhe numri vazhdon të rritet shpejt). Në implementimin aktual, Istio konsumon ~300Mb RAM për çdo kopje shërbimi. Për shkak të numrit të madh të mundësive, balancimi transparent gjithashtu ndikon në kohën totale të përgjigjes së shërbimeve (deri në 10ms).
Në fund, ne shqyrtuam se cilat mundësi na nevojiteshin pikërisht tani dhe vendosëm se arsyeja kryesore për të cilën filluam të implementonim zgjidhje të tilla ishte mundësia për të mbledhur informacionin e tracing nga e gjithë sistemi në mënyrë transparente. Gjithashtu, ne donim të kishim kontroll mbi ndërveprimin e shërbimeve dhe të bënim manipulime të ndryshme me headert që kalonin midis shërbimeve.
Në fund, arritëm në zgjidhjen tonë: .
Netramesh
— është një zgjidhje e lehtë mesh shërbimesh me mundësi për shkallëzimin e pakufizuar pa marrë parasysh numrin e shërbimeve në sistem.
Qëllimet kryesore të zgjidhjes së re ishin një overhead i vogël në burime dhe performancë e lartë. Nga mundësitë kryesore, ne donim që menjëherë të kishim mundësinë për të dërguar në mënyrë transparente spanjat e tracing në sistemin tonë Jaeger.
Sot, shumica e zgjidhjeve në cloud implementohen në Golang. Dhe, sigurisht, ka arsye të veta për këtë. Është e lehtë dhe mjaft e thjeshtë të shkruash aplikacione rrjetore në Golang, të cilat funksionojnë asinhron në I/O dhe që shkallëzohen sipas nevojës për bërthamën. Dhe, çka është gjithashtu shumë e rëndësishme, performanca është e mjaftueshme për të zgjidhur këtë detyrë. Prandaj ne gjithashtu zgjodhëm Golang.
Performanca
Ne përqendruam përpjekjet tona në arritjen e performancës maksimale. Për një zgjidhje që vendoset pranë çdo kopje shërbimi, kërkohen konsum të ulët të RAM-it dhe koha e procesorëve. Dhe, sigurisht, vonesa në përgjigje gjithashtu duhet të jetë e ulët.
Le të shohim se cilat rezultate kemi arritur.
RAM
Netramesh konsumon rreth 10Mb pa trafik dhe deri në 50Mb maksimalisht me ngarkesë deri në 10000 RPS për një instancë.
Istio envoy proxy gjithmonë konsumon rreth 300Mb në klasteret tona me mijëra instanca. Kjo nuk lejon që ai të shkallëzohet në tërë klasterin.


Me Netramesh arritëm një reduktim të konsumit të memories prej rreth 10 herësh.
CPU
Përdorimi i CPU është relativisht i barabartë nën ngarkesë. Ky varion në varësi të numrit të kërkesave për njësi kohe për sidecar. Vlerat në 3000 kërkesa në sekondë në kulm:


Ka një çështje tjetër të rëndësishme: Netramesh është një zgjidhje pa control plane dhe pa ngarkesë nuk konsumon kohën e procesorit. Me Istio, sidecar-at gjithmonë përditësojnë endpoint-et e shërbimeve. Si rezultat, mund të shohim këtë pamje pa ngarkesë:

Ne përdorim HTTP/1 për ndërveprimin midis shërbimeve. Rritja e kohës së përgjigjes me Istio gjatë proxy-timit përmes envoy ishte deri në 5-10ms, e cila është mjaft e madhe për shërbimet që janë në gjendje të përgjigjen brenda një milisekonde. Me Netramesh, ky kohë u reduktua në 0.5-2ms.
Shkallëzueshmëria
Një sasi e vogël burimesh e harxhuar nga çdo proxy lejon që ai të vendoset afër çdo shërbimi. Netramesh është krijuar qëllimisht pa një komponent control plane për mirëmbajtjen e lehtë të çdo sidecar-i. Shpesh në zgjidhjet e service mesh, control plane shpërndan informacionin rreth zbulimit të shërbimeve në çdo sidecar. Bashkë me të vjen informacioni për timeout-et, konfigurimet e balancimit. Të gjitha këto lejojnë të bëhen shumë gjëra të dobishme, por fatkeqësisht fryjnë sidecar-at në madhësi.
Zbulimi i shërbimeve

Netramesh nuk shton asnjë mekanizëm të tjera për zbulimin e shërbimeve. E gjithë trafiku proxy-titet qartë përmes netra sidecar.
Netramesh mbështet protokollin aplikativ HTTP/1. Për ta përcaktuar përdoret një listë e konfigurueshme portash. Zakonisht në sistem ka disa porta, përmes të cilave ndodh ndërveprimi përmes HTTP. Për shembull, ne përdorim portat 80, 8890, 8080 për ndërveprimin e shërbimeve dhe kërkesave të jashtme. Në këtë rast, ato mund të vendosen përmes variablit të mjedisit NETRA_HTTP_PORTS.
Nëse përdorni Kubernetes si orkestrues dhe mekanizmin e entiteteve të Shërbimeve për ndërveprimin brenda klasterit midis shërbimeve, atëherë mekanizmi mbetet i njëjtë. Fillimisht, mikrosherbimi merr adresën IP të shërbimit përmes kube-dns dhe hap një lidhje të re me të. Kjo lidhje krijohet fillimisht me netra-sidecar lokal dhe të gjitha paketat TCP fillimisht veçanërisht arrijnë në netra. Më pas, netra-sidecar krijon lidhje me pikën e origjinës. NAT në pod IP në nod mbetet i njëjtë si pa netra.
Tracing i shpërndarë dhe kalimi i kontekstit
Netramesh ofron funksionalitetin e nevojshëm për dërgimin e span-eve të tracing për ndërveprimin HTTP. Netra-sidecar analizojnë protokollin HTTP, masin vonesat e kërkesave, dhe nxjerrin informacionin e nevojshëm nga header-at e HTTP. Në fund, ne marrim të gjitha trace-t në një sistem të vetëm Jaeger. Për konfigurimin e hollësishëm, mund të përdorni gjithashtu variablat e mjedisit që ofron biblioteka zyrtare .


Por ka një problem. Derisa shërbimet të fillojnë të gjenerojnë dhe të kalojnë një header të veçantë uber, ne nuk do të shohim span-et e lidhura të tracing në sistem. Dhe kjo është ajo që na nevojitet për të gjetur shpejt shkakun e problemeve. Këtu Netramesh ka sërish një zgjidhje. Proxy-t lexojnë header-at HTTP dhe, nëse nuk ka uber trace id, e gjenerojnë atë. Netramesh gjithashtu ruan informacionin për kërkesat hyrëse dhe dalëse në sidecar dhe i ndërlidh ato duke i pasuruar me header-at e nevojshëm të kërkesave dalëse. E vetmja gjë që duhet të bëjnë shërbimet është të kalojnë vetëm një header X-Request-Id, i cili mund të konfigurohet përmes variablit të mjedisit NETRA_HTTP_REQUEST_ID_HEADER_NAME. Për të menaxhuar madhësinë e kontekstit në Netramesh, mund të caktoni variablat e mjedisit të mëposhtëm: NETRA_TRACING_CONTEXT_EXPIRATION_MILLISECONDS (koha gjatë së cilës do të ruhet konteksti) dhe NETRA_TRACING_CONTEXT_CLEANUP_INTERVAL (periodiciteti i pastrimit të kontekstit).
Gjithashtu është e mundur të kombinohen disa rrugë në sistemin tuaj duke i etiketuar ato me një marker të veçantë sesional. Netra lejon të vendoset HTTP_HEADER_TAG_MAP për ta kthyer header-at HTTP në etiketat përkatëse të tracing span. Kjo mund të jetë veçanërisht e dobishme për testimin. Pas kalimit të një testi funksional, mund të shihni se cila pjesë e sistemit është prekur, duke filtruar sipas çelësit përkatës të sesionit.
Përcaktimi i burimit të kërkesës
Për të përcaktuar se nga ka ardhur kërkesa, mund të përdorni funksionalitetin e shtimit automatik të një header burimi. Me ndihmën e variablit të mjedisit NETRA_HTTP_X_SOURCE_HEADER_NAME mund të caktohet emri i header-it që do të vendoset automatikisht. Me ndihmën e NETRA_HTTP_X_SOURCE_VALUE mund të përcaktoni vlerën që do t'i caktohet kokës X-Source për të gjitha kërkesat që dalin.
Kjo lejon që ky titull i dobishëm të shpërndahet në një mënyrë të unifikuar në të gjithë rrjetin. Më pas, mund ta përdorni atë në shërbime dhe ta shtoni në regjistrat dhe metrikat.
Rrjetizimi i trafikut dhe brendësitë e Netramesh
Netramesh përbëhet nga dy komponente kryesore. E para, netra-init, vendos rregullat e rrjetit për kapjen e trafikut. Ai përdor për të kapur të gjithë ose një pjesë të trafikut në sidecar, i cili është komponenti i dytë kryesor i Netramesh. Mund të përcaktoni se cilat porte duhet kapur për sesionet TCP që hyjnë dhe dalin: INBOUND_INTERCEPT_PORTS, OUTBOUND_INTERCEPT_PORTS.
Gjithashtu, në këtë mjet ka një mundësi interesante — rrugëzim probabilistik. Nëse përdorni Netramesh ekskluzivisht për të mbledhur tracing span, atëherë në ambientin e prodhimit mund të kurseni burime dhe të aktivizoni rrugëzimin probabilistik me ndihmën e variablave NETRA_INBOUND_PROBABILITY dhe NETRA_OUTBOUND_PROBABILITY (nga 0 deri në 1). Vlera sipas parazgjedhjes është 1 (kapet i gjithë trafiku).
Pas kapjes me sukses, netra sidecar merr një lidhje të re dhe përdor SO_ORIGINAL_DST opsionin e socket-it për të marrë pikën origjinale të destinacionit. Pastaj Netra hap një lidhje të re me adresën origjinale IP dhe vendos një komunikim TCP të dyanshëm midis palëve, duke dëgjuar të gjithë trafikun që kalon. Nëse porti është i përcaktuar si HTTP, Netra përpiqet ta analizojë dhe ta ndjekë. Nëse analysimi i HTTP dështon, Netra bën një fall-back në TCP dhe transparentisht proxy-të bajtat.
Ndërtimi i grafit të varësive
Pas marrjes së një sasi të madhe informacioni tracing në Jaeger, dëshirojmë të marrim grafikun e plotë të interaksioneve në sistem. Por nëse sistemi juaj është mjaft i ngarkuar dhe në një ditë grumbullohen miliarda tracing span, agregimi i tyre bëhet një detyrë e komplikuar. Ka një mënyrë zyrtare për këtë: . Megjithatë, do të marrë orë për ndërtimin e grafikut të plotë dhe do t'i kërkojë të shkarkojë nga Jaeger të gjithë dataset-in për 24 orët e kaluara.
Nëse përdorni Elasticsearch për ruajtjen e tracing span, mund të shfrytëzoni , i cili do të ndërtosh një grafik të tillë për minuta, duke përdorur karakteristikat dhe mundësitë e Elasticsearch.

Si të përdorni Netramesh
Netra mund të shtohet lehtësisht në çdo shërbim që operon nën menaxhimin e çdo orkestratori. Mund të shihni një shembull .
Aktualisht, Netra nuk ka mundësinë e integrimit automatik të sidecar në shërbime, por ka plane për zbatimin e saj.
E ardhmja e Netramesh
Qëllimi kryesor është arritja e kostove minimale të burimeve dhe performancës së lartë, duke ofruar mundësi kryesore për observability dhe kontrollin e ndërveprimeve midis shërbimeve.
Në të ardhmen, Netramesh do të mbështesë protokolle të tjera të nivelit të aplikacionit përveç HTTP. Së shpejti do të vijë mundësia e rrugëzimit L7.
Përdorni Netramesh nëse përballeni me këto probleme dhe na shkruani pyetje dhe sugjerime.
Burimi: habr.com
