Kubernetesin kuormituksen tasaus ja pitkäikäisten yhteyksien skaalaus
Tämä artikkeli auttaa sinua ymmärtämään, kuinka kuormituksen tasapainotus toimii Kubernetesissa, mitä tapahtuu, kun skaalataan pitkäikäisiä yhteyksiä ja miksi sinun tulee harkita asiakaspuolen tasapainottamista, jos käytät HTTP/2-, gRPC-, RSockets-, AMQP- tai muita pitkäikäisiä protokollia. .
Hieman siitä, kuinka liikenne jaetaan uudelleen Kubernetesissa
Kubernetes tarjoaa kaksi kätevää abstraktiota sovellusten käyttöönottoon: palvelut ja käyttöönotot.
Käyttöönotot kuvaavat, kuinka ja kuinka monta kopiota sovelluksestasi tulee olla käynnissä kulloinkin. Jokainen sovellus otetaan käyttöön Pod-yksikönä ja sille on määritetty IP-osoite.
Palvelut ovat toiminnaltaan samanlaisia kuin kuormituksen tasapainottaja. Ne on suunniteltu jakamaan liikennettä useisiin koteloihin.
Katsotaan miltä se näyttää.
Alla olevassa kaaviossa näet kolme esimerkkiä samasta sovelluksesta ja kuormituksen tasapainottimesta:
Kuormituksen tasapainotinta kutsutaan palveluksi ja sille on määritetty IP-osoite. Kaikki saapuvat pyynnöt ohjataan johonkin ryhmistä:
Käyttöönottoskenaario määrittää sovelluksen esiintymien lukumäärän. Sinun ei melkein koskaan tarvitse laajentaa suoraan alla:
Jokaiselle podille on määritetty oma IP-osoite:
On hyödyllistä ajatella palveluita IP-osoitteiden kokoelmana. Aina kun käytät palvelua, yksi IP-osoitteista valitaan luettelosta ja sitä käytetään kohdeosoitteena.
Se näyttää tältä.
Palveluun on vastaanotettu curl 10.96.45.152 pyyntö:
Palvelu valitsee määränpääksi yhden kolmesta pod-osoitteesta:
Liikenne ohjataan tiettyyn ryhmään:
Jos sovelluksesi koostuu käyttöliittymästä ja taustajärjestelmästä, sinulla on kummallekin sekä palvelu että käyttöönotto.
Kun käyttöliittymä tekee pyynnön taustajärjestelmälle, sen ei tarvitse tietää tarkalleen kuinka monta podia backend palvelee: niitä voi olla yksi, kymmenen tai sata.
Lisäksi käyttöliittymä ei tiedä mitään taustaosaa palvelevien pod-osoitteiden osoitteista.
Kun käyttöliittymä tekee pyynnön taustajärjestelmälle, se käyttää taustapalvelun IP-osoitetta, joka ei muutu.
Näin se näyttää.
Under 1 pyytää sisäistä taustakomponenttia. Sen sijaan, että valitsisi tietyn taustajärjestelmän, se lähettää palvelulle pyynnön:
Palvelu valitsee kohdeosoitteeksi yhden taustapäätyypeistä:
Liikenne kulkee Pod 1:stä Pod 5:een, jonka palvelu valitsee:
Alle 1 ei tiedä tarkalleen kuinka monta alle 5:n kaltaista podia on piilotettu palvelun taakse:
Mutta miten palvelu tarkalleen jakaa pyynnöt? Näyttää siltä, että käytetään round robin -tasapainotusta? Selvitetään se.
Tasapainotus Kubernetes-palveluissa
Kubernetes-palveluita ei ole olemassa. Palvelulle, jolle on määritetty IP-osoite ja portti, ei ole prosessia.
Voit varmistaa tämän kirjautumalla mihin tahansa klusterin solmuun ja suorittamalla komennon netstat -ntlp.
Et edes löydä palvelulle osoitettua IP-osoitetta.
Palvelun IP-osoite sijaitsee ohjauskerroksessa, ohjaimessa ja tallennettu tietokantaan - etcd. Samaa osoitetta käyttää toinen komponentti - kube-proxy.
Kube-välityspalvelin vastaanottaa luettelon kaikkien palveluiden IP-osoitteista ja luo joukon iptables-sääntöjä klusterin jokaiselle solmulle.
Näissä säännöissä sanotaan: "Jos näemme palvelun IP-osoitteen, meidän on muutettava pyynnön kohdeosoitetta ja lähetettävä se johonkin podista."
Palvelun IP-osoitetta käytetään vain sisääntulopisteenä, eikä sitä palvele mikään tätä IP-osoitetta ja porttia kuunteleva prosessi.
Katsotaanpa tätä.
Tarkastellaan kolmen solmun klusteria. Jokaisessa solmussa on tyynyt:
Beigiksi maalatut sidotut palot ovat osa palvelua. Koska palvelua ei ole olemassa prosessina, se näkyy harmaana:
Ensimmäinen ryhmä pyytää palvelua ja sen on siirryttävä johonkin liitetyistä podista:
Mutta palvelua ei ole olemassa, prosessia ei ole olemassa. Kuinka se toimii?
Ennen kuin pyyntö poistuu solmusta, se käy läpi iptables-säännöt:
Iptables-säännöt tietävät, että palvelua ei ole olemassa, ja korvaavat sen IP-osoitteen jollakin kyseiseen palveluun liittyvien pod-osoitteiden IP-osoitteista:
Pyyntö saa kelvollisen IP-osoitteen kohdeosoitteeksi ja se käsitellään normaalisti:
Verkkotopologiasta riippuen pyyntö saavuttaa lopulta pod:
Voiko iptables tasapainottaa kuormitusta?
Ei, iptablesia käytetään suodatukseen, eikä niitä ole suunniteltu tasapainottamiseen.
On kuitenkin mahdollista kirjoittaa joukko sääntöjä, jotka toimivat kuten pseudo-tasapainotin.
Ja tämä on juuri se, mitä Kubernetesissa toteutetaan.
Jos sinulla on kolme podia, kube-proxy kirjoittaa seuraavat säännöt:
Valitse ensimmäinen osa todennäköisyydellä 33 %, muussa tapauksessa siirry seuraavaan sääntöön.
Valitse toinen todennäköisyydellä 50%, muussa tapauksessa siirry seuraavaan sääntöön.
Valitse alta kolmas.
Tämä järjestelmä johtaa siihen, että jokainen pod valitaan 33 %:n todennäköisyydellä.
Eikä ole mitään takeita siitä, että Pod 2 valitaan seuraavaksi Pod 1:n jälkeen.
Huomata: iptables käyttää satunnaisjakaumaa tilastollista moduulia. Näin ollen tasapainotusalgoritmi perustuu satunnaiseen valintaan.
Nyt kun ymmärrät, miten palvelut toimivat, katsotaanpa mielenkiintoisempia palveluskenaarioita.
Pitkäikäiset yhteydet Kubernetesissa eivät skaalaudu oletusarvoisesti
Jokaista HTTP-pyyntöä käyttöliittymästä taustajärjestelmään palvelee erillinen TCP-yhteys, joka avataan ja suljetaan.
Jos käyttöliittymä lähettää taustalle 100 pyyntöä sekunnissa, 100 erilaista TCP-yhteyttä avataan ja suljetaan.
Voit lyhentää pyyntöjen käsittelyaikaa ja kuormitusta avaamalla yhden TCP-yhteyden ja käyttämällä sitä kaikissa myöhemmissä HTTP-pyynnöissä.
HTTP-protokollassa on ominaisuus nimeltä HTTP keep-alive tai yhteyden uudelleenkäyttö. Tässä tapauksessa yhtä TCP-yhteyttä käytetään useiden HTTP-pyyntöjen ja vastausten lähettämiseen ja vastaanottamiseen:
Tämä ominaisuus ei ole oletusarvoisesti käytössä: sekä palvelin että asiakas on määritettävä vastaavasti.
Itse asennus on yksinkertainen ja käytettävissä useimmille ohjelmointikielille ja ympäristöille.
Tässä muutamia linkkejä esimerkkeihin eri kielillä:
Mitä tapahtuu, jos käytämme Keep-alive-toimintoa Kubernetes-palvelussa?
Oletetaan, että sekä käyttöliittymä että taustaosa tukevat pysymistä elossa.
Meillä on yksi kopio käyttöliittymästä ja kolme kopiota taustaosasta. Käyttöliittymä tekee ensimmäisen pyynnön ja avaa TCP-yhteyden taustajärjestelmään. Pyyntö saapuu palveluun, kohdeosoitteeksi valitaan yksi taustapäätyypeistä. Taustaosa lähettää vastauksen ja käyttöliittymä vastaanottaa sen.
Toisin kuin tavallisessa tilanteessa, jossa TCP-yhteys suljetaan vastauksen saatuaan, se pidetään nyt avoinna muita HTTP-pyyntöjä varten.
Mitä tapahtuu, jos käyttöliittymä lähettää lisää pyyntöjä taustalle?
Näiden pyyntöjen välittämiseen käytetään avointa TCP-yhteyttä, kaikki pyynnöt menevät samaan taustajärjestelmään, johon ensimmäinen pyyntö meni.
Eikö iptablesin pitäisi jakaa liikennettä uudelleen?
Ei tässä tapauksessa.
Kun TCP-yhteys luodaan, se käy läpi iptables-säännöt, jotka valitsevat tietyn taustajärjestelmän, johon liikenne kulkee.
Koska kaikki myöhemmät pyynnöt ovat jo avoimessa TCP-yhteydessä, iptables-sääntöjä ei enää kutsuta.
Katsotaan miltä se näyttää.
Ensimmäinen pod lähettää pyynnön palveluun:
Tiedät jo mitä seuraavaksi tapahtuu. Palvelua ei ole olemassa, mutta on olemassa iptables-sääntöjä, jotka käsittelevät pyynnön:
Yksi taustapäätyypeistä valitaan kohdeosoitteeksi:
Pyyntö saapuu koteloon. Tässä vaiheessa kahden podin välille muodostetaan jatkuva TCP-yhteys:
Kaikki myöhemmät pyynnöt ensimmäisestä ryhmästä kulkevat jo muodostetun yhteyden kautta:
Tuloksena on nopeampi vasteaika ja suurempi suorituskyky, mutta menetät kyvyn skaalata taustaa.
Vaikka taustajärjestelmässä olisi kaksi podia, liikenne kulkee aina yhteen niistä.
Voiko tämän korjata?
Koska Kubernetes ei osaa tasapainottaa pysyviä yhteyksiä, tämä tehtävä kuuluu sinulle.
Palvelut ovat kokoelma IP-osoitteita ja portteja, joita kutsutaan päätepisteiksi.
Sovelluksesi voi saada luettelon päätepisteistä palvelusta ja päättää, kuinka pyynnöt jaetaan niiden välillä. Voit avata jatkuvan yhteyden jokaiseen podiin ja tasapainottaa pyyntöjä näiden yhteyksien välillä käyttämällä kiertoajoa.
Tasapainotuksesta vastaavan asiakaspuolen koodin tulee noudattaa tätä logiikkaa:
Hanki luettelo päätepisteistä palvelusta.
Avaa pysyvä yhteys jokaiselle päätepisteelle.
Kun pyyntö on tehtävä, käytä jotakin avoimista yhteyksistä.
Päivitä päätepisteiden luettelo säännöllisesti, luo uusia tai sulje vanhat pysyvät yhteydet, jos luettelo muuttuu.
Tältä se tulee näyttämään.
Sen sijaan, että ensimmäinen pod lähettäisi pyynnön palveluun, voit tasapainottaa pyyntöjä asiakaspuolella:
Sinun on kirjoitettava koodi, joka kysyy, mitkä podit ovat osa palvelua:
Kun sinulla on luettelo, tallenna se asiakaspuolelle ja käytä sitä yhteyden muodostamiseen podeihin:
Olet vastuussa kuormituksen tasapainotusalgoritmista:
Nyt herää kysymys: koskeeko tämä ongelma vain HTTP:n elossapitoa?
Asiakaspuolen kuormituksen tasapainotus
HTTP ei ole ainoa protokolla, joka voi käyttää pysyviä TCP-yhteyksiä.
Jos sovelluksesi käyttää tietokantaa, TCP-yhteyttä ei avata joka kerta, kun sinun on tehtävä pyyntö tai haettava asiakirja tietokannasta.
Sen sijaan jatkuva TCP-yhteys tietokantaan avataan ja sitä käytetään.
Jos tietokanta on otettu käyttöön Kubernetesissa ja pääsy tarjotaan palveluna, kohtaat samat ongelmat, jotka on kuvattu edellisessä osassa.
Yksi tietokannan replika ladataan enemmän kuin muut. Kube-proxy ja Kubernetes eivät auta tasapainottamaan yhteyksiä. Sinun on huolehdittava kyselyjen tasapainottamisesta tietokantaasi.
Riippuen siitä, mitä kirjastoa käytät yhteyden muodostamiseen tietokantaan, sinulla voi olla erilaisia vaihtoehtoja tämän ongelman ratkaisemiseksi.
Alla on esimerkki MySQL-tietokantaklusterin käyttämisestä Node.js:stä:
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
On monia muita protokollia, jotka käyttävät pysyviä TCP-yhteyksiä:
WebSocketit ja suojatut WebSocketit
HTTP-/ 2
gRPC
Rpistokkeet
AMQP
Sinun pitäisi jo tuntea useimmat näistä protokollista.
Mutta jos nämä protokollat ovat niin suosittuja, miksi ei ole olemassa standardoitua tasapainotusratkaisua? Miksi asiakaslogiikkaa pitää muuttaa? Onko olemassa natiivi Kubernetes-ratkaisua?
Kube-proxy ja iptables on suunniteltu kattamaan yleisimmät käyttötapaukset Kubernetesin käyttöönoton yhteydessä. Tämä on mukavuussyistä.
Jos käytät verkkopalvelua, joka paljastaa REST API:n, olet onnekas - tässä tapauksessa pysyviä TCP-yhteyksiä ei käytetä, voit käyttää mitä tahansa Kubernetes-palvelua.
Mutta kun alat käyttää pysyviä TCP-yhteyksiä, sinun on selvitettävä, kuinka jakaa kuorma tasaisesti taustaohjelmien kesken. Kubernetes ei sisällä valmiita ratkaisuja tähän tapaukseen.
On kuitenkin varmasti vaihtoehtoja, jotka voivat auttaa.
Tasapainottaa pitkäikäisiä yhteyksiä Kubernetesissa
Kubernetesissa on neljä erilaista palvelua:
KlusteriIP
Solmuportti
LoadBalancer
kannaton
Kolme ensimmäistä palvelua toimivat virtuaalisen IP-osoitteen perusteella, jota kube-proxy käyttää iptables-sääntöjen rakentamiseen. Mutta kaikkien palveluiden perusta on päätön palvelu.
Päättömään palveluun ei ole liitetty IP-osoitetta, ja se tarjoaa vain mekanismin siihen liittyvien koteloiden (päätepisteiden) IP-osoitteiden ja porttien noutamiseen.
Kaikki palvelut perustuvat päättömään palveluun.
ClusterIP-palvelu on päätön palvelu, jossa on joitain lisäyksiä:
Hallintakerros määrittää sille IP-osoitteen.
Kube-proxy luo tarvittavat iptables-säännöt.
Tällä tavalla voit ohittaa kube-proxyn ja käyttää suoraan päättömästä palvelusta saatua päätepisteluetteloa sovelluksesi kuormituksen tasapainottamiseen.
Mutta kuinka voimme lisätä samanlaisen logiikan kaikkiin klusterin sovelluksiin?
Jos sovelluksesi on jo otettu käyttöön, tämä tehtävä saattaa tuntua mahdottomalta. Vaihtoehtoinen vaihtoehto on kuitenkin olemassa.
Service Mesh auttaa sinua
Olet luultavasti jo huomannut, että asiakaspuolen kuormantasausstrategia on melko vakio.
Kun sovellus käynnistyy, se:
Hakee luettelon IP-osoitteista palvelusta.
Avaa ja ylläpitää yhteyspoolia.
Päivittää ajoittain poolia lisäämällä tai poistamalla päätepisteitä.
Kun sovellus haluaa tehdä pyynnön, se:
Valitsee käytettävissä olevan yhteyden jollakin logiikalla (esim. round-robin).
Suorittaa pyynnön.
Nämä vaiheet toimivat sekä WebSockets-, gRPC- että AMQP-yhteyksissä.
Voit erottaa tämän logiikan erilliseksi kirjastoksi ja käyttää sitä sovelluksissasi.
Voit kuitenkin käyttää sen sijaan palveluverkkoja, kuten Istio tai Linkerd.
Service Mesh laajentaa sovellustasi prosessilla, joka:
Hakee automaattisesti palvelun IP-osoitteita.
Testaa yhteyksiä, kuten WebSockets ja gRPC.
Tasapainottaa pyynnöt käyttämällä oikeaa protokollaa.
Service Mesh auttaa hallitsemaan liikennettä klusterin sisällä, mutta se on melko resurssiintensiivistä. Muita vaihtoehtoja ovat käyttää kolmannen osapuolen kirjastoja, kuten Netflix Ribbon, tai ohjelmoitavia välityspalvelimia, kuten Envoy.
Mitä tapahtuu, jos jätät huomioimatta tasapainotusongelmat?
Voit halutessasi olla käyttämättä kuormituksen tasapainotusta etkä silti huomaa muutoksia. Katsotaanpa muutamia työskenaarioita.
Jos sinulla on enemmän asiakkaita kuin palvelimia, tämä ei ole niin suuri ongelma.
Oletetaan, että on viisi asiakasta, jotka muodostavat yhteyden kahteen palvelimeen. Vaikka tasapainotusta ei olisi, molempia palvelimia käytetään:
Yhteydet eivät ehkä ole jakautuneet tasaisesti: ehkä neljä asiakasta on yhteydessä samaan palvelimeen, mutta on hyvä mahdollisuus, että molempia palvelimia käytetään.
Ongelmallisempi on päinvastainen skenaario.
Jos sinulla on vähemmän asiakkaita ja enemmän palvelimia, resurssit voivat olla vajaakäytössä ja mahdollinen pullonkaula ilmaantuu.
Oletetaan, että siellä on kaksi asiakasta ja viisi palvelinta. Parhaassa tapauksessa kaksi pysyvää yhteyttä kahteen palvelimeen viidestä.
Loput palvelimet ovat käyttämättömänä:
Jos nämä kaksi palvelinta eivät pysty käsittelemään asiakaspyyntöjä, vaakasuuntainen skaalaus ei auta.
Johtopäätös
Kubernetes-palvelut on suunniteltu toimimaan useimmissa tavallisissa verkkosovellusskenaarioissa.
Kun kuitenkin alat työskennellä pysyviä TCP-yhteyksiä käyttävien sovellusprotokollien, kuten tietokantojen, gRPC:n tai WebSockettien, kanssa, palvelut eivät enää sovellu. Kubernetes ei tarjoa sisäisiä mekanismeja pysyvien TCP-yhteyksien tasapainottamiseksi.
Tämä tarkoittaa, että sinun on kirjoitettava sovelluksia asiakaspuolen tasapainotusta ajatellen.