Ŝarĝbalancado kaj skalado longdaŭraj konektoj en Kubernetes
Ĉi tiu artikolo helpos vin kompreni kiel funkcias ŝarĝoekvilibro en Kubernetes, kio okazas dum skalado de longdaŭraj konektoj, kaj kial vi devus konsideri klientflankan ekvilibron se vi uzas HTTP/2, gRPC, RSockets, AMQP aŭ aliajn longdaŭrajn protokolojn. .
Iom pri kiel trafiko estas redistribuita en Kubernetes
Kubernetes provizas du oportunajn abstraktaĵojn por disfaldi aplikaĵojn: Servoj kaj Deplojoj.
Deplojoj priskribas kiel kaj kiom da kopioj de via aplikaĵo devus funkcii samtempe. Ĉiu aplikaĵo estas deplojita kiel Pod kaj ricevas IP-adreson.
Servoj estas similaj en funkcio al ŝarĝbalancilo. Ili estas dizajnitaj por distribui trafikon tra multoblaj podoj.
Ni vidu kiel ĝi aspektas.
En la malsupra diagramo vi povas vidi tri okazojn de la sama aplikaĵo kaj ŝarĝbalancilo:
La ŝarĝbalancilo nomiĝas Servo kaj ricevas IP-adreson. Ĉiu envenanta peto estas redirektita al unu el la podoj:
La deploja scenaro determinas la nombron da okazoj de la aplikaĵo. Vi preskaŭ neniam devos vastiĝi rekte sub:
Ĉiu podo ricevas sian propran IP-adreson:
Estas utile pensi pri servoj kiel kolekto de IP-adresoj. Ĉiufoje kiam vi aliras la servon, unu el la IP-adresoj estas elektita el la listo kaj uzata kiel la cela adreso.
Ĝi aspektas tiel.
Buklo 10.96.45.152 peto estas ricevita al la servo:
La servo elektas unu el tri pod-adresoj kiel cellokon:
Trafiko estas redirektita al specifa podo:
Se via aplikaĵo konsistas el fasado kaj backend, tiam vi havos kaj servon kaj deplojon por ĉiu.
Kiam la fasado faras peton al la backend, ĝi ne bezonas scii precize kiom da podoj servas la backend: povus esti unu, dek aŭ cent.
Ankaŭ, la fasado scias nenion pri la adresoj de la podoj servantaj la backend.
Kiam la fasado faras peton al la backend, ĝi uzas la IP-adreson de la backend-servo, kiu ne ŝanĝiĝas.
Jen kiel ĝi aspektas.
Sub 1 petoj la interna backend komponanto. Anstataŭ elekti specifan por la backend, ĝi faras peton al la servo:
La servo elektas unu el la backend pods kiel la cela adreso:
Trafiko iras de Pod 1 al Pod 5, elektita de la servo:
Sub 1 ne scias precize kiom da kapsuloj kiel sub 5 estas kaŝitaj malantaŭ la servo:
Sed kiel precize la servo distribuas petojn? Ŝajnas, ke cirkla-subskribolista ekvilibro estas uzata? Ni eltrovu ĝin.
Ekvilibro en Kubernetes-servoj
Kubernetes-servoj ne ekzistas. Ne ekzistas procezo por la servo al kiu estas asignita IP-adreso kaj haveno.
Vi povas kontroli ĉi tion ensalutante en iu ajn nodo en la areto kaj rulante la komandon netstat -ntlp.
Vi eĉ ne povos trovi la IP-adreson asignitan al la servo.
La IP-adreso de la servo situas en la kontroltavolo, en la regilo, kaj registrita en la datumbazo - ktp. La sama adreso estas uzata de alia komponanto - kube-proxy.
Kube-proxy ricevas liston de IP-adresoj por ĉiuj servoj kaj generas aron de iptables-reguloj sur ĉiu nodo en la areto.
Ĉi tiuj reguloj diras: "Se ni vidas la IP-adreson de la servo, ni devas modifi la cel-adreson de la peto kaj sendi ĝin al unu el la podoj."
La servo IP-adreso estas uzata nur kiel enirpunkto kaj ne estas servata de iu ajn procezo aŭskultanta tiun IP-adreson kaj havenon.
Ni rigardu ĉi tion.
Konsideru areton de tri nodoj. Ĉiu nodo havas podojn:
Ligitaj guŝoj pentritaj flavgrizaj estas parto de la servo. Ĉar la servo ne ekzistas kiel procezo, ĝi estas montrita en griza:
La unua podo petas servon kaj devas iri al unu el la rilataj podoj:
Sed la servo ne ekzistas, la procezo ne ekzistas. Kiel ĝi funkcias?
Antaŭ ol la peto forlasas la nodon, ĝi ekzamenas la regulojn de iptables:
La reguloj de iptables scias, ke la servo ne ekzistas kaj anstataŭigas ĝian IP-adreson per unu el la IP-adresoj de la podoj asociitaj kun tiu servo:
La peto ricevas validan IP-adreson kiel la cel-adreson kaj estas procesita normale:
Depende de la retotopologio, la peto finfine atingas la pod:
Ĉu iptables povas ŝarĝi ekvilibron?
Ne, iptables estas uzataj por filtri kaj ne estis desegnitaj por ekvilibrigi.
Tamen, eblas skribi aron da reguloj, kiuj funkcias kiel pseŭdo-balancilo.
Kaj ĉi tio estas ĝuste kio estas efektivigita en Kubernetes.
Se vi havas tri podojn, kube-proxy skribos la jenajn regulojn:
Elektu la unuan suban kun probablo de 33%, alie iru al la sekva regulo.
Elektu la duan kun probableco de 50%, alie iru al la sekva regulo.
Elektu la trian sub.
Ĉi tiu sistemo rezultigas, ke ĉiu balgo estas elektita kun probableco de 33%.
Kaj ne estas garantio, ke Pod 2 estos elektita poste post Pod 1.
Примечание: iptables uzas statistikan modulon kun hazarda distribuo. Tiel, la ekvilibra algoritmo estas bazita sur hazarda elekto.
Nun kiam vi komprenas kiel funkcias servoj, ni rigardu pli interesajn servajn scenarojn.
Longvivaj konektoj en Kubernetes ne skalas defaŭlte
Ĉiu HTTP-peto de la fasado al la backend estas servata de aparta TCP-konekto, kiu estas malfermita kaj fermita.
Se la fasado sendas 100 petojn sekundo al la backend, tiam 100 malsamaj TCP-konektoj estas malfermitaj kaj fermitaj.
Vi povas redukti peton prilaborado kaj ŝarĝo malfermante unu TCP-konekton kaj uzante ĝin por ĉiuj postaj HTTP-petoj.
La HTTP-protokolo havas funkcion nomitan HTTP-teni-viva aŭ konektoreuzo. En ĉi tiu kazo, ununura TCP-konekto estas uzata por sendi kaj ricevi plurajn HTTP-petojn kaj respondojn:
Ĉi tiu funkcio ne estas ebligita defaŭlte: kaj la servilo kaj la kliento devas esti agorditaj laŭe.
La aranĝo mem estas simpla kaj alirebla por plej multaj programlingvoj kaj medioj.
Jen kelkaj ligiloj al ekzemploj en malsamaj lingvoj:
Kio okazas se ni uzas keep-alive en Kubernetes-servo?
Ni supozu, ke ambaŭ la fasado kaj la backend subtenas vivaj.
Ni havas unu kopion de la fasado kaj tri kopiojn de la backend. La fasado faras la unuan peton kaj malfermas TCP-konekton al la backend. La peto atingas la servon, unu el la backend pods estas elektita kiel la cela adreso. La backend sendas respondon, kaj la fasado ricevas ĝin.
Male al la kutima situacio kie la TCP-konekto estas fermita post ricevado de respondo, ĝi nun estas konservita malfermita por pliaj HTTP-petoj.
Kio okazas se la fasado sendas pli da petoj al la backend?
Por plusendi ĉi tiujn petojn, malferma TCP-konekto estos uzata, ĉiuj petoj iros al la sama backend kie iris la unua peto.
Ĉu iptables ne devus redistribui la trafikon?
Ne en ĉi tiu kazo.
Kiam TCP-konekto estas kreita, ĝi trapasas iptables-regulojn, kiuj elektas specifan backend kie la trafiko iros.
Ĉar ĉiuj postaj petoj estas sur jam malfermita TCP-konekto, la reguloj de iptables ne plu estas nomitaj.
Ni vidu kiel ĝi aspektas.
La unua pod sendas peton al la servo:
Vi jam scias, kio okazos poste. La servo ne ekzistas, sed ekzistas reguloj de iptables, kiuj prilaboros la peton:
Unu el la malantaŭaj kapsuloj estos elektita kiel la cela adreso:
La peto atingas la balgon. Je ĉi tiu punkto, konstanta TCP-konekto inter la du balgoj estos establita:
Ajna posta peto de la unua pod trairos la jam establitan konekton:
La rezulto estas pli rapida responda tempo kaj pli alta trairo, sed vi perdas la kapablon grimpi la backend.
Eĉ se vi havas du podojn en la backend, kun konstanta konekto, trafiko ĉiam iros al unu el ili.
Ĉu tio povas esti riparita?
Ĉar Kubernetes ne scias kiel ekvilibrigi konstantajn ligojn, ĉi tiu tasko apartenas al vi.
Servoj estas kolekto de IP-adresoj kaj havenoj nomataj finpunktoj.
Via aplikaĵo povas ricevi liston de finpunktoj de la servo kaj decidi kiel distribui petojn inter ili. Vi povas malfermi konstantan konekton al ĉiu pod kaj ekvilibrigi petojn inter ĉi tiuj konektoj per cirkla-subskribolista.
La klientflanka kodo, kiu respondecas pri ekvilibro, devus sekvi ĉi tiun logikon:
Akiru liston de finpunktoj de la servo.
Malfermu konstantan konekton por ĉiu finpunkto.
Kiam peto devas esti farita, uzu unu el la malfermitaj konektoj.
Regule ĝisdatigu la liston de finpunktoj, kreu novajn aŭ fermu malnovajn konstantajn konektojn se la listo ŝanĝiĝas.
Jen kiel ĝi aspektos.
Anstataŭ ke la unua pod sendas la peton al la servo, vi povas ekvilibrigi petojn ĉe la klienta flanko:
Vi devas skribi kodon, kiu demandas, kiuj balgoj estas parto de la servo:
Post kiam vi havas la liston, konservu ĝin ĉe la klienta flanko kaj uzu ĝin por konekti al la podoj:
Vi respondecas pri la ŝarĝbalanca algoritmo:
Nun la demando ekestas: ĉu ĉi tiu problemo validas nur por HTTP-tenado?
Kliento-flanka ŝarĝoekvilibro
HTTP ne estas la sola protokolo, kiu povas uzi konstantajn TCP-konektojn.
Se via aplikaĵo uzas datumbazon, tiam TCP-konekto ne estas malfermita ĉiufoje kiam vi bezonas fari peton aŭ preni dokumenton el la datumbazo.
Anstataŭe, konstanta TCP-konekto al la datumbazo estas malfermita kaj uzata.
Se via datumbazo estas deplojita sur Kubernetes kaj aliro estas provizita kiel servo, tiam vi renkontos la samajn problemojn priskribitajn en la antaŭa sekcio.
Unu datumbaza kopio estos pli ŝarĝita ol la aliaj. Kube-proxy kaj Kubernetes ne helpos ekvilibrigi ligojn. Vi devas zorgi ekvilibrigi la demandojn al via datumbazo.
Depende de kiu biblioteko vi uzas por konekti al la datumbazo, vi eble havas malsamajn eblojn por solvi ĉi tiun problemon.
Malsupre estas ekzemplo de aliro al MySQL-datumbaza areto de 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
Estas multaj aliaj protokoloj, kiuj uzas konstantajn TCP-ligojn:
WebSockets kaj sekurigitaj WebSockets
HTTP / 2
gRPC
Ringoj
AMQP
Vi jam devus koni la plej multajn el ĉi tiuj protokoloj.
Sed se ĉi tiuj protokoloj estas tiel popularaj, kial ne ekzistas normigita ekvilibra solvo? Kial la klienta logiko bezonas ŝanĝiĝi? Ĉu ekzistas denaska solvo de Kubernetes?
Kube-proxy kaj iptables estas dizajnitaj por kovri plej oftajn uzkazojn dum deplojiĝo al Kubernetes. Ĉi tio estas por komforto.
Se vi uzas retservon, kiu elmontras REST-API, vi bonŝancas - ĉi-kaze, konstantaj TCP-konektoj ne estas uzataj, vi povas uzi ajnan Kubernetes-servon.
Sed post kiam vi komencos uzi konstantajn TCP-konektojn, vi devos eltrovi kiel egale distribui la ŝarĝon tra la backends. Kubernetes ne enhavas pretajn solvojn por ĉi tiu kazo.
Tamen, estas certe ebloj kiuj povas helpi.
Ekvilibro de longedaŭraj rilatoj en Kubernetes
Estas kvar specoj de servoj en Kubernetes:
ClusterIP
NodePort
LoadBalancer
Sen kapo
La unuaj tri servoj funkcias surbaze de virtuala IP-adreso, kiu estas uzata de kube-proxy por konstrui iptables-regulojn. Sed la fundamenta bazo de ĉiuj servoj estas senkapa servo.
La senkapa servo ne havas ajnan IP-adreson asociitan kun ĝi kaj nur provizas mekanismon por retrovi liston de IP-adresoj kaj havenoj de la balgoj (finpunktoj) asociitaj kun ĝi.
Ĉiuj servoj baziĝas sur la senkapa servo.
La ClusterIP-servo estas senkapa servo kun kelkaj aldonoj:
La administra tavolo asignas al ĝi IP-adreson.
Kube-proxy generas la necesajn regulojn de iptables.
Tiel vi povas ignori kube-proxy kaj rekte uzi la liston de finpunktoj akiritaj de la senkapa servo por ŝarĝi ekvilibron de via aplikaĵo.
Sed kiel ni povas aldoni similan logikon al ĉiuj aplikaĵoj deplojitaj en la areto?
Se via aplikaĵo jam estas deplojita, ĉi tiu tasko povas ŝajni neebla. Tamen, ekzistas alternativa opcio.
Servo Mesh helpos vin
Vi verŝajne jam rimarkis, ke la strategio pri ekvilibra ŝarĝo de la kliento estas sufiĉe norma.
Kiam la aplikaĵo komenciĝas, ĝi:
Akiras liston de IP-adresoj de la servo.
Malfermas kaj konservas konektan naĝejon.
Periode ĝisdatigas la naĝejon aldonante aŭ forigante finpunktojn.
Ĉi tiuj paŝoj funkcias por ambaŭ konektoj WebSockets, gRPC kaj AMQP.
Vi povas apartigi ĉi tiun logikon en apartan bibliotekon kaj uzi ĝin en viaj aplikaĵoj.
Tamen vi povas uzi servajn retojn kiel Istio aŭ Linkerd anstataŭe.
Service Mesh pliigas vian aplikaĵon per procezo, kiu:
Aŭtomate serĉas servon IP-adresojn.
Testas konektojn kiel WebSockets kaj gRPC.
Ekvilibras petojn uzante la ĝustan protokolon.
Service Mesh helpas administri trafikon ene de la areto, sed ĝi estas sufiĉe rimeda. Aliaj opcioj uzas triajn bibliotekojn kiel Netflix Ribbon aŭ programeblajn prokurojn kiel Envoy.
Kio okazas se vi ignoras ekvilibrajn problemojn?
Vi povas elekti ne uzi ŝarĝan ekvilibron kaj ankoraŭ ne rimarki ŝanĝojn. Ni rigardu kelkajn laborscenarojn.
Se vi havas pli da klientoj ol serviloj, ĉi tio ne estas tiom granda problemo.
Ni diru, ke estas kvin klientoj, kiuj konektas al du serviloj. Eĉ se ne ekzistas ekvilibro, ambaŭ serviloj estos uzataj:
Konektoj eble ne estas egale distribuitaj: eble kvar klientoj konektitaj al la sama servilo, sed estas bona ŝanco ke ambaŭ serviloj estos uzataj.
Kio estas pli problema estas la kontraŭa scenaro.
Se vi havas malpli da klientoj kaj pli da serviloj, viaj rimedoj povas esti subutiligitaj kaj ebla proplemkolo aperos.
Ni diru, ke estas du klientoj kaj kvin serviloj. En la plej bona kazo, estos du konstantaj konektoj al du serviloj el kvin.
La ceteraj serviloj estos neaktivaj:
Se ĉi tiuj du serviloj ne povas trakti klientajn petojn, horizontala skalo ne helpos.
konkludo
Kubernetes-servoj estas dezajnitaj por funkcii en la plej multaj normaj retprogramoj.
Tamen, kiam vi komencas labori kun aplikaj protokoloj, kiuj uzas konstantajn TCP-konektojn, kiel datumbazoj, gRPC aŭ WebSockets, servoj ne plu taŭgas. Kubernetes ne disponigas internajn mekanismojn por ekvilibrigi persistajn TCP-ligojn.
Ĉi tio signifas, ke vi devas skribi aplikojn kun klientflanka ekvilibro en menso.