ProHoster > Blog > uprava > Balansiranje opterećenja i skaliranje dugotrajnih veza u Kubernetesu
Balansiranje opterećenja i skaliranje dugotrajnih veza u Kubernetesu
Ovaj će vam članak pomoći razumjeti kako funkcionira balansiranje opterećenja u Kubernetesu, što se događa pri skaliranju dugotrajnih veza i zašto biste trebali razmotriti balansiranje na strani klijenta ako koristite HTTP/2, gRPC, RSockets, AMQP ili druge dugotrajne protokole .
Malo o tome kako se redistribuira promet u Kubernetesu
Kubernetes pruža dvije prikladne apstrakcije za implementaciju aplikacija: usluge i implementacije.
Implementacije opisuju kako i koliko kopija vaše aplikacije treba biti pokrenuto u bilo kojem trenutku. Svaka aplikacija je raspoređena kao Pod i dodijeljena joj je IP adresa.
Usluge su po funkciji slične balanseru opterećenja. Osmišljeni su za raspodjelu prometa na više grupa.
Da vidimo kako to izgleda.
Na donjem dijagramu možete vidjeti tri instance iste aplikacije i balansera opterećenja:
Uravnoteživač opterećenja naziva se usluga i dodjeljuje mu se IP adresa. Svaki dolazni zahtjev preusmjerava se na jednu od grupa:
Scenarij implementacije određuje broj instanci aplikacije. Gotovo nikad se nećete morati proširiti izravno ispod:
Svakom modulu je dodijeljena vlastita IP adresa:
Korisno je zamisliti usluge kao skup IP adresa. Svaki put kada pristupite usluzi, jedna od IP adresa odabire se s popisa i koristi se kao odredišna adresa.
Ovako izgleda.
Zahtjev curl 10.96.45.152 primljen je na uslugu:
Usluga odabire jednu od tri adrese modula kao svoje odredište:
Promet se preusmjerava na određenu pod:
Ako se vaša aplikacija sastoji od frontenda i backenda, tada ćete imati i uslugu i implementaciju za svaki.
Kada sučelje podnese zahtjev pozadini, ne mora točno znati koliko podova služi pozadina: može biti jedan, deset ili stotinu.
Također, sučelje ne zna ništa o adresama podova koji opslužuju pozadinu.
Kada sučelje uputi zahtjev pozadini, koristi se IP adresa pozadinske usluge, koja se ne mijenja.
Evo kako to izgleda.
Pod 1 zahtijeva internu pozadinsku komponentu. Umjesto odabira određenog za pozadinu, šalje zahtjev usluzi:
Usluga odabire jedan od backend modula kao odredišnu adresu:
Promet se odvija od Pod 1 do Pod 5 prema izboru servisa:
Pod 1 ne zna točno koliko je mahuna kao pod 5 skriveno iza usluge:
Ali kako točno usluga distribuira zahtjeve? Čini se da se koristi kružno balansiranje? Hajdemo shvatiti.
Balansiranje u Kubernetes uslugama
Kubernetes usluge ne postoje. Ne postoji proces za uslugu kojoj je dodijeljena IP adresa i port.
To možete provjeriti prijavom na bilo koji čvor u klasteru i pokretanjem naredbe netstat -ntlp.
Nećete čak moći pronaći IP adresu dodijeljenu usluzi.
IP adresa servisa nalazi se u kontrolnom sloju, u kontroleru, i zabilježena u bazi podataka - itd. Istu adresu koristi druga komponenta - kube-proxy.
Kube-proxy prima popis IP adresa za sve usluge i generira skup pravila iptables na svakom čvoru u klasteru.
Ova pravila kažu: "Ako vidimo IP adresu usluge, moramo izmijeniti odredišnu adresu zahtjeva i poslati je jednoj od grupa."
IP adresa usluge koristi se samo kao ulazna točka i ne opslužuje je nijedan proces koji sluša tu IP adresu i port.
Pogledajmo ovo.
Razmotrimo klaster od tri čvora. Svaki čvor ima podove:
Vezane mahune obojene u bež boju dio su servisa. Budući da usluga ne postoji kao proces, prikazana je sivom bojom:
Prva grupa zahtijeva uslugu i mora ići na jednu od pridruženih grupa:
Ali usluga ne postoji, proces ne postoji. Kako radi?
Prije nego zahtjev napusti čvor, prolazi kroz pravila iptables:
Pravila iptables znaju da usluga ne postoji i zamjenjuju njenu IP adresu jednom od IP adresa podova povezanih s tom uslugom:
Zahtjev prima valjanu IP adresu kao odredišnu adresu i obrađuje se normalno:
Ovisno o topologiji mreže, zahtjev na kraju stigne do modula:
Može li iptables balansirati opterećenje?
Ne, iptables se koriste za filtriranje i nisu dizajnirani za balansiranje.
Međutim, moguće je napisati skup pravila koja funkcioniraju kao pseudo-balanser.
A upravo je to implementirano u Kubernetesu.
Ako imate tri mahune, kube-proxy će napisati sljedeća pravila:
Odaberite prvi pod s vjerojatnošću od 33%, inače prijeđite na sljedeće pravilo.
Odaberite drugo s vjerojatnošću od 50%, inače prijeđite na sljedeće pravilo.
Odaberite treće ispod.
Ovaj sustav rezultira odabirom svake mahune s vjerojatnošću od 33%.
I nema jamstva da će Pod 2 biti odabran sljedeći nakon Pod 1.
Primijetiti: iptables koristi statistički modul sa slučajnom distribucijom. Stoga se algoritam balansiranja temelji na slučajnom odabiru.
Sada kada razumijete kako usluge funkcioniraju, pogledajmo zanimljivije scenarije usluga.
Dugotrajne veze u Kubernetesu ne skaliraju se prema zadanim postavkama
Svaki HTTP zahtjev od frontenda do backenda poslužuje zasebna TCP veza, koja se otvara i zatvara.
Ako frontend pošalje 100 zahtjeva u sekundi backendu, tada se otvara i zatvara 100 različitih TCP veza.
Možete smanjiti vrijeme obrade zahtjeva i opterećenje otvaranjem jedne TCP veze i njezinim korištenjem za sve naredne HTTP zahtjeve.
HTTP protokol ima značajku koja se zove HTTP keep-alive ili ponovno korištenje veze. U ovom slučaju, jedna TCP veza koristi se za slanje i primanje više HTTP zahtjeva i odgovora:
Ova značajka nije omogućena prema zadanim postavkama: i poslužitelj i klijent moraju biti konfigurirani u skladu s tim.
Samo postavljanje je jednostavno i dostupno za većinu programskih jezika i okruženja.
Evo nekoliko poveznica na primjere na različitim jezicima:
Što se događa ako koristimo keep-alive u Kubernetes usluzi?
Pretpostavimo da i frontend i backend podržavaju održavanje života.
Imamo jednu kopiju frontenda i tri kopije backenda. Sučelje postavlja prvi zahtjev i otvara TCP vezu s pozadinom. Zahtjev stigne do usluge, jedan od pozadinskih modula odabran je kao odredišna adresa. Backend šalje odgovor, a frontend ga prima.
Za razliku od uobičajene situacije u kojoj se TCP veza zatvara nakon primitka odgovora, sada ostaje otvorena za daljnje HTTP zahtjeve.
Što se događa ako sučelje pošalje više zahtjeva pozadini?
Za prosljeđivanje ovih zahtjeva koristit će se otvorena TCP veza, svi će zahtjevi ići u istu pozadinu gdje je otišao prvi zahtjev.
Ne bi li iptables trebao redistribuirati promet?
Ne u ovom slučaju.
Kada se stvori TCP veza, ona prolazi kroz pravila iptables, koja odabiru određenu pozadinu gdje će promet ići.
Budući da su svi sljedeći zahtjevi na već otvorenoj TCP vezi, pravila iptables se više ne pozivaju.
Da vidimo kako to izgleda.
Prva grupa šalje zahtjev servisu:
Već znate što će se sljedeće dogoditi. Usluga ne postoji, ali postoje iptables pravila koja će obraditi zahtjev:
Jedna od pozadinskih grupa bit će odabrana kao odredišna adresa:
Zahtjev stiže pod. U ovoj će se točki uspostaviti stalna TCP veza između dva modula:
Svaki sljedeći zahtjev iz prve jedinice ići će kroz već uspostavljenu vezu:
Rezultat je brže vrijeme odgovora i veća propusnost, ali gubite mogućnost skaliranja pozadine.
Čak i ako imate dva modula u pozadini, sa stalnom vezom, promet će uvijek ići na jedan od njih.
Može li se to popraviti?
Budući da Kubernetes ne zna kako uravnotežiti trajne veze, ovaj zadatak pada na vas.
Usluge su skup IP adresa i priključaka koji se nazivaju krajnje točke.
Vaša aplikacija može dobiti popis krajnjih točaka od usluge i odlučiti kako distribuirati zahtjeve između njih. Možete otvoriti trajnu vezu sa svakim modulom i uravnotežiti zahtjeve između tih veza koristeći kružni postupak.
Kôd na strani klijenta koji je odgovoran za balansiranje trebao bi slijediti ovu logiku:
Dobijte popis krajnjih točaka od usluge.
Otvorite trajnu vezu za svaku krajnju točku.
Kada je potrebno podnijeti zahtjev, koristite jednu od otvorenih veza.
Redovito ažurirajte popis krajnjih točaka, stvorite nove ili zatvorite stare trajne veze ako se popis promijeni.
Ovako će to izgledati.
Umjesto da prvi modul pošalje zahtjev usluzi, možete uravnotežiti zahtjeve na strani klijenta:
Morate napisati kod koji pita koji su podovi dio usluge:
Kada imate popis, spremite ga na strani klijenta i upotrijebite ga za povezivanje s podovima:
Vi ste odgovorni za algoritam balansiranja opterećenja:
Sada se postavlja pitanje: odnosi li se ovaj problem samo na HTTP keep-alive?
Balansiranje opterećenja na strani klijenta
HTTP nije jedini protokol koji može koristiti stalne TCP veze.
Ako vaša aplikacija koristi bazu podataka, tada se TCP veza ne otvara svaki put kada trebate napraviti zahtjev ili dohvatiti dokument iz baze podataka.
Umjesto toga, otvara se i koristi stalna TCP veza s bazom podataka.
Ako je vaša baza podataka postavljena na Kubernetes i pristup je omogućen kao usluga, tada ćete naići na iste probleme opisane u prethodnom odjeljku.
Jedna replika baze podataka bit će opterećenija od ostalih. Kube-proxy i Kubernetes neće pomoći u ravnoteži veza. Morate paziti na uravnoteženje upita u svojoj bazi podataka.
Ovisno o tome koju biblioteku koristite za povezivanje s bazom podataka, možete imati različite opcije za rješavanje ovog problema.
Ispod je primjer pristupa MySQL klasteru baze podataka iz Node.js:
var mysql = require('mysql');
var poolCluster = mysql.createPoolCluster();
var endpoints = /* retrieve endpoints from the Service */
for (var [index, endpoint] of endpoints) {
poolCluster.add(`mysql-replica-${index}`, endpoint);
}
// Make queries to the clustered MySQL database
Postoje mnogi drugi protokoli koji koriste stalne TCP veze:
WebSockets i zaštićeni WebSockets
HTTP / 2
gRPC
RSockets
AMQP
Već ste trebali biti upoznati s većinom ovih protokola.
Ali ako su ti protokoli toliko popularni, zašto ne postoji standardizirano rješenje za balansiranje? Zašto se logika klijenta mora promijeniti? Postoji li izvorno Kubernetes rješenje?
Kube-proxy i iptables dizajnirani su da pokriju najčešće slučajeve upotrebe prilikom postavljanja na Kubernetes. Ovo je zbog praktičnosti.
Ako koristite web uslugu koja izlaže REST API, imate sreće - u ovom slučaju se ne koriste trajne TCP veze, možete koristiti bilo koju Kubernetes uslugu.
Ali kad jednom počnete koristiti trajne TCP veze, morat ćete smisliti kako ravnomjerno rasporediti opterećenje po pozadinskim dijelovima. Kubernetes ne sadrži gotova rješenja za ovaj slučaj.
Međutim, svakako postoje opcije koje mogu pomoći.
Balansiranje dugotrajnih veza u Kubernetesu
U Kubernetesu postoje četiri vrste usluga:
KlasterIP
Čvorni priključak
LoadBalancer
Bezglav
Prve tri usluge rade na temelju virtualne IP adrese, koju koristi kube-proxy za izgradnju pravila iptables. Ali temeljna osnova svih usluga je usluga bez glave.
Bezglava usluga nema nikakvu IP adresu povezanu sa sobom i pruža samo mehanizam za dohvaćanje popisa IP adresa i priključaka mahuna (krajnjih točaka) povezanih s njom.
Sve usluge temelje se na bezglavom servisu.
Usluga ClusterIP je usluga bez glave s nekim dodacima:
Upravljački sloj mu dodjeljuje IP adresu.
Kube-proxy generira potrebna pravila za iptables.
Na ovaj način možete ignorirati kube-proxy i izravno upotrijebiti popis krajnjih točaka dobiven od headless usluge za uravnoteženje opterećenja vaše aplikacije.
Ali kako možemo dodati sličnu logiku svim aplikacijama raspoređenim u klasteru?
Ako je vaša aplikacija već implementirana, ovaj se zadatak može činiti nemogućim. Međutim, postoji alternativna opcija.
Service Mesh će vam pomoći
Vjerojatno ste već primijetili da je strategija balansiranja opterećenja na strani klijenta prilično standardna.
Kada se aplikacija pokrene, ona:
Dobiva popis IP adresa od usluge.
Otvara i održava skup veza.
Povremeno ažurira skup dodavanjem ili uklanjanjem krajnjih točaka.
Nakon što aplikacija želi podnijeti zahtjev, ona:
Odabire dostupnu vezu pomoću neke logike (npr. kružno).
Izvršava zahtjev.
Ovi koraci rade i za WebSockets, gRPC i AMQP veze.
Ovu logiku možete odvojiti u zasebnu biblioteku i koristiti je u svojim aplikacijama.
Međutim, umjesto toga možete koristiti servisne mreže kao što su Istio ili Linkerd.
Service Mesh proširuje vašu aplikaciju procesom koji:
Automatski traži IP adrese servisa.
Testira veze kao što su WebSockets i gRPC.
Uravnotežuje zahtjeve pomoću ispravnog protokola.
Service Mesh pomaže u upravljanju prometom unutar klastera, ali je dosta intenzivan. Ostale opcije su korištenje biblioteka trećih strana kao što je Netflix Ribbon ili programabilnih proxyja kao što je Envoy.
Što se događa ako zanemarite probleme s ravnotežom?
Možete odabrati da ne koristite balansiranje opterećenja, a da ipak ne primijetite nikakve promjene. Pogledajmo nekoliko scenarija rada.
Ako imate više klijenata nego poslužitelja, to i nije tako velik problem.
Recimo da postoji pet klijenata koji se spajaju na dva poslužitelja. Čak i ako nema balansiranja, koristit će se oba poslužitelja:
Veze možda nisu ravnomjerno raspoređene: možda su četiri klijenta spojena na isti poslužitelj, ali postoji velika vjerojatnost da će se koristiti oba poslužitelja.
Problematičniji je suprotan scenarij.
Ako imate manje klijenata, a više poslužitelja, vaši resursi mogu biti nedovoljno iskorišteni i pojavit će se potencijalno usko grlo.
Recimo da postoje dva klijenta i pet poslužitelja. U najboljem slučaju postojat će dvije stalne veze na dva poslužitelja od pet.
Preostali poslužitelji bit će u stanju mirovanja:
Ako ova dva poslužitelja ne mogu obraditi zahtjeve klijenata, horizontalno skaliranje neće pomoći.
Zaključak
Kubernetes usluge dizajnirane su za rad u većini standardnih scenarija web aplikacija.
Međutim, nakon što počnete raditi s aplikacijskim protokolima koji koriste trajne TCP veze, kao što su baze podataka, gRPC ili WebSockets, usluge više nisu prikladne. Kubernetes ne pruža interne mehanizme za balansiranje trajnih TCP veza.
To znači da morate pisati aplikacije imajući na umu balansiranje na strani klijenta.