ProHoster > Blog > Adminisztráció > Terheléselosztás és hosszú élettartamú kapcsolatok méretezése a Kubernetesben
Terheléselosztás és hosszú élettartamú kapcsolatok méretezése a Kubernetesben
Ez a cikk segít megérteni, hogyan működik a terheléselosztás a Kubernetesben, mi történik a hosszú élettartamú kapcsolatok méretezésekor, és miért érdemes megfontolni az ügyféloldali kiegyenlítést, ha HTTP/2, gRPC, RSockets, AMQP vagy más hosszú élettartamú protokollokat használ. .
Egy kicsit arról, hogyan osztják újra a forgalmat Kubernetesben
A Kubernetes két kényelmes absztrakciót biztosít az alkalmazások üzembe helyezéséhez: Szolgáltatások és Telepítések.
A telepítések leírják, hogyan és hány példányban kell futnia az alkalmazásnak egy adott időpontban. Minden alkalmazás Pod-ként kerül telepítésre, és hozzá van rendelve egy IP-cím.
A szolgáltatások funkciójukban hasonlóak a terheléselosztóhoz. Úgy tervezték, hogy a forgalmat több pod között osszák el.
Lássuk, hogyan néz ki.
Az alábbi ábrán ugyanannak az alkalmazásnak és egy terheléselosztónak három példánya látható:
A terheléselosztót szolgáltatásnak hívják, és hozzá van rendelve egy IP-cím. Minden bejövő kérés át lesz irányítva az egyik sorba:
A telepítési forgatókönyv meghatározza az alkalmazás példányainak számát. Szinte soha nem kell közvetlenül a következő alatt bővíteni:
Minden podhoz saját IP-cím van hozzárendelve:
Hasznos a szolgáltatásokat IP-címek gyűjteményének tekinteni. Minden alkalommal, amikor hozzáfér a szolgáltatáshoz, az egyik IP-címet kiválasztja a listából, és célcímként használja.
Ez így néz ki.
Curl 10.96.45.152 kérés érkezett a szolgáltatáshoz:
A szolgáltatás a három podcím egyikét választja célként:
A forgalom egy adott csoportba van átirányítva:
Ha az alkalmazás egy előtérből és egy háttérrendszerből áll, akkor mindegyikhez lesz egy szolgáltatás és egy központi telepítés.
Amikor a frontend kérelmet küld a háttérrendszernek, nem kell pontosan tudnia, hogy a háttérrendszer hány podot szolgál ki: lehet egy, tíz vagy száz.
Ezenkívül a frontend semmit sem tud a háttérrendszert kiszolgáló podok címeiről.
Amikor a frontend kérelmet küld a háttérrendszernek, a háttérszolgáltatás IP-címét használja, amely nem változik.
Így néz ki.
Az 1 alatt a belső háttérkomponenst kéri. Ahelyett, hogy egy konkrétat választana ki a háttérrendszer számára, kérést küld a szolgáltatásnak:
A szolgáltatás az egyik háttérrendszert választja ki célcímként:
A forgalom a szolgáltatás által kiválasztott Pod 1-ről Pod 5-re megy:
Az 1 alatti nem tudja pontosan, hány 5 alatti pod van elrejtve a szolgáltatás mögött:
De pontosan hogyan osztja el a szolgáltatás a kéréseket? Úgy tűnik, a körmérkőzéses egyensúlyozást használják? Találjuk ki.
Kiegyensúlyozás a Kubernetes szolgáltatásokban
A Kubernetes szolgáltatások nem léteznek. A szolgáltatáshoz nincs folyamat, amelyhez IP-cím és port van hozzárendelve.
Ezt úgy ellenőrizheti, hogy bejelentkezik a fürt bármely csomópontjába, és futtatja a netstat -ntlp parancsot.
Még a szolgáltatáshoz kiosztott IP-címet sem fogja megtalálni.
A szolgáltatás IP-címe a vezérlőrétegben, a vezérlőben található, és rögzítve van az adatbázisban - stb. Ugyanezt a címet egy másik összetevő is használja - a kube-proxy.
A Kube-proxy megkapja az összes szolgáltatáshoz tartozó IP-címek listáját, és iptables-szabályokat állít elő a fürt minden csomópontján.
Ezek a szabályok a következőket mondják: "Ha látjuk a szolgáltatás IP-címét, módosítanunk kell a kérés célcímét, és el kell küldenünk az egyik podba."
A szolgáltatás IP-címe csak belépési pontként használatos, és nem szolgálja ki az adott IP-címet és portot figyelő folyamat.
Nézzük ezt.
Tekintsünk egy három csomópontból álló klasztert. Minden csomópontnak vannak hüvelyei:
A bézsre festett kötött hüvelyek a szolgáltatás részét képezik. Mivel a szolgáltatás nem folyamatként létezik, szürkén jelenik meg:
Az első pod szolgáltatást kér, és az egyik társított podhoz kell mennie:
De a szolgáltatás nem létezik, a folyamat nem létezik. Hogyan működik?
Mielőtt a kérés elhagyná a csomópontot, végigmegy az iptables szabályokon:
Az iptables szabályok tudják, hogy a szolgáltatás nem létezik, és lecserélik az IP-címét a szolgáltatáshoz társított podok egyik IP-címére:
A kérelem egy érvényes IP-címet kap célcímként, és a szokásos módon kerül feldolgozásra:
A hálózati topológiától függően a kérés végül eléri a pod-ot:
Tud iptables terhelési egyensúlyt?
Nem, az iptable-okat szűrésre használják, és nem egyensúlyozásra tervezték.
Lehetőség van azonban olyan szabályok felírására, amelyek hasonlóan működnek ál-egyensúlyozó.
És pontosan ezt valósítják meg a Kubernetesben.
Ha három podja van, a kube-proxy a következő szabályokat írja le:
Válassza ki az első alcímet 33%-os valószínűséggel, ellenkező esetben lépjen a következő szabályra.
Válassza ki a másodikat 50%-os valószínűséggel, ellenkező esetben lépjen a következő szabályra.
Alul válassza ki a harmadikat.
Ez a rendszer azt eredményezi, hogy minden pod 33%-os valószínűséggel kerül kiválasztásra.
És nincs garancia arra, hogy a Pod 2-t választják a Pod 1 után.
Megjegyzés: Az iptables véletlen eloszlású statisztikai modult használ. Így a kiegyenlítő algoritmus véletlenszerű kiválasztáson alapul.
Most, hogy megértette a szolgáltatások működését, nézzünk meg érdekesebb szolgáltatási forgatókönyveket.
A Kubernetes hosszú élettartamú kapcsolatai alapértelmezés szerint nem skálázódnak
Az előtértől a háttér felé tartó minden HTTP-kérést külön TCP-kapcsolat szolgál ki, amelyet megnyit és zár.
Ha az előtér másodpercenként 100 kérést küld a háttérrendszernek, akkor 100 különböző TCP-kapcsolat nyílik meg és zár be.
Csökkentheti a kérésfeldolgozási időt és a terhelést, ha megnyit egy TCP-kapcsolatot, és ezt használja az összes további HTTP-kéréshez.
A HTTP protokoll rendelkezik a HTTP életben tartás, vagyis a kapcsolat újrafelhasználása nevű funkcióval. Ebben az esetben egyetlen TCP-kapcsolatot használnak több HTTP kérés és válasz küldésére és fogadására:
Ez a funkció alapértelmezés szerint nincs engedélyezve: a szervert és a klienst is ennek megfelelően kell konfigurálni.
Maga a beállítás egyszerű és a legtöbb programozási nyelv és környezet számára elérhető.
Íme néhány hivatkozás a különböző nyelvű példákhoz:
Mi történik, ha a Keep-alive-t használjuk egy Kubernetes szolgáltatásban?
Tegyük fel, hogy a frontend és a háttérrendszer egyaránt támogatja az életben tartást.
Van egy példányunk az előtérből és három másolatunk a háttérből. A frontend végrehajtja az első kérést, és megnyit egy TCP-kapcsolatot a háttérrel. A kérés eléri a szolgáltatást, célcímként az egyik backend pod kerül kiválasztásra. A háttérrendszer választ küld, a frontend pedig megkapja azt.
A szokásos helyzettől eltérően, amikor a TCP-kapcsolat a válasz beérkezése után bezárul, most nyitva marad a további HTTP-kérések számára.
Mi történik, ha a frontend több kérést küld a háttérrendszernek?
Ezeknek a kéréseknek a továbbításához egy nyitott TCP-kapcsolat kerül felhasználásra, és minden kérés ugyanahhoz a háttérrendszerhez fog eljutni, ahol az első kérés.
Az iptables-nak nem kellene újraosztania a forgalmat?
Ebben az esetben nem.
A TCP-kapcsolat létrehozásakor az iptables szabályokon megy keresztül, amelyek kiválasztanak egy adott háttérrendszert, ahová a forgalom irányul.
Mivel minden további kérés már nyitott TCP-kapcsolaton van, az iptables szabályok többé nem kerülnek meghívásra.
Lássuk, hogyan néz ki.
Az első pod kérést küld a szolgáltatásnak:
Már tudod, mi fog történni ezután. A szolgáltatás nem létezik, de vannak iptables szabályok, amelyek feldolgozzák a kérést:
Az egyik háttér-pod lesz kiválasztva célcímként:
A kérés eléri a tokot. Ezen a ponton állandó TCP-kapcsolat jön létre a két pod között:
Az első podból érkező minden további kérés a már létrehozott kapcsolaton keresztül megy keresztül:
Az eredmény gyorsabb válaszidő és nagyobb átviteli sebesség, de elveszíti a háttér méretezésének lehetőségét.
Még ha két pod van is a háttérben, állandó kapcsolat mellett, a forgalom mindig az egyikre fog menni.
Ezt meg lehet oldani?
Mivel a Kubernetes nem tudja, hogyan kell egyensúlyba hozni a tartós kapcsolatokat, ez a feladat rád hárul.
A szolgáltatások végpontoknak nevezett IP-címek és portok gyűjteménye.
Az alkalmazás lekérheti a végpontok listáját a szolgáltatástól, és eldöntheti, hogyan osztja el a kéréseket közöttük. Állandó kapcsolatot nyithat meg minden egyes podhoz, és kör-robin segítségével egyensúlyba hozhatja a kéréseket ezek között a kapcsolatok között.
A kiegyenlítésért felelős ügyféloldali kódnak a következő logikát kell követnie:
Szerezze be a végpontok listáját a szolgáltatásból.
Nyisson meg egy állandó kapcsolatot minden végponthoz.
Ha kérést kell benyújtani, használja a nyitott kapcsolatok egyikét.
Rendszeresen frissítse a végpontok listáját, hozzon létre újakat, vagy zárja be a régi állandó kapcsolatokat, ha a lista megváltozik.
Így fog kinézni.
Ahelyett, hogy az első pod küldi el a kérést a szolgáltatásnak, az ügyféloldalon egyensúlyozhatja a kéréseket:
Olyan kódot kell írnia, amely megkérdezi, hogy mely podok tartoznak a szolgáltatáshoz:
Ha megvan a lista, mentse el az ügyféloldalra, és használja a podokhoz való csatlakozáshoz:
Ön felelős a terheléselosztási algoritmusért:
Most felmerül a kérdés: ez a probléma csak a HTTP életben tartásra vonatkozik?
Ügyféloldali terheléselosztás
A HTTP nem az egyetlen protokoll, amely állandó TCP-kapcsolatokat tud használni.
Ha az alkalmazás adatbázist használ, akkor a TCP-kapcsolat nem nyílik meg minden alkalommal, amikor kérést kell benyújtania vagy dokumentumot kell lekérnie az adatbázisból.
Ehelyett egy állandó TCP-kapcsolatot nyit meg és használ az adatbázishoz.
Ha az adatbázis a Kubernetesen van telepítve, és a hozzáférés szolgáltatásként van biztosítva, akkor az előző részben leírt problémákkal fog szembesülni.
Az egyik adatbázis-replika jobban betöltődik, mint a többi. A Kube-proxy és a Kubernetes nem segít kiegyensúlyozni a kapcsolatokat. Gondoskodnia kell a lekérdezések és az adatbázis közötti egyensúlyról.
Attól függően, hogy melyik könyvtárat használja az adatbázishoz való csatlakozáshoz, különböző lehetőségek közül választhat a probléma megoldására.
Az alábbiakban egy MySQL adatbázis-fürt Node.js-ből való elérésére látható példa:
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
Sok más protokoll is állandó TCP-kapcsolatot használ:
WebSockets és biztonságos WebSockets
HTTP / 2
gRPC
RSockets
AMQP
A legtöbb ilyen protokollt már ismernie kell.
De ha ezek a protokollok olyan népszerűek, miért nem létezik szabványosított kiegyenlítő megoldás? Miért kell az ügyfél logikáját megváltoztatni? Van natív Kubernetes megoldás?
A Kube-proxy és az iptables úgy lett kialakítva, hogy a Kubernetes rendszerbe történő telepítéskor a leggyakoribb használati eseteket lefedjék. Ez a kényelem kedvéért.
Ha olyan webszolgáltatást használ, amely egy REST API-t tesz közzé, akkor szerencséje van – ebben az esetben a rendszer nem használ állandó TCP-kapcsolatokat, bármelyik Kubernetes szolgáltatást használhatja.
De miután elkezdi használni a tartós TCP-kapcsolatokat, ki kell találnia, hogyan lehet egyenletesen elosztani a terhelést a háttérrendszerek között. A Kubernetes erre az esetre nem tartalmaz kész megoldásokat.
Azonban biztosan vannak olyan lehetőségek, amelyek segíthetnek.
A hosszú életű kapcsolatok kiegyensúlyozása Kubernetesben
A Kubernetesben négyféle szolgáltatás létezik:
ClusterIP
Csomópont port
Terhelés elosztó
Fejetlen
Az első három szolgáltatás virtuális IP-címen működik, amelyet a kube-proxy használ az iptables szabályok felépítéséhez. De minden szolgáltatás alapvető alapja a fejetlen szolgáltatás.
A fej nélküli szolgáltatáshoz nem tartozik semmilyen IP-cím, és csak egy mechanizmust biztosít az IP-címek és a hozzá társított podok (végpontok) portjainak lekéréséhez.
Minden szolgáltatás a fej nélküli szolgáltatáson alapul.
A ClusterIP szolgáltatás egy fej nélküli szolgáltatás néhány kiegészítéssel:
A felügyeleti réteg hozzárendel egy IP-címet.
A Kube-proxy előállítja a szükséges iptables szabályokat.
Így figyelmen kívül hagyhatja a kube-proxyt, és közvetlenül használhatja a fej nélküli szolgáltatásból kapott végpontok listáját az alkalmazás terheléselosztásához.
De hogyan adhatunk hozzá hasonló logikát a fürtben telepített összes alkalmazáshoz?
Ha az alkalmazás már telepítve van, ez a feladat lehetetlennek tűnhet. Van azonban egy alternatív lehetőség.
A Service Mesh segít Önnek
Valószínűleg már észrevette, hogy az ügyféloldali terheléselosztási stratégia meglehetősen szabványos.
Amikor az alkalmazás elindul, akkor:
Lekéri az IP-címek listáját a szolgáltatástól.
Megnyitja és karbantartja a kapcsolati készletet.
Rendszeresen frissíti a készletet végpontok hozzáadásával vagy eltávolításával.
Ha az alkalmazás kérelmet szeretne benyújtani, akkor:
Kiválaszt egy elérhető kapcsolatot valamilyen logika segítségével (pl. körmérkőzés).
Végrehajtja a kérést.
Ezek a lépések WebSockets, gRPC és AMQP kapcsolatok esetén is működnek.
Ezt a logikát külön könyvtárba választhatja, és felhasználhatja alkalmazásaiban.
Ehelyett azonban használhat szolgáltatáshálókat, például az Istio-t vagy a Linkerd-et.
A Service Mesh egy olyan folyamattal egészíti ki az alkalmazást, amely:
Automatikusan megkeresi a szolgáltatás IP-címeit.
Olyan kapcsolatokat tesztel, mint a WebSockets és a gRPC.
Kiegyensúlyozza a kéréseket a megfelelő protokoll használatával.
A Service Mesh segít a fürtön belüli forgalom kezelésében, de meglehetősen erőforrás-igényes. További lehetőségek a harmadik féltől származó könyvtárak, például a Netflix Ribbon vagy a programozható proxyk, például az Envoy használata.
Mi történik, ha figyelmen kívül hagyja az egyensúlyi problémákat?
Dönthet úgy, hogy nem használja a terheléselosztást, és továbbra sem vesz észre semmilyen változást. Nézzünk néhány munkaforgatókönyvet.
Ha több kliensünk van, mint szerverünk, ez nem olyan nagy probléma.
Tegyük fel, hogy öt kliens csatlakozik két szerverhez. Még akkor is, ha nincs egyensúlyozás, mindkét szervert használni fogja:
Lehet, hogy a kapcsolatok nem egyenletesen oszlanak el: lehet, hogy négy kliens csatlakozik ugyanahhoz a szerverhez, de jó eséllyel mindkét szervert használni fogják.
A problémásabb az ellenkező forgatókönyv.
Ha kevesebb kliense és több szervere van, előfordulhat, hogy erőforrásai alul vannak kihasználva, és potenciális szűk keresztmetszetek jelennek meg.
Tegyük fel, hogy két kliens és öt szerver van. A legjobb esetben két állandó kapcsolat lesz ötből kettő szerverrel.
A többi szerver tétlen lesz:
Ha ez a két szerver nem tudja kezelni az ügyfélkéréseket, a vízszintes méretezés nem segít.
Következtetés
A Kubernetes-szolgáltatásokat úgy tervezték, hogy a legtöbb szabványos webalkalmazás-forgatókönyvben működjenek.
Ha azonban elkezd dolgozni állandó TCP-kapcsolatokat használó alkalmazásprotokollokkal, például adatbázisokkal, gRPC-vel vagy WebSocketekkel, a szolgáltatások már nem alkalmasak. A Kubernetes nem biztosít belső mechanizmusokat az állandó TCP-kapcsolatok kiegyensúlyozására.
Ez azt jelenti, hogy az alkalmazásokat az ügyféloldali kiegyensúlyozás figyelembevételével kell írnia.