Nou consells de rendiment de Kubernetes

Nou consells de rendiment de Kubernetes

Hola a tots! Em dic Oleg Sidorenkov, treballo a DomClick com a líder d'equip d'infraestructura. Fa més de tres anys que utilitzem el Cube per a la venda i durant aquest temps hem viscut molts moments interessants amb ell. Avui us explicaré com, amb l'enfocament adequat, podeu aprofitar encara més el rendiment de Vanilla Kubernetes per al vostre clúster. Preparats llestos ja!

Tots sabeu molt bé que Kubernetes és un sistema de codi obert escalable per a l'orquestració de contenidors; bé, o 5 binaris que fan màgia gestionant el cicle de vida dels vostres microserveis en un entorn de servidor. A més, es tracta d'una eina bastant flexible que es pot muntar com un constructor de Lego per a la màxima personalització per a diferents tasques.

I sembla que tot va bé: llenceu servidors al clúster, com llenya a una caixa de foc, i no coneixeu el dolor. Però si ets pel medi ambient, llavors pensaràs: "Com puc mantenir el foc a l'estufa i penedir-me del bosc?". En altres paraules, com trobar maneres de millorar la infraestructura i reduir costos.

1. Feu un seguiment dels recursos de l'equip i de les aplicacions

Nou consells de rendiment de Kubernetes

Un dels mètodes més banals però efectius és la introducció de peticions/límits. Separeu les aplicacions per espais de noms i els espais de noms per equips de desenvolupament. Configureu l'aplicació abans de desplegar valors per al consum de temps del processador, memòria, emmagatzematge efímer.

resources:
   requests:
     memory: 2Gi
     cpu: 250m
   limits:
     memory: 4Gi
     cpu: 500m

Per experiència, vam arribar a la conclusió: no val la pena inflar les sol·licituds des dels límits més de dues vegades. El volum del clúster es calcula en funció de les sol·licituds i, si configureu la diferència de recursos a les aplicacions, per exemple, de 5 a 10 vegades, imagineu què passarà amb el vostre node quan s'ompli de beines i de sobte rebi una càrrega. . Res de bo. Com a mínim, accelerant, i com a màxim, acomiadar-se del treballador i obtenir una càrrega cíclica a la resta de nodes després que les beines comencin a moure's.

A més, amb l'ajuda limitranges podeu establir valors de recursos per al contenidor a l'inici: mínim, màxim i predeterminat:

➜  ~ kubectl describe limitranges --namespace ops
Name:       limit-range
Namespace:  ops
Type        Resource           Min   Max   Default Request  Default Limit  Max Limit/Request Ratio
----        --------           ---   ---   ---------------  -------------  -----------------------
Container   cpu                50m   10    100m             100m           2
Container   ephemeral-storage  12Mi  8Gi   128Mi            4Gi            -
Container   memory             64Mi  40Gi  128Mi            128Mi          2

Recordeu limitar els recursos de l'espai de noms perquè una comanda no pugui prendre tots els recursos del clúster:

➜  ~ kubectl describe resourcequotas --namespace ops
Name:                   resource-quota
Namespace:              ops
Resource                Used          Hard
--------                ----          ----
limits.cpu              77250m        80
limits.memory           124814367488  150Gi
pods                    31            45
requests.cpu            53850m        80
requests.memory         75613234944   150Gi
services                26            50
services.loadbalancers  0             0
services.nodeports      0             0

Com podeu veure a la descripció resourcequotas, si l'ordre ops vol desplegar pods que consumiran 10 CPU més, el planificador no permetrà que es faci i emetrà un error:

Error creating: pods "nginx-proxy-9967d8d78-nh4fs" is forbidden: exceeded quota: resource-quota, requested: limits.cpu=5,requests.cpu=5, used: limits.cpu=77250m,requests.cpu=53850m, limited: limits.cpu=10,requests.cpu=10

Per resoldre un problema similar, podeu escriure una eina, per exemple, com aquest, que pot emmagatzemar i confirmar l'estat dels recursos de comandament.

2. Trieu el millor emmagatzematge de fitxers

Nou consells de rendiment de Kubernetes

Aquí m'agradaria tocar el tema dels volums persistents i el subsistema de disc dels nodes de treball de Kubernetes. Espero que ningú faci servir el "Cube" del disc dur en producció, però de vegades fins i tot amb un SSD normal ja no n'hi ha prou. Ens vam enfrontar a un problema tal que els registres estaven matant el disc per operacions d'E/S i aquí no hi ha moltes solucions:

  • Utilitzeu SSD d'alt rendiment o canvieu a NVMe (si gestioneu el vostre propi maquinari).

  • Disminueix el nivell de registre.

  • Feu un equilibri "intel·ligent" de les beines que violen el disc (podAntiAffinity).

La captura de pantalla de dalt mostra què passa amb nginx-ingress-controller amb un disc quan el registre access_logs està habilitat (~ 12 k registres/s). Aquest estat, per descomptat, pot provocar la degradació de totes les aplicacions d'aquest node.

Pel que fa a PV, per desgràcia, no ho he provat tot. espècie Volums persistents. Utilitzeu la millor opció que us convingui. Històricament ha passat al nostre país que una petita part dels serveis necessita volums RWX i fa molt de temps que van començar a utilitzar l'emmagatzematge NFS per a aquesta tasca. Barat i... prou. Per descomptat, vam menjar merda amb ell: estigues saludable, però vam aprendre a afinar-lo i ja no li fa mal el cap. I, si és possible, canvieu a l'emmagatzematge d'objectes S3.

3. Construeix imatges optimitzades

Nou consells de rendiment de Kubernetes

El millor és utilitzar imatges optimitzades per a contenidors perquè Kubernetes les pugui recuperar més ràpidament i executar-les de manera més eficient. 

L'optimització significa que les imatges:

  • contenir només una aplicació o realitzar només una funció;

  • mida petita, perquè les imatges grans es transmeten pitjor per la xarxa;

  • tenir punts finals de salut i preparació que Kubernetes pugui utilitzar per prendre mesures en cas d'inactivitat;

  • utilitzar sistemes operatius compatibles amb contenidors (com Alpine o CoreOS) que siguin més resistents als errors de configuració;

  • utilitzeu compilacions en diverses etapes perquè només pugueu desplegar aplicacions compilades i no les fonts que l'acompanyen.

Hi ha moltes eines i serveis que permeten comprovar i optimitzar imatges sobre la marxa. És important mantenir-los sempre actualitzats i segurs. Com a resultat, obteniu:

  1. Reducció de la càrrega de xarxa a tot el clúster.

  2. Disminució del temps d'inici del contenidor.

  3. Mida més petita de tot el vostre registre Docker.

4. Utilitzeu una memòria cau DNS

Nou consells de rendiment de Kubernetes

Si parlem de càrregues elevades, sense ajustar el sistema DNS del clúster, la vida és bastant dolenta. Hi havia una vegada, els desenvolupadors de Kubernetes van donar suport a la seva solució kube-dns. També es va implementar al nostre país, però aquest programari no s'adaptava especialment i no donava el rendiment requerit, tot i que, segons sembla, la tasca és senzilla. Llavors van aparèixer els coredns, als quals vam canviar i no coneixíem el dolor, més tard es va convertir en el servei DNS predeterminat a K8s. En algun moment, vam créixer fins a 40 mil rps al sistema DNS, i aquesta solució tampoc va ser suficient. Però, per una sort, va sortir Nodelocaldns, també conegut com a memòria cau local de nodes, també NodeLocal DNSCache.

Per què l'estem utilitzant? Hi ha un error al nucli de Linux que, quan accedeix múltiples a través de conntrack NAT a través d'UDP, provoca una condició de carrera per escriure a les taules de conntrack i es perd part del trànsit a través de NAT (cada viatge a través del Servei és NAT). Nodelocaldns soluciona aquest problema eliminant el NAT i actualitzant la connectivitat TCP a DNS amunt, així com a la memòria cau localment les consultes de DNS amunt (incloent una memòria cau negativa curta de 5 segons).

5. Escala les beines horitzontalment i verticalment automàticament

Nou consells de rendiment de Kubernetes

Podeu dir amb confiança que tots els vostres microserveis estan preparats per a un augment de la càrrega de dues a tres vegades? Com assignar correctament els recursos a les vostres aplicacions? Mantenir un parell de pods en execució per sobre de la càrrega de treball pot ser redundant, i mantenir-los una a esquena comporta el risc d'inactivitat d'un augment sobtat del trànsit al servei. La mitjana daurada ajuda a aconseguir l'encanteri de multiplicació com ara Escalador automàtic de pods horitzontals и Escalador automàtic de pod vertical.

VPA us permet augmentar automàticament les sol·licituds/límits dels vostres contenidors en un pod en funció de l'ús real. Com pot ser útil? Si teniu Pods que per algun motiu no es poden escalar horitzontalment (que no és del tot fiable), podeu provar de confiar en VPA per canviar els seus recursos. La seva característica és un sistema de recomanació basat en dades històriques i actuals del metric-server, de manera que si no voleu canviar les sol·licituds/límits automàticament, només podeu supervisar els recursos recomanats per als vostres contenidors i optimitzar la configuració per estalviar CPU i memòria. al clúster.

Nou consells de rendiment de KubernetesImatge extreta de https://levelup.gitconnected.com/kubernetes-autoscaling-101-cluster-autoscaler-horizontal-pod-autoscaler-and-vertical-pod-2a441d9ad231

El planificador de Kubernetes sempre es basa en les sol·licituds. Sigui quin sigui el valor que hi poseu, el planificador buscarà un node adequat basat en ell. El valor límit és necessari pel kublet per saber quan accelerar o matar una beina. I com que l'únic paràmetre important és el valor de les sol·licituds, VPA funcionarà amb ell. Sempre que escaleu la vostra aplicació verticalment, definiu quines han de ser les sol·licituds. I què passarà amb els límits llavors? Aquest paràmetre també s'escalarà proporcionalment.

Per exemple, aquí teniu la configuració típica del pod:

resources:
   requests:
     memory: 250Mi
     cpu: 200m
   limits:
     memory: 500Mi
     cpu: 350m

El motor de recomanacions determina que la vostra aplicació necessita 300 m de CPU i 500 Mi per funcionar correctament. Obtindreu aquests paràmetres:

resources:
   requests:
     memory: 500Mi
     cpu: 300m
   limits:
     memory: 1000Mi
     cpu: 525m

Com s'ha esmentat anteriorment, es tracta d'una escala proporcional basada en la relació de sol·licituds/límits del manifest:

  • CPU: 200m → 300m: relació 1:1.75;

  • Memòria: 250 Mi → 500 Mi: proporció 1:2.

Quant a HPA, aleshores el mecanisme de funcionament és més transparent. S'estableixen llindars per a mètriques com ara el processador i la memòria, i si la mitjana de totes les rèpliques supera el llindar, l'aplicació s'escala en +1 pod fins que el valor cau per sota del llindar o fins que s'arriba al nombre màxim de rèpliques.

Nou consells de rendiment de KubernetesImatge extreta de https://levelup.gitconnected.com/kubernetes-autoscaling-101-cluster-autoscaler-horizontal-pod-autoscaler-and-vertical-pod-2a441d9ad231

A més de les mètriques habituals com la CPU i la memòria, podeu establir llindars a les vostres mètriques personalitzades de Prometheus i treballar-hi si creieu que aquesta és la manera més precisa de determinar quan escalar la vostra aplicació. Un cop l'aplicació s'estabilitzi per sota del llindar mètric especificat, HPA començarà a escalar els Pods fins al nombre mínim de rèpliques o fins que la càrrega assoleixi el llindar.

6. No us oblideu de Node Affinity i Pod Affinity

Nou consells de rendiment de Kubernetes

No tots els nodes s'executen amb el mateix maquinari i no tots els pods necessiten executar aplicacions intensives en càlcul. Kubernetes us permet especificar l'especialització de nodes i pods utilitzant Afinitat de nodes и Pod afinitat.

Si teniu nodes adequats per a operacions intensives en càlcul, per obtenir la màxima eficiència, és millor vincular les aplicacions als nodes adequats. Per fer-ho, utilitzeu nodeSelector amb etiqueta de node.

Suposem que teniu dos nodes: un amb CPUType=HIGHFREQ i un gran nombre de nuclis ràpids, un altre amb MemoryType=HIGHMEMORY més memòria i rendiment més ràpid. La manera més senzilla és assignar un desplegament de pod a un node HIGHFREQafegint a la secció spec un selector com aquest:

…
nodeSelector:
	CPUType: HIGHFREQ

Una manera més costosa i específica de fer-ho és utilitzar nodeAffinity en camp affinity secció spec. Hi ha dues opcions:

  • requiredDuringSchedulingIgnoredDuringExecution: configuració difícil (el planificador només desplegarà pods en nodes específics (i en cap altre lloc));

  • preferredDuringSchedulingIgnoredDuringExecution: configuració suau (el planificador intentarà desplegar-se a nodes específics i, si falla, intentarà desplegar-se al següent node disponible).

Podeu especificar una sintaxi específica per gestionar les etiquetes de nodes, per exemple, In, NotIn, Exists, DoesNotExist, Gt o Lt. Tanmateix, recordeu que els mètodes complexos en llargues llistes d'etiquetes alentiran la presa de decisions en situacions crítiques. En altres paraules, no us compliqueu massa.

Com s'ha esmentat anteriorment, Kubernetes us permet establir l'enllaç dels pods actuals. És a dir, podeu fer que determinats pods funcionin juntament amb altres pods a la mateixa zona de disponibilitat (rellevant per als núvols) o nodes.

В podAffinity camps affinity secció spec estan disponibles els mateixos camps que en el cas de nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution и preferredDuringSchedulingIgnoredDuringExecution. L'única diferència és que matchExpressions enllaçarà els pods a un node que ja està executant un pod amb aquesta etiqueta.

Més Kubernetes ofereix un camp podAntiAffinity, que, en canvi, no enllaça un pod a un node amb pods específics.

Sobre les expressions nodeAffinity es pot donar el mateix consell: intenteu mantenir les regles senzilles i lògiques, no intenteu sobrecarregar l'especificació del pod amb un conjunt complex de regles. És molt fàcil crear una regla que no coincideixi amb les condicions del clúster, posant una càrrega addicional al planificador i degradant el rendiment general.

7. Tances i tolerància

Hi ha una altra manera de gestionar el planificador. Si teniu un clúster gran amb centenars de nodes i milers de microserveis, és molt difícil evitar que determinats pods estiguin allotjats per determinats nodes.

El mecanisme de taques - regles prohibidores - ajuda a això. Per exemple, podeu evitar que determinats nodes executin pods en determinats escenaris. Per aplicar la contaminació a un node específic, utilitzeu l'opció taint en kubectl. Especifiqueu la clau i el valor i, a continuació, tingueu com NoSchedule o NoExecute:

$ kubectl taint nodes node10 node-role.kubernetes.io/ingress=true:NoSchedule

També val la pena assenyalar que el mecanisme de contaminació admet tres efectes principals: NoSchedule, NoExecute и PreferNoSchedule.

  • NoSchedule significa que fins que hi hagi una entrada corresponent a l'especificació del pod tolerations, no es pot desplegar al node (en aquest exemple node10).

  • PreferNoSchedule - versió simplificada NoSchedule. En aquest cas, el planificador intentarà no assignar pods que no tinguin una entrada coincident. tolerations per node, però aquest no és un límit dur. Si no hi ha recursos al clúster, els pods començaran a desplegar-se en aquest node.

  • NoExecute - aquest efecte provoca una evacuació immediata de beines que no tenen una entrada coincident tolerations.

Curiosament, aquest comportament es pot anul·lar mitjançant el mecanisme de toleràncies. Això és convenient quan hi ha un node "prohibit" i només cal col·locar-hi serveis d'infraestructura. Com fer-ho? Només permeteu beines per a les quals hi hagi una tolerància adequada.

Aquí teniu l'aspecte de l'especificació del pod:

spec:
   tolerations:
     - key: "node-role.kubernetes.io/ingress"
        operator: "Equal"
        value: "true"
        effect: "NoSchedule"

Això no vol dir que durant la propera redistribució, el pod tocarà exactament aquest node, aquest no és el mecanisme d'afinitat del node i nodeSelector. Però combinant diverses funcions, podeu aconseguir una configuració del programador molt flexible.

8. Estableix la prioritat de desplegament del pod

El fet que hàgiu configurat els enllaços de pod a node no vol dir que tots els pods s'hagin de tractar amb la mateixa prioritat. Per exemple, és possible que vulgueu desplegar alguns Pods abans que d'altres.

Kubernetes ofereix diferents maneres d'establir la prioritat de pod i la preempció. La configuració consta de diverses parts: objecte PriorityClass i descripcions de camps priorityClassName a l'especificació del pod. Considereu un exemple:

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 99999
globalDefault: false
description: "This priority class should be used for very important pods only"

Creem PriorityClass, doneu-li un nom, una descripció i un valor. Com més alt value, com més alta sigui la prioritat. El valor pot ser qualsevol nombre enter de 32 bits inferior o igual a 1. Els valors més alts es reserven per als pods del sistema de missió crítica, que normalment no es poden anticipar. El desallotjament només es produirà si la càpsula d'alta prioritat no té on girar-se, llavors algunes de les càpsules d'un node concret seran evacuades. Si aquest mecanisme és massa rígid per a vosaltres, podeu afegir l'opció preemptionPolicy: Never, i aleshores no hi haurà cap preempció, el pod serà el primer de la cua i esperarà que el planificador hi trobi recursos gratuïts.

A continuació, creem un pod, en el qual especifiquem el nom priorityClassName:

apiVersion: v1
kind: Pod
metadata:
  name: static-web
  labels:
    role: myrole
 spec:
  containers:
    - name: web
      image: nginx
      ports:
        - name: web
          containerPort: 80
          protocol: TCP
  priorityClassName: high-priority
          

Pots crear tantes classes de prioritat com vulguis, tot i que es recomana no deixar-se portar amb això (per exemple, limitar-se a la prioritat baixa, mitjana i alta).

Així, si cal, podeu augmentar l'eficiència del desplegament de serveis crítics, com ara nginx-ingress-controller, coredns, etc.

9. Optimitzeu el vostre clúster ETCD

Nou consells de rendiment de Kubernetes

ETCD es pot anomenar el cervell de tot el clúster. És molt important mantenir el funcionament d'aquesta base de dades a un alt nivell, ja que la velocitat de les operacions al "Cube" depèn d'això. Una solució bastant estàndard i, al mateix temps, una bona solució seria mantenir un clúster ETCD als nodes mestres per tal de tenir un retard mínim al kube-apiserver. Si això no és possible, col·loqueu l'ETCD el més a prop possible, amb una bona amplada de banda entre els participants. També presteu atenció a quants nodes d'ETCD poden caure sense danyar el clúster.

Nou consells de rendiment de Kubernetes

Tingueu en compte que un augment excessiu del nombre de participants al clúster pot augmentar la tolerància a errors a costa del rendiment, tot hauria de ser amb moderació.

Si parlem de la configuració del servei, hi ha algunes recomanacions:

  1. Tenir un bon maquinari, basat en la mida del clúster (podeu llegir aquí).

  2. Ajusteu alguns paràmetres si heu estès un clúster entre un parell de DC o la vostra xarxa i els discs deixen molt que desitjar (podeu llegir aquí).

Conclusió

Aquest article descriu els punts que el nostre equip intenta complir. Aquesta no és una descripció pas a pas de les accions, sinó opcions que poden ser útils per optimitzar la sobrecàrrega d'un clúster. Està clar que cada clúster és únic a la seva manera, i les solucions d'ajust poden variar molt, per la qual cosa seria interessant rebre comentaris de tu: com controles el teu clúster de Kubernetes, com millores el seu rendiment. Comparteix la teva experiència als comentaris, serà interessant conèixer-la.

Font: www.habr.com