10 greșeli frecvente când utilizați Kubernetes

Notă. transl.: Autorii acestui articol sunt ingineri de la o mică companie cehă, pipetail. Au reușit să alcătuiască o listă minunată de probleme [uneori banale, dar totuși] foarte presante și concepții greșite legate de funcționarea clusterelor Kubernetes.

10 greșeli frecvente când utilizați Kubernetes

De-a lungul anilor de utilizare a Kubernetes, am lucrat cu un număr mare de clustere (atât gestionate, cât și neadministrate - pe GCP, AWS și Azure). De-a lungul timpului, am început să observăm că unele greșeli se repetau în mod constant. Cu toate acestea, nu este nicio rușine în asta: majoritatea le-am făcut noi înșine!

Articolul conține cele mai frecvente erori și menționează, de asemenea, cum să le corectăm.

1. Resurse: solicitări și limite

Acest articol merită cu siguranță cea mai mare atenție și primul loc pe listă.

Cererea CPU de obicei fie nu se specifică deloc, fie are o valoare foarte mică (pentru a plasa cât mai multe poduri pe fiecare nod). Astfel, nodurile devin supraîncărcate. În perioadele de încărcare mare, puterea de procesare a nodului este utilizată pe deplin și o anumită sarcină de lucru primește doar ceea ce a „solicitat” de către Reglarea procesorului. Acest lucru duce la creșterea latenței aplicației, timeout-uri și alte consecințe neplăcute. (Citiți mai multe despre asta în cealaltă traducere recentă a noastră: „Limite CPU și throttling agresiv în Kubernetes„- aprox. traducere)

Cel mai bun efort (extrem nu recomandat):

resources: {}

Solicitare CPU extrem de scăzută (extrem de nu recomandat):

   resources:
      Requests:
        cpu: "1m"

Pe de altă parte, prezența unei limite CPU poate duce la omiterea nerezonabilă a ciclurilor de ceas de către pod-uri, chiar dacă procesorul nodului nu este încărcat complet. Din nou, acest lucru poate duce la creșterea întârzierilor. Controversa continuă în jurul parametrului Cota CPU CFS în nucleul Linux și throttling CPU în funcție de limitele stabilite, precum și dezactivarea cotei CFS... Din păcate, limitele CPU pot cauza mai multe probleme decât pot rezolva. Mai multe informații despre acest lucru pot fi găsite la linkul de mai jos.

Selectie excesiva (depășirea angajamentelor) problemele de memorie pot duce la probleme mai mari. Atingerea limitei CPU implică omiterea ciclurilor de ceas, în timp ce atingerea limitei de memorie implică uciderea podului. Ai observat vreodată OOMkill? Da, exact despre asta vorbim.

Doriți să minimizați probabilitatea ca acest lucru să se întâmple? Nu supraalocați memorie și utilizați QoS (Calitatea Serviciului) garantată setând cererea de memorie la limită (ca în exemplul de mai jos). Citiți mai multe despre asta în Prezentări Henning Jacobs (Inginer principal la Zalando).

Burstable (șansă mai mare de a deveni OOMkilled):

   resources:
      requests:
        memory: "128Mi"
        cpu: "500m"
      limits:
        memory: "256Mi"
        cpu: 2

Garantat:

   resources:
      requests:
        memory: "128Mi"
        cpu: 2
      limits:
        memory: "128Mi"
        cpu: 2

Ce poate ajuta la configurarea resurselor?

Cu metrics-server puteți vedea consumul curent de resurse CPU și utilizarea memoriei de către pod-uri (și containerele din interiorul acestora). Cel mai probabil, îl folosești deja. Doar rulați următoarele comenzi:

kubectl top pods
kubectl top pods --containers
kubectl top nodes

Cu toate acestea, ele arată doar utilizarea curentă. Vă poate oferi o idee aproximativă a ordinului de mărime, dar în cele din urmă veți avea nevoie istoricul modificărilor în timp (pentru a răspunde la întrebări precum: „Care a fost sarcina maximă a CPU?”, „Care a fost sarcina ieri dimineață?” etc.). Pentru aceasta puteți folosi Prometeu, DataDog și alte instrumente. Pur și simplu obțin valori de la metrics-server și le stochează, iar utilizatorul le poate interoga și le poate reprezenta în mod corespunzător.

VerticalPodAutoscaler Acesta permite a automatiza acest proces. Acesta urmărește istoricul utilizării CPU și a memoriei și stabilește noi solicitări și limite pe baza acestor informații.

Utilizarea eficientă a puterii de calcul nu este o sarcină ușoară. E ca și cum ai juca Tetris tot timpul. Dacă plătiți prea mult pentru putere de calcul cu un consum mediu scăzut (să zicem ~10%), vă recomandăm să priviți produse bazate pe AWS Fargate sau Virtual Kubelet. Acestea sunt construite pe un model de facturare fără server/plată-pe-utilizare, care se poate dovedi a fi mai ieftin în astfel de condiții.

2. Sonde de viabilitate și pregătire

În mod implicit, verificările de viabilitate și de pregătire nu sunt activate în Kubernetes. Și uneori uită să le pornească...

Dar cum altfel puteți iniția o repornire a serviciului în cazul unei erori fatale? Și de unde știe echilibratorul de încărcare că un pod este gata să accepte trafic? Sau că poate gestiona mai mult trafic?

Aceste teste sunt adesea confundate între ele:

  • Viața — verificarea „supraviețuirii”, care repornește podul dacă eșuează;
  • promptitudine — verificarea pregătirii, dacă eșuează, deconectează pod-ul de la serviciul Kubernetes (acest lucru poate fi verificat folosind kubectl get endpoints) și traficul nu ajunge la acesta până când următoarea verificare nu este finalizată cu succes.

Ambele verificări EFECTUAT PE TOTUL CICLU DE VIAȚĂ AL PODULUI. Este foarte important.

O concepție greșită comună este că sondele de pregătire sunt rulate doar la pornire, astfel încât echilibratorul să poată ști că podul este gata (Ready) și poate începe procesarea traficului. Cu toate acestea, aceasta este doar una dintre opțiunile pentru utilizarea lor.

Alta este posibilitatea de a afla ca traficul pe pod este excesiv si îl supraîncărcă (sau podul efectuează calcule mari consumatoare de resurse). În acest caz, verificarea pregătirii ajută reduceți sarcina pe pod și „răciți-l”.. Finalizarea cu succes a unei verificări de pregătire în viitor permite crește încărcătura pe pod. În acest caz (dacă testul de pregătire eșuează), eșecul testului de viață ar fi foarte contraproductiv. De ce reporniți un pod care este sănătos și lucrează din greu?

Prin urmare, în unele cazuri, nicio verificare este mai bună decât activarea lor cu parametri configurați incorect. După cum sa menționat mai sus, dacă verificarea vieții copii verificarea pregătirii, atunci ai probleme mari. Opțiunea posibilă este configurarea numai testul de pregătireȘi viaţă periculoasă lasa deoparte.

Ambele tipuri de verificări nu ar trebui să eșueze atunci când dependențele comune eșuează, altfel acest lucru va duce la o defecțiune în cascadă (ca avalanșă) a tuturor podurilor. Cu alte cuvinte, nu-ți face rău.

3. LoadBalancer pentru fiecare serviciu HTTP

Cel mai probabil, aveți servicii HTTP în cluster pe care ați dori să le redirecționați către lumea exterioară.

Dacă deschideți serviciul ca type: LoadBalancer, controlerul său (în funcție de furnizorul de servicii) va furniza și negocia un LoadBalancer extern (nu rulează neapărat pe L7, ci chiar și pe L4), iar acest lucru poate afecta costul (adresă IPv4 statică externă, putere de calcul, facturare pe secundă). ) din cauza necesității creării unui număr mare de astfel de resurse.

În acest caz, este mult mai logic să folosiți un echilibrator de încărcare extern, deschizând servicii ca type: NodePort. Sau mai bine, extinde ceva de genul nginx-ingress-controller (Sau, traefik), care va fi singurul Portul nodului punctul final asociat cu echilibratorul de încărcare extern și va direcționa traficul în cluster folosind pătrundere-Resurse Kubernetes.

Alte (micro)servicii intra-cluster care interacționează între ele pot „comunica” folosind servicii precum ClusterIP și un mecanism de descoperire a serviciilor încorporat prin DNS. Doar nu folosiți DNS/IP-ul lor public, deoarece acest lucru poate afecta latența și poate crește costul serviciilor cloud.

4. Autoscaling un cluster fără a lua în considerare caracteristicile acestuia

Când adăugați noduri și le eliminați dintr-un cluster, nu ar trebui să vă bazați pe unele valori de bază, cum ar fi utilizarea CPU pe acele noduri. Planificarea podului trebuie să țină cont de mulți restricții, cum ar fi afinitatea pod/nod, impurități și toleranțe, solicitări de resurse, QoS etc. Utilizarea unui autoscaler extern care nu ia în considerare aceste nuanțe poate duce la probleme.

Imaginați-vă că un anumit pod ar trebui programat, dar toată puterea CPU disponibilă este solicitată/dezasamblată și podul se blochează într-o stare Pending. Autoscalerul extern vede încărcarea curentă medie a CPU (nu cea solicitată) și nu inițiază expansiunea (scale-out) - nu adaugă un alt nod. Ca urmare, acest pod nu va fi programat.

În acest caz, scalare inversă (scalare) — eliminarea unui nod dintr-un cluster este întotdeauna mai dificil de implementat. Imaginați-vă că aveți un pod cu stare (cu stocare persistentă conectată). Volume persistente aparțin de obicei zona de disponibilitate specifica și nu sunt replicate în regiune. Astfel, dacă un autoscaler extern șterge un nod cu acest pod, planificatorul nu va putea programa acest pod pe alt nod, deoarece acest lucru se poate face numai în zona de disponibilitate în care se află stocarea persistentă. Podul va fi blocat în stare Pending.

Foarte popular în comunitatea Kubernetes cluster-autoscaler. Funcționează pe un cluster, acceptă API-uri de la furnizorii importanți de cloud, ia în considerare toate restricțiile și se poate scala în cazurile de mai sus. De asemenea, este capabil să se extindă, menținând în același timp toate limitele stabilite, economisind astfel bani (care altfel ar fi cheltuiți pentru capacitatea nefolosită).

5. Neglijarea capabilităților IAM/RBAC

Atenție la folosirea utilizatorilor IAM cu secrete persistente pentru mașini și aplicații. Organizați accesul temporar folosind roluri și conturi de serviciu (conturi de servicii).

Ne confruntăm adesea cu faptul că cheile de acces (și secretele) sunt codificate în configurația aplicației, precum și neglijarea rotației secretelor în ciuda faptului că avem acces la Cloud IAM. Utilizați roluri IAM și conturi de serviciu în loc de utilizatori, acolo unde este cazul.

10 greșeli frecvente când utilizați Kubernetes

Uitați de kube2iam și mergeți direct la rolurile IAM pentru conturile de serviciu (așa cum este descris în nota cu acelasi nume Štěpán Vraný):

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/my-app-role
  name: my-serviceaccount
  namespace: default

O adnotare. Nu atât de greu, nu?

De asemenea, nu acordați privilegii conturilor de serviciu și profilurilor de instanță admin и cluster-admindacă nu au nevoie. Acest lucru este puțin mai dificil de implementat, mai ales în RBAC K8-uri, dar cu siguranță merită efortul.

6. Nu te baza pe anti-afinitatea automată pentru păstăi

Imaginați-vă că aveți trei replici ale unei implementări pe un nod. Nodul cade și odată cu el toate replicile. Situație neplăcută, nu? Dar de ce erau toate replicile pe același nod? Nu ar trebui Kubernetes să ofere disponibilitate înaltă (HA)?!

Din păcate, planificatorul Kubernetes, din proprie inițiativă, nu respectă regulile existenței separate (anti-afinitate) pentru păstăi. Acestea trebuie precizate în mod explicit:

// опущено для краткости
      labels:
        app: zk
// опущено для краткости
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - zk
              topologyKey: "kubernetes.io/hostname"

Asta e tot. Acum podurile vor fi programate pe diferite noduri (aceasta condiție este verificată numai în timpul programării, dar nu și în timpul funcționării lor - prin urmare requiredDuringSchedulingIgnoredDuringExecution).

Aici vorbim despre podAntiAffinity pe diferite noduri: topologyKey: "kubernetes.io/hostname", - și nu despre diferite zone de disponibilitate. Pentru a implementa un HA cu drepturi depline, va trebui să aprofundați acest subiect.

7. Ignorarea PodDisruptionBudgets

Imaginați-vă că aveți o sarcină de producție pe un cluster Kubernetes. Periodic, nodurile și clusterul în sine trebuie actualizate (sau dezafectate). PodDisruptionBudget (PDB) este ceva ca un acord de garantare a serviciilor între administratorii clusterului și utilizatori.

PDB vă permite să evitați întreruperile serviciului cauzate de lipsa nodurilor:

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeeper

În acest exemplu, dumneavoastră, ca utilizator al clusterului, spuneți administratorilor: „Hei, am un serviciu de îngrijire a grădinii zoologice și, indiferent ce faceți, aș dori să am cel puțin 2 replici ale acestui serviciu mereu disponibile.”

Puteți citi mai multe despre asta aici.

8. Utilizatori multipli sau medii într-un cluster comun

Spații de nume Kubernetes (spații de nume) nu asigură izolație puternică.

O concepție greșită comună este că, dacă implementați o încărcare non-prod într-un spațiu de nume și o încărcare prod în altul, atunci acestea nu se vor influența reciproc în niciun fel... Cu toate acestea, un anumit nivel de izolare poate fi atins folosind solicitări/limitări de resurse, stabilirea cotelor și stabilirea claselor prioritare. O anumită izolație „fizică” în planul de date este asigurată de afinități, toleranțe, însemnări (sau nodeselectors), dar o astfel de separare este destul de dificil implementează.

Cei care trebuie să combine ambele tipuri de sarcini de lucru în același cluster vor trebui să facă față complexității. Dacă nu există o astfel de nevoie și vă puteți permite să aveți una încă un grup (să zicem, într-un cloud public), atunci este mai bine să faceți acest lucru. Acest lucru va obține un nivel mult mai ridicat de izolare.

9. externalTrafficPolicy: Cluster

Foarte des vedem că tot traficul din interiorul clusterului vine printr-un serviciu precum NodePort, pentru care este setată politica implicită externalTrafficPolicy: Cluster... Înseamnă că Portul nodului este deschis pe fiecare nod din cluster și puteți folosi oricare dintre ele pentru a interacționa cu serviciul dorit (set de pod-uri).

10 greșeli frecvente când utilizați Kubernetes

În același timp, pod-urile reale asociate cu serviciul NodePort menționat mai sus sunt de obicei disponibile doar pe o anumită subset al acestor noduri. Cu alte cuvinte, dacă mă conectez la un nod care nu are podul necesar, acesta va redirecționa traficul către un alt nod, adăugând un hop și creșterea latenței (dacă nodurile sunt situate în diferite zone de disponibilitate/centre de date, latența poate fi destul de mare; în plus, costurile de trafic de ieșire vor crește).

Pe de altă parte, dacă un anumit serviciu Kubernetes are un set de politici externalTrafficPolicy: Local, apoi NodePort se deschide numai pe acele noduri în care rulează efectiv podurile necesare. Când utilizați un echilibrator de încărcare extern care verifică starea (verificarea sănătății) puncte finale (cum se descurcă AWS ELB), El va trimite trafic numai către nodurile necesare, care va avea un efect benefic asupra întârzierilor, nevoilor de calcul, facturilor de ieșire (și bunul simț dictează același lucru).

Există șanse mari să utilizați deja ceva de genul traefik sau nginx-ingress-controller ca punct final NodePort (sau LoadBalancer, care folosește și NodePort) pentru a ruta traficul de intrare HTTP, iar setarea acestei opțiuni poate reduce semnificativ latența pentru astfel de solicitări.

В această publicație Puteți afla mai multe despre externalTrafficPolicy, avantajele și dezavantajele acesteia.

10. Nu vă legați de clustere și nu abuzați de planul de control

Anterior, era obișnuit să apelăm serverele prin nume proprii: Anton, HAL9000 și Colossus... Astăzi au fost înlocuite cu identificatori generați aleatoriu. Cu toate acestea, obiceiul a rămas, iar acum numele proprii merg în grupuri.

O poveste tipică (bazată pe evenimente reale): totul a început cu o dovadă a conceptului, astfel încât clusterul avea un nume mândru de testare… Au trecut ani și încă este folosit în producție și tuturor se teme să-l atingă.

Nu este nimic distractiv în ceea ce privește transformarea ciorchinelor în animale de companie, așa că vă recomandăm să le îndepărtați periodic în timp ce exersați recuperare în caz de dezastru (aceasta va ajuta ingineria haosului - aprox. traducere). În plus, nu ar strica să lucrați la stratul de control (plan de control). Să-ți fie frică să-l atingi nu este un semn bun. etcd mort? Băieți, chiar aveți probleme!

Pe de altă parte, nu ar trebui să te lași purtat de manipulare. Cu timpul stratul de control poate deveni lent. Cel mai probabil, acest lucru se datorează faptului că un număr mare de obiecte sunt create fără a le roti (o situație obișnuită când se folosește Helm cu setări implicite, motiv pentru care starea sa în configmaps/secrete nu este actualizată - ca urmare, mii de obiecte se acumulează în stratul de control) sau cu editarea constantă a obiectelor kube-api (pentru scalare automată, pentru CI/CD, pentru monitorizare, jurnalele de evenimente, controlere etc.).

În plus, vă recomandăm să verificați acordurile SLA/SLO cu furnizorul Kubernetes administrat și să acordați atenție garanțiilor. Vânzătorul poate garanta controlul disponibilității stratului (sau subcomponentele sale), dar nu întârzierea p99 a solicitărilor pe care le trimiteți acestuia. Cu alte cuvinte, poți intra kubectl get nodes, și primiți un răspuns numai după 10 minute, iar aceasta nu va fi o încălcare a termenilor acordului de servicii.

11. Bonus: folosind cea mai recentă etichetă

Dar acesta este deja un clasic. În ultimul timp am întâlnit mai rar această tehnică, deoarece mulți, după ce au învățat din experiență amară, au încetat să mai folosească eticheta :latest și a început să fixați versiuni. Ura!

ECR menține imuabilitatea etichetelor de imagine; Vă recomandăm să vă familiarizați cu această caracteristică remarcabilă.

Rezumat

Nu vă așteptați ca totul să funcționeze peste noapte: Kubernetes nu este un panaceu. Aplicație proastă va rămâne așa chiar și în Kubernetes (și probabil se va înrăutăți). Neatenția va duce la o complexitate excesivă, o muncă lentă și stresantă a stratului de control. În plus, riscați să rămâneți fără o strategie de recuperare în caz de dezastru. Nu vă așteptați ca Kubernetes să ofere izolare și disponibilitate ridicată de la cutie. Petreceți ceva timp pentru ca aplicația dvs. să fie cu adevărat nativă în cloud.

Vă puteți familiariza cu experiențele nereușite ale diferitelor echipe din această colecție de povești de Henning Jacobs.

Cei care doresc să adauge la lista de erori din acest articol ne pot contacta pe Twitter (@MarekBartik, @MstrsObserver).

PS de la traducator

Citește și pe blogul nostru:

Sursa: www.habr.com

Adauga un comentariu