Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes
Aquest article us ajudarà a entendre com funciona l'equilibri de càrrega a Kubernetes, què passa en escalar connexions de llarga durada i per què hauríeu de considerar l'equilibri del costat del client si feu servir HTTP/2, gRPC, RSockets, AMQP o altres protocols de llarga durada. . 

Una mica sobre com es redistribueix el trànsit a Kubernetes 

Kubernetes ofereix dues abstraccions convenients per desplegar aplicacions: Serveis i Desplegaments.

Els desplegaments descriuen com i quantes còpies de la vostra aplicació s'han d'executar en un moment donat. Cada aplicació es desplega com a Pod i se li assigna una adreça IP.

Els serveis són similars en funció a un equilibrador de càrrega. Estan dissenyats per distribuir el trànsit entre múltiples pods.

Anem a veure com es veu.

  1. Al diagrama següent podeu veure tres instàncies de la mateixa aplicació i un equilibrador de càrrega:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  2. L'equilibrador de càrrega s'anomena servei i se li assigna una adreça IP. Qualsevol sol·licitud entrant es redirigeix ​​a un dels pods:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  3. L'escenari de desplegament determina el nombre d'instàncies de l'aplicació. Gairebé mai no haureu d'expandir-vos directament sota:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  4. Cada pod té assignada la seva pròpia adreça IP:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

És útil pensar en els serveis com una col·lecció d'adreces IP. Cada vegada que accediu al servei, es selecciona una de les adreces IP de la llista i s'utilitza com a adreça de destinació.

Es veu així.

  1. Es rep una sol·licitud curl 10.96.45.152 al servei:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  2. El servei selecciona una de les tres adreces de pod com a destinació:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  3. El trànsit es redirigeix ​​a un pod específic:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

Si la vostra aplicació consta d'un front-end i un backend, tindreu un servei i un desplegament per a cadascun.

Quan el frontend fa una sol·licitud al backend, no necessita saber exactament quants pods serveix el backend: n'hi pot haver un, deu o cent.

A més, el frontend no sap res sobre les adreces dels pods que serveixen al backend.

Quan el frontend fa una sol·licitud al backend, utilitza l'adreça IP del servei de backend, que no canvia.

Així és com es veu.

  1. Under 1 sol·licita el component de backend intern. En lloc de seleccionar-ne un específic per al backend, fa una sol·licitud al servei:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  2. El servei selecciona un dels pods de backend com a adreça de destinació:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  3. El trànsit va del Pod 1 al Pod 5, seleccionat pel servei:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  4. Menors d'1 no sap exactament quants pods com els menors de 5 s'amaguen darrere del servei:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

Però, com distribueix exactament les peticions el servei? Sembla que s'utilitza l'equilibri round-robin? Anem a esbrinar-ho. 

Equilibri en els serveis de Kubernetes

Els serveis de Kubernetes no existeixen. No hi ha cap procés per al servei que tingui assignada una adreça IP i un port.

Podeu verificar-ho iniciant sessió a qualsevol node del clúster i executant l'ordre netstat -ntlp.

Ni tan sols podreu trobar l'adreça IP assignada al servei.

L'adreça IP del servei es troba a la capa de control, al controlador, i es registra a la base de dades, etcd. La mateixa adreça l'utilitza un altre component: kube-proxy.
Kube-proxy rep una llista d'adreces IP per a tots els serveis i genera un conjunt de regles iptables a cada node del clúster.

Aquestes regles diuen: "Si veiem l'adreça IP del servei, hem de modificar l'adreça de destinació de la sol·licitud i enviar-la a un dels pods".

L'adreça IP del servei només s'utilitza com a punt d'entrada i cap procés que escolta aquesta adreça IP i aquest port.

Mirem això

  1. Considereu un clúster de tres nodes. Cada node té beines:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  2. Les beines lligades pintades de beix formen part del servei. Com que el servei no existeix com a procés, es mostra en gris:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  3. El primer pod sol·licita un servei i ha d'anar a un dels pods associats:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  4. Però el servei no existeix, el procés no existeix. Com funciona?

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  5. Abans que la sol·licitud surti del node, passa per les regles d'iptables:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  6. Les regles d'iptables saben que el servei no existeix i substitueixen la seva adreça IP per una de les adreces IP dels pods associades a aquest servei:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  7. La sol·licitud rep una adreça IP vàlida com a adreça de destinació i es processa normalment:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  8. Depenent de la topologia de la xarxa, la sol·licitud arriba finalment al pod:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

Es pot equilibrar la càrrega d'iptables?

No, els iptables s'utilitzen per filtrar i no s'han dissenyat per a l'equilibri.

Tanmateix, és possible escriure un conjunt de regles que funcionin com pseudo-equilibrador.

I això és exactament el que s'implementa a Kubernetes.

Si teniu tres pods, kube-proxy escriurà les regles següents:

  1. Seleccioneu el primer sub amb una probabilitat del 33%, en cas contrari aneu a la següent regla.
  2. Trieu el segon amb una probabilitat del 50%, en cas contrari aneu a la següent regla.
  3. Seleccioneu el tercer a sota.

Aquest sistema fa que cada pod sigui seleccionat amb una probabilitat del 33%.

Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

I no hi ha cap garantia que el Pod 2 sigui escollit després del Pod 1.

Nota: iptables utilitza un mòdul estadístic amb distribució aleatòria. Per tant, l'algorisme d'equilibri es basa en la selecció aleatòria.

Ara que enteneu com funcionen els serveis, mirem escenaris de serveis més interessants.

Les connexions de llarga vida a Kubernetes no s'escalen per defecte

Cada sol·licitud HTTP des de l'interfície fins al backend es dóna per una connexió TCP independent, que s'obre i es tanca.

Si la interfície envia 100 sol·licituds per segon al backend, s'obren i es tanquen 100 connexions TCP diferents.

Podeu reduir el temps de processament i la càrrega de les sol·licituds obrint una connexió TCP i utilitzant-la per a totes les sol·licituds HTTP posteriors.

El protocol HTTP té una funció anomenada HTTP keep-alive o reutilització de la connexió. En aquest cas, s'utilitza una única connexió TCP per enviar i rebre diverses sol·licituds i respostes HTTP:

Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

Aquesta característica no està habilitada per defecte: tant el servidor com el client s'han de configurar en conseqüència.

La configuració en si és senzilla i accessible per a la majoria de llenguatges i entorns de programació.

Aquí teniu alguns enllaços a exemples en diferents idiomes:

Què passa si fem servir Keep-alive en un servei de Kubernetes?
Suposem que tant el frontend com el backend admeten es mantenen vius.

Tenim una còpia del frontend i tres còpies del backend. L'interfície fa la primera sol·licitud i obre una connexió TCP al backend. La sol·licitud arriba al servei, es selecciona un dels pods de backend com a adreça de destinació. El backend envia una resposta i el frontend la rep.

A diferència de la situació habitual en què la connexió TCP es tanca després de rebre una resposta, ara es manté oberta per a més sol·licituds HTTP.

Què passa si el frontend envia més sol·licituds al backend?

Per reenviar aquestes sol·licituds, s'utilitzarà una connexió TCP oberta, totes les sol·licituds aniran al mateix backend on va anar la primera sol·licitud.

Els iptables no haurien de redistribuir el trànsit?

No en aquest cas.

Quan es crea una connexió TCP, passa per les regles d'iptables, que seleccionen un backend específic on anirà el trànsit.

Com que totes les peticions posteriors es troben en una connexió TCP ja oberta, les regles d'iptables ja no es criden.

Anem a veure com es veu.

  1. El primer pod envia una sol·licitud al servei:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  2. Ja sabeu què passarà després. El servei no existeix, però hi ha regles d'iptables que processaran la sol·licitud:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  3. Se seleccionarà un dels pods de fons com a adreça de destinació:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  4. La petició arriba al pod. En aquest punt, s'establirà una connexió TCP persistent entre els dos pods:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  5. Qualsevol sol·licitud posterior del primer pod passarà per la connexió ja establerta:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

El resultat és un temps de resposta més ràpid i un rendiment més elevat, però perds la capacitat d'escalar el backend.

Fins i tot si teniu dos pods al backend, amb una connexió constant, el trànsit sempre anirà a un d'ells.

Això es pot arreglar?

Com que Kubernetes no sap com equilibrar les connexions persistents, aquesta tasca us correspon.

Els serveis són una col·lecció d'adreces IP i ports anomenats punts finals.

La vostra aplicació pot obtenir una llista de punts finals del servei i decidir com distribuir les sol·licituds entre ells. Podeu obrir una connexió persistent a cada pod i equilibrar les sol·licituds entre aquestes connexions utilitzant el round-robin.

O aplicar més algorismes d'equilibri complexos.

El codi del costat del client responsable de l'equilibri hauria de seguir aquesta lògica:

  1. Obteniu una llista de punts finals del servei.
  2. Obriu una connexió persistent per a cada punt final.
  3. Quan cal fer una sol·licitud, utilitzeu una de les connexions obertes.
  4. Actualitzeu regularment la llista de punts finals, creeu-ne de nous o tanqueu connexions persistents antigues si la llista canvia.

Així es veurà.

  1. En lloc que el primer pod enviï la sol·licitud al servei, podeu equilibrar les sol·licituds al costat del client:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  2. Heu d'escriure un codi que us demani quins pods formen part del servei:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  3. Un cop tingueu la llista, deseu-la al costat del client i utilitzeu-la per connectar-vos als pods:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

  4. Sou responsable de l'algorisme d'equilibri de càrrega:

    Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

Ara sorgeix la pregunta: aquest problema només s'aplica al manteniment HTTP?

Equilibri de càrrega del costat del client

HTTP no és l'únic protocol que pot utilitzar connexions TCP persistents.

Si la vostra aplicació utilitza una base de dades, no s'obre una connexió TCP cada vegada que necessiteu fer una sol·licitud o recuperar un document de la base de dades. 

En canvi, s'obre i s'utilitza una connexió TCP persistent a la base de dades.

Si la vostra base de dades està desplegada a Kubernetes i l'accés es proporciona com a servei, trobareu els mateixos problemes descrits a la secció anterior.

Una rèplica de base de dades estarà més carregada que les altres. Kube-proxy i Kubernetes no ajudaran a equilibrar les connexions. Heu de tenir cura d'equilibrar les consultes a la vostra base de dades.

Depenent de quina biblioteca utilitzeu per connectar-vos a la base de dades, podeu tenir diferents opcions per resoldre aquest problema.

A continuació es mostra un exemple d'accés a un clúster de bases de dades MySQL des 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

Hi ha molts altres protocols que utilitzen connexions TCP persistents:

  • WebSockets i WebSockets segurs
  • HTTP / 2
  • gRPC
  • RSockets
  • AMQP

Ja hauríeu d'estar familiaritzat amb la majoria d'aquests protocols.

Però si aquests protocols són tan populars, per què no hi ha una solució d'equilibri estandarditzada? Per què cal canviar la lògica del client? Hi ha una solució nativa de Kubernetes?

Kube-proxy i iptables estan dissenyats per cobrir els casos d'ús més habituals quan es despleguen a Kubernetes. Això és per comoditat.

Si utilitzeu un servei web que exposa una API REST, esteu de sort; en aquest cas, no s'utilitzen connexions TCP persistents, podeu utilitzar qualsevol servei de Kubernetes.

Però un cop comenceu a utilitzar connexions TCP persistents, haureu d'esbrinar com distribuir uniformement la càrrega entre els backends. Kubernetes no conté solucions preparades per a aquest cas.

No obstant això, sens dubte hi ha opcions que poden ajudar.

Equilibrant connexions de llarga vida a Kubernetes

Hi ha quatre tipus de serveis a Kubernetes:

  1. ClústerIP
  2. Port del node
  3. LoadBalancer
  4. Sense cap

Els tres primers serveis funcionen en funció d'una adreça IP virtual, que kube-proxy utilitza per crear regles d'iptables. Però la base fonamental de tots els serveis és un servei sense cap.

El servei sense cap no té cap adreça IP associada i només proporciona un mecanisme per recuperar una llista d'adreces IP i ports dels pods (punts finals) associats amb ell.

Tots els serveis es basen en el servei sense cap.

El servei ClusterIP és un servei sense cap amb algunes addicions: 

  1. La capa de gestió li assigna una adreça IP.
  2. Kube-proxy genera les regles iptables necessàries.

D'aquesta manera, podeu ignorar kube-proxy i utilitzar directament la llista de punts finals obtinguts del servei sense cap per equilibrar la càrrega de la vostra aplicació.

Però, com podem afegir una lògica similar a totes les aplicacions desplegades al clúster?

Si la vostra aplicació ja està implementada, aquesta tasca pot semblar impossible. Tanmateix, hi ha una opció alternativa.

Service Mesh t'ajudarà

Probablement ja us heu adonat que l'estratègia d'equilibri de càrrega del costat del client és bastant estàndard.

Quan s'inicia l'aplicació:

  1. Obté una llista d'adreces IP del servei.
  2. Obre i manté un grup de connexions.
  3. Actualitza periòdicament el grup afegint o eliminant punts finals.

Un cop l'aplicació vol fer una sol·licitud, aquesta:

  1. Selecciona una connexió disponible utilitzant alguna lògica (per exemple, round-robin).
  2. Executa la petició.

Aquests passos funcionen tant per a connexions WebSockets, gRPC i AMQP.

Podeu separar aquesta lògica en una biblioteca independent i utilitzar-la a les vostres aplicacions.

Tanmateix, podeu utilitzar malles de servei com Istio o Linkerd.

Service Mesh augmenta la vostra aplicació amb un procés que:

  1. Cerca automàticament les adreces IP del servei.
  2. Prova connexions com ara WebSockets i gRPC.
  3. Equilibra les sol·licituds utilitzant el protocol correcte.

Service Mesh ajuda a gestionar el trànsit dins del clúster, però requereix força recursos. Altres opcions són utilitzar biblioteques de tercers com Netflix Ribbon o servidors intermediaris programables com Envoy.

Què passa si ignores els problemes d'equilibri?

Podeu optar per no utilitzar l'equilibri de càrrega i encara no noteu cap canvi. Vegem alguns escenaris de treball.

Si teniu més clients que servidors, aquest no és un problema tan gran.

Suposem que hi ha cinc clients que es connecten a dos servidors. Encara que no hi hagi equilibri, s'utilitzaran els dos servidors:

Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

És possible que les connexions no estiguin distribuïdes uniformement: potser quatre clients connectats al mateix servidor, però hi ha moltes possibilitats que s'utilitzin els dos servidors.

El que és més problemàtic és l'escenari contrari.

Si teniu menys clients i més servidors, és possible que els vostres recursos estiguin infrautilitzats i apareixerà un possible coll d'ampolla.

Suposem que hi ha dos clients i cinc servidors. En el millor dels casos, hi haurà dues connexions permanents a dos servidors de cada cinc.

La resta de servidors estaran inactius:

Equilibri de càrrega i escalat de connexions de llarga durada a Kubernetes

Si aquests dos servidors no poden gestionar les sol·licituds dels clients, l'escala horitzontal no ajudarà.

Conclusió

Els serveis de Kubernetes estan dissenyats per funcionar en la majoria dels escenaris estàndard d'aplicacions web.

Tanmateix, un cop comenceu a treballar amb protocols d'aplicació que utilitzen connexions TCP persistents, com ara bases de dades, gRPC o WebSockets, els serveis ja no són adequats. Kubernetes no proporciona mecanismes interns per equilibrar les connexions TCP persistents.

Això vol dir que heu d'escriure aplicacions tenint en compte l'equilibri del costat del client.

Traducció elaborada per l'equip Kubernetes aaS de Mail.ru.

Què més llegir sobre el tema:

  1. Tres nivells d'escala automàtica a Kubernetes i com utilitzar-los de manera eficaç
  2. Kubernetes amb esperit de pirateria amb una plantilla per a la implementació.
  3. El nostre canal de Telegram sobre transformació digital.

Font: www.habr.com

Afegeix comentari