Kontejnerët, mikro-shërbimet dhe mesh-të shërbimit

Në internet ka shumë artikull o shërbime ndërlidhëse (service mesh), dhe këtu është edhe një tjetër. Hurra! Po pse? Sepse dua të shpreh mendimin tim se do kishte qenë më mirë nëse shërbimet ndërlidhëse do ishin shfaqur 10 vjet më parë, para se të shfaqeshin platformat kontejnerike si Docker dhe Kubernetes. Nuk po pretendoj se pikëpamja ime është më e mirë ose më e keqe se të tjerat, por pasi shërbimet ndërlidhëse janë krijesa mjaft komplekse, shumëllojshmëria e pikëpamjeve do të ndihmojë për t'i kuptuar ato më mirë.

Do flas për platformën dotCloud, e cila u ndërtua mbi më shumë se njëqind mikroshërbime dhe mbështeste mijëra aplikacione në kontejnerë. Do shpjegoj problemet me të cilat u përballëm gjatë zhvillimit dhe lansimit të saj, dhe si shërbimet ndërlidhëse mund të kishin ndihmuar (ose jo).

Historia e dotCloud

Kam shkruar tashmë për historinë e dotCloud dhe zgjedhjen e arkitekturës për këtë platformë, por kam folur pak për nivelin rrjetë. Nëse nuk dëshironi të zhytni në lexim të mëparshëm rreth dotCloud, ja përmbledhja: është një platformë si shërbim PaaS që lejon klientët të lansojnë një gamë të gjerë aplikacionesh (Java, PHP, Python…), me mbështetje për një gamë të gjerë shërbimesh të dhënash (MongoDB, MySQL, Redis…) dhe një proces punimi si Heroku: ngarkoni kodin tuaj në platformë, ajo ndërton imazhet e kontejnerëve dhe i deploy-on.

Do flas për si u drejtuar trafiku në platformën dotCloud. Jo për faktin se ishte veçanërisht e shkëlqyer (edhe pse për kohën e saj sistemi funksiononte mjaft mirë!), por kryesisht sepse me ndihmën e mjeteve moderne një dizajn i tillë mund të realizohet lehtësisht për një ekip modest brenda një kohe të shkurtër, në rast se ata kanë nevojë për një mënyrë për të orientuar trafikun midis një grushti mikroshërbimesh ose aplikacionesh. Kështu, mund të krahasohen opsionet: çfarë ndodh nëse e zhvilloni gjithçka vetë ose përdorni një shërbim ndërlidhës ekzistues. Zgjedhja standarde: ta bësh vetë ose ta blesh.

Orientimi i trafikut për aplikacionet e hostuara

Aplikacionet në dotCloud mund të ofrojnë pikët përfundimtare HTTP dhe TCP.

Pikat përfundimtare HTTP shtohen dinamikisht në konfigurimin e grupit të balancuesve të ngarkesës Hipache. Kjo ngjan me atë që bëjnë sot burimet Ingress në Kubernetes dhe balancuesit e ngarkesës si Traefik.

Konsumatorët lidhen me pikat përfundimtare HTTP përmes domain-eve përkatëse nëse emri i domain-it tregon në balancuesit e ngarkesës dotCloud. Nuk ka asgjë të veçantë.

Pikat përfundimtare TCP lidhen me numrin e portit, i cili më pas transmetohet në të gjitha kontejnerët e këtij steku përmes variablave mjedisorë.

Klientët mund të lidheshin me piketat TCP duke përdorur emrin e duhur të hostit (diçka si gateway-X.dotcloud.com) dhe numrin e portit.

Ky emër hosti zgjidhet në klasën e serverëve "nats" (nuk ka lidhje me NATS), të cilët do të riorganizojnë lidhjet TCP që hyjnë në konteinerin e duhur (ose, në rastin e shërbimeve me balancim ngarkese, në konteinerët e duhur).

Nëse jeni të njohur me Kubernetes, ndoshta kjo do t'ju kujtojë shërbimet NodePort.

Në platformën dotCloud nuk kishte ekuivalente të shërbimeve ClusterIP: për thjeshtësi, qasja në shërbime ndodhte njësoj nga brenda ashtu edhe nga jashtë platformës.

Të gjitha ishin organizuar mjaft thjesht: realizimet fillestare të rrjeteve të riorganizimit HTTP dhe TCP, ndoshta vetëm disa qindra rreshta Python. Algoritme të thjeshta (do të thosha, naive) që u përmirësuan me rritjen e platformës dhe shfaqjen e kërkesave shtesë.

Rindërtimi i gjerë i kodit ekzistues nuk ishte i nevojshëm. Në veçanti, aplikacionet 12-faktor mund të përdorin drejtpërdrejt adresën e marrë përmes variablave mjedisorë.

Si ndryshon kjo nga një mesh shërbimi modern?

Pamja e kufizuar e shikueshmërisë. Ne s'kishim asnjë metrikë për rrjetin e riorganizimit TCP. Sa i përket riorganizimit HTTP, në versionet më të vonshme u shfaqën metrikë të detajuara HTTP me kode gabimi dhe kohë përgjigjeje, por mesh-at moderne të shërbimeve shkojnë akoma më tej, duke siguruar integrim me sistemet e mbledhjes së metrikave, si Prometheus, për shembull.

Shikueshmëria nuk është e rëndësishme vetëm nga pikëpamja operacionale (për të ndihmuar në zgjidhjen e problemeve), por gjithashtu gjatë rritjes së funksioneve të reja. Bëhet fjalë për depozitimin e sigurt të bluzave dhe gjelave dhe depozitimin e kanarinave.

Efikasiteti i riorganizimit është gjithashtu e kufizuar. Në rrjetin e drejtimit dotCloud, gjithë trafiku duhej të kalonte përmes një klusteri të nyjeve të dedikuara të drejtimit. Kjo do të thoshte potencialin e kalimit të disa kufijve AZ (zona të disponueshmërisë) dhe një rritje të konsiderueshme të vonesës. E kam të qartë si kam zgjidhur problemet me kodin, i cili bënte më shumë se njëqind SQL kërkesa për faqe dhe për çdo kërkesë hapte një lidhje të re me serverin SQL. Kur e nisa lokal, faqja ngarkohet menjëherë, por në dotCloud ngarkimi merr disa sekonda, sepse për çdo lidhje TCP (dhe kërkesë SQL të mëpasshme) kërkohen dhjetëra milisekonda. Në këtë rast të veçantë, problemi u zgjidh me lidhjet e vazhdueshme.

Shërbimet moderne të meshave janë më të efektshme për këtë lloj problemi. Para se gjithash, ato kontrollojnë se lidhjet drejtohen në burim. Rrjedha logjike është e njëjtë: klienti → mesh → shërbim, por tani mesh funksionon lokal, e jo në nyje të largëta, kështu që lidhja klienti → mesh është lokale dhe shumë e shpejtë (mikroskonda në vend të milisekondave).

Shërbimet moderne të meshave gjithashtu zbatojnë algoritme më të zgjuara të balancimit të ngarkesës. Duke kontrolluar funksionalitetin e backend-ëve, ato mund të dërgojnë më shumë trafik në backend-e më të shpejtë, çka çon në përmirësimin e përgjithshëm të performancës.

Siguria është gjithashtu më mirë. Rrjeti i drejtimit dotCloud funksiononte plotësisht në EC2 Classic dhe nuk enkriptonte trafikun (në bazë të supozimit që nëse dikush kishte arritur të vendoste një sniffer në trafikun rrjetit EC2, ju keni tashmë probleme të mëdha). Shërbimet moderne të meshave mbrojnë në mënyrë transparente gjithë trafikun tonë, për shembull, me autentifikimin mutuar TLS dhe enkriptimin e mëpasshëm.

Drejtimi i trafikut për shërbimet e platformës

Mirë, kemi diskutuar për trafikun midis aplikacioneve, por çfarë mund të thuhet për platformën vetë dotCloud?

Platforma vetë përbënte rreth njëqind mikrosherbimesh, të cilat ishin përgjegjëse për funksione të ndryshme. Disa merrnin kërkesa nga të tjerët, ndërsa disa ishin punëtorë në sfond që lidheshin me shërbime të tjera, por vetë nuk merrnin lidhje. Në çdo rast, çdo shërbim duhet të dijë adresat e pikave përfundimtare që duhet të lidhet.

Shumë shërbime të nivelit të lartë mund të përdorin rrjetin e routingut të përshkruar më sipër. Në të vërtetë, shumë nga më shumë se njëqind mikrosërvices dotCloud ishin deployuar si aplikacione të zakonshme në vetë platformën dotCloud. Por një numër i vogël shërbimesh të nivelit të ulët (në veçanti, ato që realizojnë këtë rrjet routing) kishin nevojë për diçka më të thjeshtë, me më pak varësi (sepse për të funksionuar ato nuk mund të varen nga vetvetja - problemi i njohur i pulës dhe vezës).

Këto shërbime të ulëta, të rëndësishme, u deployuan duke nisur kontejnerë direkt në disa nyje kyçe. Në këtë rast, nuk u aktivizuan shërbimet standarde të platformës: ampullator, planifikues dhe runner. Nëse dëshironi ta krahasoni me platformat moderne të kontejnerëve, kjo është si të nisin një plan menaxhimi me docker run drejt për nyjet, në vend që të delegohet detyra Kubernetes. Kjo është mjaft e ngjashme me konceptin e moduleve statike (pod), të cilat përdor kubeadm ose bootkube në ngarkimin e një klasteri autonom.

Këto shërbime u ekspozuan në një mënyrë të thjeshtë e të egër: emrat dhe adresat e tyre ishin listuar në një skedar YAML; dhe çdo klient duhej të merrte një kopje të këtij skedari YAML për të realizuar deploy.

Nga njëra anë, kjo është jashtëzakonisht e besueshme, sepse nuk kërkon mbështetje të një magazinimi të jashtëm çelësesh/vlerash, si Zookeeper (mos harroni, në atë kohë nuk ekzistonte ende etcd apo Consul). Nga ana tjetër, kjo e komplikonte lëvizjen e shërbimeve. Çdo herë që ndodhte lëvizja, të gjithë klientët duhej të merrnin një skedar YAML të përditësuar (dhe potencialisht të rinisnin më vete). Jo shumë e përshtatshme!

Më vonë, filluam të zbatonim një skemë të re, ku çdo klient lidhej me një server proxy lokal. Në vend të adresës dhe portit, ajo mjafton të dijë vetëm numrin e portit të shërbimit dhe të lidhet përmes localhost. Serveri proxy lokal menaxhon këtë lidhje dhe e drejton atë në serverin real. Tani, kur backend zhvendoset në një makinë tjetër ose kur ndodh skalim, në vend të përditësimit të të gjithë klientëve, duhet të përditësohen vetëm këto servera proxy lokale; dhe rinisja nuk është më e nevojshme.

(Po gjithashtu ishte planifikuar të enkapsulonte trafikun në lidhjet TLS dhe të vendoste një proxy tjetër në anën e pranuese, si dhe të kontrollonte certifikatat TLS pa pjesëmarrjen e shërbimit pranuese, i cili është konfiguruar për të pranuar lidhjet vetëm në localhost. Më vonë për këtë).

Kjo është shumë e ngjashme me SmartStack nga Airbnb, por ndryshimi kryesor është se SmartStack është realizuar dhe vendosur në prodhim, ndërsa sistema e brendshme e routing dotCloud u hoq në kuti kur dotCloud u shndërrua në Docker.

Unë personalisht mendoj se SmartStack është një nga pararendësit e sistemeve të tilla si Istio, Linkerd dhe Consul Connect, sepse të gjithë ata ndjekin një model të ngjashëm:

  • Nisja e proxy-it në çdo nyje.
  • Klientët lidhen me proxy-in.
  • Niveli i menaxhimit përditëson konfigurimin e proxy-t kur ndodhin ndryshime në backend.
  • … Profiti!

Implementimi modern i një service mesh

Nëse na nevojitet të realizojmë një rrjet të tillë sot, mund të përdorim principe të ngjashme. Për shembull, të konfigurojmë një zonë të brendshme DNS, duke i lidhur emrat e shërbimeve me adresat në hapësirën 127.0.0.0/8. Pastaj, të nisim HAProxy në çdo nyje të klasterit, duke pranuar lidhje në çdo adresë shërbimi (në këtë nënrrjetë 127.0.0.0/8) dhe duke drejtuar/balancuar ngarkesën në backendet përkatës. Konfigurimi i HAProxy mund të menaxhohet confd, duke lejuar ruajtjen e informacionit mbi backend në etcd ose Consul dhe automatikisht të shtyjë konfigurimin e përditësuar në HAProxy kur është e nevojshme.

Kështu funksionon Istio! Por me disa dallime:

  • Përdor Envoy Proxy në vend të HAProxy.
  • Ruajti konfigurimin e backendit nëpërmjet Kubernetes API në vend të etcd ose Consul.
  • Shërbimeve u jepen adresa në nënrrjetën e brendshme (adresat Kubernetes ClusterIP) në vend të 127.0.0.0/8.
  • Ka një komponent shtesë (Citadel) për shtimin e verifikimit të ndërsjellë të autentikimit TLS midis klientit dhe serverëve.
  • Mbështet funksione të reja si prishja e ciklit (circuit breaking), gjurmimi i shpërndarë, dhe implementimi i kanarenjve e të tjerë.

Le të shqyrtojmë shkurt disa dallime.

Envoy Proxy

Envoy Proxy u shkrua nga kompania Lyft [konkurrenti i Uber në tregun e taksive - shën. red.]. Ai është shumë i ngjashëm me proxy të tjera (p.sh. HAProxy, Nginx, Traefik…), por Lyft e shkroi veten e tyre sepse ata kishin nevojë për funksione që mungonin në proxy të tjera, dhe dukej më e arsyeshme të bëhej një e re sesa të zgjerohej ekzistuesi.

Envoy mund të përdoret vetë. Nëse kam një shërbim konkret që duhet të lidhet me shërbime të tjera, mund ta konfiguroj atë për t'u lidhur me Envoy, dhe pastaj ta konfiguroj dhe ri-konfiguroj dinamik me vendndodhjen e shërbimeve të tjera, duke fituar shumë funksione të shkëlqyera, për shembull, përmbushjen e kërkesave të monitorimit. Në vend të një biblioteke klienti të personalizuar ose të integrimit në kodin e thirrjeve, ne drejtojmë trafikun në Envoy, dhe ai mbledh metrika për ne.

Por Envoy gjithashtu mund të funksionojë si plane të dhënave (data plane) për një mesh shërbimesh. Kjo do të thotë se tani për këtë mesh shërbimesh, Envoy konfigurohet me plane kontrolli (control plane).

Plani i kontrollit

Në planin e kontrollit, Istio mbështetet në API-në e Kubernetes. Kjo nuk është shumë ndryshe nga përdorimi i confd, i cili mbështetet në etcd ose Consul për të shqyrtuar një grup çelësash në depo. Istio përmes API-së së Kubernetes shqyrton një grup burimesh Kubernetes.

Ndërkohë: personalisht më duket e dobishme kjo përshkrim i API-së së Kubernetes, i cili thotë:

Serveri API i Kubernetes është një "server i budall" që ofron ruajtje, menaxhim versionesh, verifikim, përditësim dhe semantike për burimet e API-së.

Istio është projektuar për t'u punuar me Kubernetes; dhe nëse dëshoni ta përdorni jashtë Kubernetes, duhet të lançoni një instancë të serverit API të Kubernetes (dhe shërbimin e ndihmës etcd).

Adresat e shërbimeve

Istio mbështetet në adresat ClusterIP, të cilat i jep Kubernetes, kështu që shërbimet e Istio marrin një adresë të brendshme (jo në diapazonin 127.0.0.0/8).

Trafiku në adresën ClusterIP për një shërbim konkret në klasterin Kubernetes pa Istio kapet nga kube-proxy dhe dërgohet në backendin e këtij proxy. Nëse ju interesojnë detajet teknike, kube-proxy vendos rregulla iptables (ose balancues IPVS, në varësi të mënyrës se si është konfiguruar), për të ridrejtuar adresat IP të lidhjeve që kalojnë në adresën ClusterIP.

Pas instalimit të Istio në klasterin Kubernetes, asgjë nuk ndryshon derisa të aktivizohet shprehimisht për një konsumator të caktuar ose madje për gjithë hapësirën emëruese, përmes rrjedhjes së një kontejneri sidecar në pod-et e personalizuara. Ky kontejner do të drejtojë një instancë të Envoy dhe do të vendosë një sërë rregullash iptables për të kapur trafikun që shkon në shërbime të tjera dhe për të ridrejtuar këtë trafik në Envoy.

Në integrimin me Kubernetes DNS, kjo do të thotë se kodi ynë mund të lidhet me emrin e shërbimit dhe gjithçka "thjesht funksionon". Me fjalë të tjera, kodi ynë bën kërkesa të tipit http://api/v1/users/4242, atëherë api rezolvon kërkesën në 10.97.105.48, rregullat e iptables kapin lidhjet me 10.97.105.48 dhe i drejtojnë ato te proxy lokal Envoy, dhe ky proxy lokal do të dërgojë kërkesën te backend-i real të API-it. Uf!

Shtesa të tjera

Istio gjithashtu ofron enkriptim dhe autentifikim end-to-end përmes mTLS (mutual TLS). Kjo përpjekje mbikëqyret nga një komponent i quajtur Citadel.

Ka gjithashtu një komponent Mixer, i cili mund të kërkohet nga Envoy për çdo kërkesën, për të marrë një vendim të veçantë për këtë kërkesë në varësi të faktorëve të ndryshëm, siç janë titujt, ngarkesa e backend-it etj... (Mos u shqetësoni: ka shumë mjete për të siguruar funksionimin e Mixer, dhe madje, nëse ai dështon, Envoy do të vazhdojë të funksionojë si proxy).

Dhe, sigurisht, kemi përmendur monitorimin: Envoy mbledh një sasi të madhe metricash, duke siguruar gjithashtu gjurmimin e shpërndarë. Në arkitekturën e mikroshërbimeve, nëse një kërkesë API duhet të kalojë përmes mikroshërbimeve A, B, C dhe D, atëherë kur hyn në sistem, gjurmimi i shpërndarë do të shtojë një identifikues të veçantë për kërkesën dhe do ta ruajë atë identifikues nëpër nën-kërkesat te të gjithë këto mikroshërbime, duke lejuar që të regjistrohen të gjitha thirrjet e lidhura, vonesat e tyre etj.

Të zhvillosh ose të blesh

Istio ka një reputacion si një sistem i komplikuar. Në anën tjetër, ndërtimi i rrjetit të rrugëzimit që e përshkrova në fillim të këtij posta është relativisht i thjeshtë me mjetet ekzistuese. Pra, a ka kuptim që të ndërtojmë një shërbim-mesh të vetin?

Nëse kemi nevoja modeste (nuk kemi nevojë për monitorim, ndërprerës zinxi dhe detaje të tjera), mendimet për zhvillimin e një mjeti të vetin ndodhin. Por nëse përdorim Kubernetes, ndoshta nuk do të na nevojitet as, sepse Kubernetes tashmë ofron mjetet bazë për zbulimin e shërbimeve dhe balancimin e ngarkesës.

Por nëse kemi kërkesa të avancuara, atëherë "blerja" e një shërbim-meshi duket shumë më e mirë. (Kjo nuk është gjithmonë "blerje", pasi Istio vjen me kod burimi të hapur, por ne gjithsesi duhet të investojmë kohë inxhinierike për të kuptuar funksionimin e tij, për ta vendosur dhe menaxhuar atë).

Çfarë të zgjedhim: Istio, Linkerd apo Consul Connect?

Derisa kemi folur vetëm për Istio, ky nuk është shërbimi i vetëm i rrjetit. Një alternative popullore është Linkerd, dhe ka edhe Consul Connect.

Çfarë të zgjedhim?

Sinqerisht, nuk e di. Në këtë moment, nuk e ndiej veten mjaft kompetent për të përgjigjur në këtë çështje. Ka disa interesante artikull me krahasimin e këtyre mjeteve dhe madje benchmark-e.

Një nga qasjet e shpresuara është të përdorësh një mjet si SuperGloo. Ai implementon një shtresë abstraksioni për të thjeshtuar dhe unifikuar API-të që ofrohen nga shërbimet e rrjetit. Në vend që të mësojmë API-të specifike (dhe, sipas mendimit tim, relativisht të komplikuara) të shërbimeve të ndryshme të rrjetit, ne mund të përdorim struktura më të thjeshta të SuperGloo - dhe të kalojmë lehtësisht nga njëri në tjetrin, sikur të kishim një format të ndërmjetëm konfigurimi që përshkruan ndërfaqet HTTP dhe backend-et, të aftë për të gjeneruar konfigurimin aktual për Nginx, HAProxy, Traefik, Apache...

Kam luajtur pak me Istio dhe SuperGloo, dhe në artikullin e ardhshëm dua të tregoj se si të shtoj Istio ose Linkerd në një klaster ekzistues duke përdorur SuperGloo, dhe sa mirë e përballon ky i fundit punën e tij, domethënë lejon kalimin nga një shërbim në tjetrin pa riparimin e konfigurimeve.

Burimi: habr.com

Blini hosting të besueshëm për faqe interneti me mbrojtje nga DDoS, serverë VPS VDS 🔥 Blini hosting të besueshëm për faqe interneti me mbrojtje nga DDoS, serverë VPS VDS | ProHoster