Na internetu (service mesh), i evo još jednog. Hurra! Ali zašto? Zato što želim iznijeti svoje mišljenje da bi bilo bolje da su sustavi-mreže postojali prije 10 godina, prije nego što su se pojavile platforme s kontejnerima poput Dockera i Kubernetesa. Ne tvrdim da je moje gledište bolje ili lošije od drugih, ali budući da su sustavi-mreže prilično složena kreativna bića, višestruka gledišta pomoći će ih bolje razumjeti.
Govorit ću o platformi dotCloud, koja je bila izgrađena na više od stotinu mikroservisa i podržavala tisuće aplikacija u kontejnerima. Objasnit ću probleme s kojima smo se suočili tijekom njezine izrade i lansiranja te kako bi sustavi-mreže mogli pomoći (ili ne).
Povijest dotCloud
Već sam pisao o povijesti dotCloud i izboru arhitekture za ovu platformu, ali malo sam govorio o mrežnoj razini. Ako ne želite uroniti u čitanje o dotCloud, evo u kratkim crtama: to je platforma-kao-usluga PaaS koja omogućava klijentima pokretanje širokog spektra aplikacija (Java, PHP, Python...) uz podršku širokog spektra podataka (MongoDB, MySQL, Redis...) i radni tijek poput Heroku: učitavate svoj kod na platformu, ona gradi slike kontejnera i raspoređuje ih.
Objasnit ću kako je promet usmjeravan na platformu dotCloud. Ne zato što je to bilo posebno sjajno (iako je sustav funkcionirao dobro za svoje vrijeme!), već prije svega zato što s pomoću modernih alata takav dizajn može lako realizirati skromni tim u kratkom vremenu, ako im je potreban način za usmjeravanje prometa između mnoštva mikroservisa ili aplikacija. Također, mogu se usporediti opcije: što dobivate ako sve razvijate sami ili koristite postojeći sustav-mreža. Standardni izbor: raditi sami ili kupiti.
Usmjeravanje prometa za hosted-aplikacije
Aplikacije na dotCloud mogu pružati HTTP i TCP krajnje točke.
HTTP krajnje točke dinamički se dodaju u konfiguraciju klastera ravnoteža opterećenja . To izgleda kao ono što danas rade resursi u Kubernetesu i ravnoteža opterećenja poput .
Klijenti se povezuju s HTTP krajnjim točkama putem odgovarajućih domena pod uvjetom da naziv domene upućuje na ravnoteže opterećenja dotCloud. Ništa posebno.
TCP krajnje točke su povezane s brojem porta, koji se zatim prosljeđuje svim kontejnerima ove hrpe putem varijabli okoline.
Klijenti se mogu povezati na TCP krajnje točke koristeći odgovarajuće ime domaćina (nešto poput gateway-X.dotcloud.com) i broj porta.
To ime domaćina se razrješava na klaster poslužitelja ‘nats‘ (nije povezano s ), koji će usmjeravati dolazne TCP veze u pravi kontejner (ili, u slučaju usluga s ravnotežom opterećenja, u prave kontejnere).
Ako ste upoznati s Kubernetesom, vjerojatno će vam to djelovati poznato kao usluge .
Na platformi dotCloud nije bilo ekvivalenta uslugama : za jednostavnost, pristup uslugama bio je jednako organiziran iznutra i izvana platforme.
Sve je bilo organizirano dovoljno jednostavno: prvotne implementacije mreža usmjeravanja HTTP i TCP, vjerojatno samo nekoliko stotina linija Pythona. Jednostavni (rekao bih, naivni) algoritmi su se usavršavali s rastom platforme i pojavom dodatnih zahtjeva.
Opsežno preuređenje postojećeg koda nije bilo potrebno. Konkretno, mogu izravno koristiti adresu dobivenu putem varijabli okoline.
Kako se to razlikuje od modernog servisnog mesha?
Ograničena vidljivost. Uopće nismo imali nikakve metrike za mrežno usmjeravanje TCP-a. Što se tiče usmjeravanja HTTP-a, u kasnijim verzijama pojavio se detaljan HTTP metrike s kodovima grešaka i vremenom odziva, ali moderni servisni meshi idu još dalje, osiguravajući integraciju s sustavima za prikupljanje metrika, kao što je Prometheus, na primjer.
Vidljivost je važna ne samo s operativnog stajališta (da pomogne u rješavanju problema), već i prilikom izdavanja novih funkcija. Riječ je o sigurnom i .
Učinkovitost usmjeravanja također je ograničeno. U mreži usmjeravanja dotCloud, sav promet mora proći kroz klaster posvećenih usmjerivača. To je značilo potencijalno prelazak preko više granica AZ (zona dostupnosti) i značajno povećanje latencije. Sjećam se kako sam otklanjao probleme s kodom koji je izvodio više od stotinu SQL upita po stranici i za svaki upit otvarao novo povezivanje s SQL poslužiteljem. Kada se lokalno pokrene, stranica se učitava odmah, no u dotCloud-u učitavanje traje nekoliko sekundi, jer je za svako TCP povezivanje (i sljedeći SQL upit) potrebno desetke milisekundi. U ovom konkretnom slučaju problem je riješen stalnim povezivanjima.
Moderne mreže usluga bolje se nose s takvim problemima. Prije svega, provjeravaju da li su povezivanja usmjerena na izvor. Logički tok ostaje isti: klijent → mreža → usluga, ali sada mreža radi lokalno, a ne na udaljenim čvorovima, tako da je povezivanje klijent → mreža lokalno i vrlo brzo (mikrosekunde umjesto milisekundi).
Moderne mreže usluga također implementiraju pametnije algoritme za raspodjelu opterećenja. Praćenjem funkcionalnosti backend-a, mogu slati više prometa na brže backend-e, što dovodi do povećanja ukupne izvedbe.
Sigurnost također je bolje. Mreža usmjeravanja dotCloud radila je isključivo na EC2 Classic i nije šifrirala promet (na osnovu pretpostavke da ako je netko uspio postaviti sniffer na mrežni promet EC2, već imate velike probleme). Moderne mreže usluga prozirno štite sav naš promet, na primjer, s obostranom TLS autentifikacijom i kasnijim šifriranjem.
Usmjeravanje prometa za platforme
Dobro, razgovarali smo o prometu između aplikacija, ali što je s samom platformom dotCloud?
Sama platforma se sastojala od otprilike stotinu mikroslužbi, odgovornih za različite funkcije. Neke su primale zahtjeve od drugih, a neke su bile pozadinski radnici koji su se povezivali s drugim uslugama, ali nisu sami primale povezivanja. U svakom slučaju, svaka usluga mora znati krajnje točke adresa kojima se treba povezati.
Mnoge visoko razine usluge mogu koristiti mrežu usmjeravanja opisanu iznad. Uistinu, mnoge od više od stotinu mikrosservisa dotCloud su raspoređene kao obične aplikacije na samoj platformi dotCloud. No, mali broj niskonivojskih usluga (osobito onih koje implementiraju ovu mrežu usmjeravanja) trebao je nešto jednostavnije, s manje ovisnosti (budući da nisu mogle ovisiti same o sebi — stara dobra problematika kokoši i jajeta).
Ove niskonivojske, ključne usluge su raspoređene pokretanjem kontejnera izravno na nekoliko ključnih čvorova. Pri tome nisu korištene standardne platforme usluge: orkestrator, planirač i runner. Ako želite usporediti s modernim kontejnerskim platformama, ovo je slično pokretanju kontrolne ravnine s docker run izravno na čvorovima, umjesto da se zadaća delegira Kubernetesu. To je prilično slično konceptu , koji koristi ili pri pokretanju autonomnog klastera.
Ove su usluge izlagane jednostavnim i grubim načinom: u YAML datoteci su navedeni njihovi nazivi i adrese; a svaki klijent trebao je uzeti kopiju te YAML datoteke za uvođenje.
S jedne strane, to je izuzetno pouzdano jer ne zahtijeva podršku vanjskog spremišta ključeva/vrijednosti kao što je Zookeeper (ne zaboravite, u to vrijeme još nije postojalo etcd ili Consul). S druge strane, to je otežavalo premještanje usluga. Svaki put kada se premjestio, svi klijenti su morali dobiti ažuriranu YAML datoteku (i potencijalno se ponovo pokrenuti). Nije baš praktično!
Kasnije smo počeli implementirati novu shemu, gdje se svaki klijent povezao na lokalni proksi poslužitelj. Umjesto adrese i porta, trebalo mu je znati samo broj porta usluge i povezati se putem localhost. Lokalni proksi poslužitelj obrađuje tu vezu i preusmjerava je na stvarni poslužitelj. Sada prilikom premještanja pozadine na drugo računalo ili skaliranja, umjesto ažuriranja svih klijenata, potrebno je ažurirati samo sve te lokalne proksije; i ponovno pokretanje više nije potrebno.
(Također se planiralo inkapsulirati promet u TLS vezama i postaviti još jedan proxy server na prijemnoj strani, kao i provjeravati TLS certifikate bez sudjelovanja prijemne usluge koja je postavljena za primanje veza samo na localhost. O tome kasnije).
To vrlo podsjeća na tvrtke Airbnb, ali značajna razlika je u tome što je SmartStack implementiran i raspoređen u proizvodnji, dok je unutarnji sustav usmjeravanja dotCloud stavljen u ladicu kada je dotCloud postao Docker.
Osobno smatram da je SmartStack jedan od prethodnika sustava poput Istio, Linkerd i Consul Connect, jer svi oni slijede jedan obrazac:
- Pokreće proxy na svakom čvoru.
- Klijenti se povezuju s proxyjem.
- Upravljačka ravnina ažurira konfiguraciju proxy servera kada se promijene backendovi.
- … Profit!
Moderna implementacija servisne mreže
Ako trebamo implementirati sličnu mrežu danas, možemo koristiti slične principe. Na primjer, postaviti unutarnju DNS zonu, usklađujući imena usluga s adresama u prostoru 127.0.0.0/8. Zatim pokrenuti HAProxy na svakom čvoru klastera, prihvaćajući veze na svakoj adresi usluge (u ovoj podmreži 127.0.0.0/8) i preusmjeravajući/uravnotežujući opterećenje na odgovarajuće backendove. HAProxy konfiguracija može se upravljati , omogućujući pohranu informacija o backendu u etcd ili Consul i automatsko slanje ažurirane konfiguracije na HAProxy kada je to potrebno.
Otprilike tako radi Istio! Ali s nekim razlikama:
- Koristi umjesto HAProxy.
- Čuva konfiguraciju backendova putem Kubernetes API-a umjesto etcd-a ili Consula.
- Uslugama se dodjeljuju adrese u unutarnjoj podmreži (Kubernetes ClusterIP adrese) umjesto 127.0.0.0/8.
- Ima dodatnu komponentu (Citadel) za dodavanje međusobne provjere autentičnosti TLS između klijenta i servera.
- Podržava nove funkcije, kao što su prekid lanca (circuit breaking), distribuirano traciranje, isporuka kanarinca itd.
Pogledajmo ukratko neke razlike.
Envoy Proxy
Envoy Proxy napisala je tvrtka Lyft [konkurent Ubera na tržištu taksija – nap. prev.]. U mnogome je sličan drugim proxyjima (npr. HAProxy, Nginx, Traefik…), ali je Lyft napisao svoj jer su im bile potrebne funkcije koje nedostaju u drugim proxyjima, a činilo se razumnijim napraviti novi nego proširivati postojeći.
Envoy se može koristiti samostalno. Ako imam određenu uslugu koja se treba povezati s drugim uslugama, mogu je postaviti za povezivanje s Envoy-em, a zatim dinamički konfigurirati i prekonfigurirati Envoy s lokacijama drugih usluga, uz mnoge dodatne funkcije, poput preglednosti. Umjesto prilagođene klijentske biblioteke ili implementacije u kodu za praćenje poziva, usmjeravamo promet prema Envoy-u, koji prikuplja metrike za nas.
No, Envoy također može raditi kao podaci (data plane) za servisni mesh. To znači da se za ovaj servisni mesh Envoy konfigurira kontrolnom ravninom (control plane).
Kontrolna ravnina
U kontrolnoj ravnini, Istio se oslanja na Kubernetes API. To se ne razlikuje mnogo od korištenja confd, koji se oslanja na etcd ili Consul za pregled skupa ključeva u skladištu podataka. Istio pregledava skup resursa Kubernetes putem Kubernetes API-ja.
U međuvremenu: osobno mi se učinilo korisnim ovo , koje kaže:
Kubernetes API server je "glupi server" koji nudi pohranu, upravljanje verzijama, provjeru, ažuriranje i semantiku API resursa.
Istio je dizajniran za rad s Kubernetesom; i ako ga želite koristiti izvan Kubernetesa, morate pokrenuti instancu Kubernetes API servera (i pomoćnu uslugu etcd).
Adrese usluga
Istio se oslanja na ClusterIP adrese koje dodjeljuje Kubernetes, tako da usluge Istio dobivaju unutarnju adresu (ne unutar raspona 127.0.0.0/8).
Promet na ClusterIP adresu za određenu uslugu unutar Kubernetes klastera bez Istio se presreće pomoću kube-proxy i šalje na serverski dio tog proxyja. Ako vas zanimaju tehnički detalji, kube-proxy postavlja pravila iptables (ili IPVS balansere opterećenja, ovisno o tome kako je konfiguriran) za prepisivanje IP adresa odredišta za veze koje idu na ClusterIP adresu.
Nakon što je Istio instaliran u Kubernetes klaster, ništa se ne mijenja dok se izričito ne omogući za određenog potrošača ili čak za cijelu namjenu, putem uvođenja sidecar u prilagođene podove. Taj kontejner pokreće instancu Envoy-a i postavlja niz pravila iptables za presretanje prometa koji ide na druge usluge i preusmjeravanje tog prometa na Envoy.
Integracijom s Kubernetes DNS-om, naš kod može se povezati putem imena usluge, i sve "jednostavno radi". Drugim riječima, naš kod izbacuje upite tipa http://api/v1/users/4242, tada api rezolvira upit na 10.97.105.48, iptables pravila presreću veze s 10.97.105.48 i preusmjeravaju ih na lokalni proxy Envoy, a taj lokalni proxy će usmjeriti upit na stvarni API backend. Uf!
Dodatni detalji
Istio također osigurava end-to-end enkripciju i autentifikaciju putem mTLS (uzajamna TLS). To osigurava komponenta pod nazivom Citadel.
Također postoji komponenta Mixer, koju Envoy može zatražiti za svaku upit, kako bi donio posebnu odluku o ovom upitu ovisno o raznim faktorima, poput zaglavlja, opterećenja backenda itd... (ne brinite: postoji mnogo načina za osiguranje funkcionalnosti Mixera, a čak i ako on zakaže, Envoy će nastaviti normalno raditi kao proxy).
I, naravno, spomenuli smo uvid: Envoy prikuplja ogromnu količinu metrike, osiguravajući pri tome distribuirano praćenje. U arhitekturi mikroservisa, ako jedan API upit mora proći kroz mikroservise A, B, C i D, tada će prilikom ulaska u sustav distribuirano praćenje dodati jedinstveni identifikator upitu i zadržati taj identifikator kroz podupite svim tim mikroservisima, omogućujući bilježenje svih povezanih poziva, njihovih kašnjenja itd.
Razvijati ili kupiti
Istio ima reputaciju složenog sustava. Nasuprot tome, izgradnja mreže usmjeravanja koju sam opisao na početku ovog posta, relativno je jednostavna s postojećim alatima. Dakle, ima li smisla umjesto toga stvoriti vlastitu servisnu mrežu?
Ako imamo skromne potrebe (ne trebamo uvid, prekidač lanaca i druge finese), onda se javljaju misli o razvoju vlastitog alata. No ako koristimo Kubernetes, možda nam on čak i neće trebati, jer Kubernetes već pruža osnovne alate za otkrivanje usluga i ravnotežu opterećenja.
Ali ako imamo napredne zahtjeve, tada se "kupovina" servisne mreže čini puno boljom opcijom. (To nije uvijek zapravo "kupovina", budući da Istio dolazi s otvorenim izvorom, ali i dalje trebamo uložiti inženjersko vrijeme da shvatimo kako to funkcionira, implementiramo i upravljamo njime).
Što odabrati: Istio, Linkerd ili Consul Connect?
Dok smo govorili samo o Istio, to nije jedini servis-mreža. Popularna alternativa je , a tu je i .
Što odabrati?
Iskreno, ne znam. Trenutno se ne smatram dovoljno kompetentnim da odgovorim na ovo pitanje. Postoji nekoliko usporedba ovih alata i čak .
Jedan od obećavajućih pristupa je korištenje alata poput . On uvodi sloj apstrakcije za pojednostavljenje i unifikaciju API-ja koje pružaju servis-mreže. Umjesto da proučavamo specifične (i, po mom mišljenju, relativno složene) API-je raznih servis-mreža, možemo koristiti jednostavnije konstrukcije SuperGloo— i lako se prebacivati s jednog na drugi, kao da imamo međuprostor konfiguracije koji opisuje HTTP sučelja i backendove, sposoban generirati stvarnu konfiguraciju za Nginx, HAProxy, Traefik, Apache…
Malo sam se igrao s Istio i SuperGloo, a u sljedećem članku želim pokazati kako dodati Istio ili Linkerd u postojeći klaster pomoću SuperGloo, i koliko će se posljednji snaći u svom poslu, odnosno, omogućiti prebacivanje s jedne servis-mreže na drugu bez prepisivanja konfiguracija.
Izvor: habr.com
