Cele mai bune practici pentru containerele Kubernetes: verificări de sănătate

Cele mai bune practici pentru containerele Kubernetes: verificări de sănătate

TL; DR

  • Pentru a obține o observabilitate ridicată a containerelor și microserviciilor, jurnalele și valorile primare nu sunt suficiente.
  • Pentru o recuperare mai rapidă și o rezistență sporită, aplicațiile ar trebui să aplice Principiul de înaltă observabilitate (HOP).
  • La nivel de aplicație, NOP necesită: înregistrare adecvată, monitorizare atentă, verificări de sănătate și urmărirea performanței/tranziției.
  • Utilizați cecurile ca element al NOR ReadinessProbe и Sonda de viaţă Kubernetes.

Ce este un șablon de verificare a stării de sănătate?

Când proiectați o aplicație critică și foarte disponibilă, este foarte important să ne gândim la un aspect precum toleranța la erori. O aplicație este considerată tolerantă la erori dacă își revine rapid după defecțiune. O aplicație tipică în cloud utilizează o arhitectură de microservicii - în care fiecare componentă este plasată într-un container separat. Și pentru a vă asigura că aplicația de pe k8s este foarte disponibilă atunci când proiectați un cluster, trebuie să urmați anumite modele. Printre acestea se numără șablonul de verificare a stării de sănătate. Acesta definește modul în care aplicația îi comunică lui k8s că este sănătoasă. Acestea nu sunt doar informații despre dacă podul rulează, ci și despre modul în care primește și răspunde la solicitări. Cu cât Kubernetes știe mai multe despre starea de sănătate a podului, cu atât va lua decizii mai inteligente cu privire la rutarea traficului și echilibrarea încărcăturii. Astfel, Principiul Observabilității Înalte permite aplicației să răspundă solicitărilor în timp util.

Principiul de înaltă observabilitate (HOP)

Principiul observabilității ridicate este unul dintre principii pentru proiectarea aplicaţiilor containerizate. Într-o arhitectură de microservicii, serviciilor nu le pasă cum este procesată cererea lor (și pe bună dreptate), dar ceea ce contează este modul în care primesc răspunsuri de la serviciile care le primesc. De exemplu, pentru a autentifica un utilizator, un container trimite o solicitare HTTP altuia, așteptând un răspuns într-un anumit format - asta e tot. PythonJS poate procesa și cererea, iar Python Flask poate răspunde. Containerele sunt ca niște cutii negre cu conținut ascuns unul altuia. Cu toate acestea, principiul NOP necesită ca fiecare serviciu să expună mai multe puncte finale API care indică cât de sănătos este, precum și starea de pregătire și toleranță la erori. Kubernetes solicită acești indicatori pentru a se gândi la următorii pași pentru rutare și echilibrarea încărcăturii.

O aplicație cloud bine concepută își înregistrează evenimentele principale folosind fluxurile standard I/O STDERR și STDOUT. Urmează un serviciu auxiliar, de exemplu filebeat, logstash sau fluentd, care livrează jurnalele către un sistem de monitorizare centralizat (de exemplu Prometheus) și un sistem de colectare a jurnalelor (suita de software ELK). Diagrama de mai jos arată cum funcționează o aplicație cloud în conformitate cu modelul de testare a sănătății și principiul de înaltă observabilitate.

Cele mai bune practici pentru containerele Kubernetes: verificări de sănătate

Cum se aplică modelul de verificare a stării de sănătate în Kubernetes?

Din cutie, k8s monitorizează starea podurilor folosind unul dintre controlere (Implementări, ReplicaSets, DaemonSets, StatefulSets etc., etc.). După ce a descoperit că podul a căzut dintr-un motiv oarecare, controlerul încearcă să îl repornească sau să îl mute pe un alt nod. Cu toate acestea, un pod poate raporta că este în funcțiune, dar în sine nu funcționează. Să dăm un exemplu: aplicația dvs. folosește Apache ca server web, ați instalat componenta pe mai multe pod-uri ale clusterului. Deoarece biblioteca a fost configurată incorect, toate solicitările către aplicație răspund cu codul 500 (eroare internă de server). La verificarea livrării, verificarea stării podurilor dă un rezultat de succes, dar clienții gândesc diferit. Vom descrie această situație nedorită după cum urmează:

Cele mai bune practici pentru containerele Kubernetes: verificări de sănătate

În exemplul nostru, k8s face verificarea funcționalității. În acest tip de verificare, kubeletul verifică continuu starea procesului din container. Odată ce înțelege că procesul s-a oprit, îl va reporni. Dacă eroarea poate fi rezolvată prin simpla repornire a aplicației, iar programul este proiectat să se închidă la orice eroare, atunci o verificare a stării de sănătate a procesului este tot ce aveți nevoie pentru a urma NOP și modelul de testare de sănătate. Singurul păcat este că nu toate erorile sunt eliminate prin repornire. În acest caz, k8s oferă 2 moduri mai profunde de a identifica problemele cu pod: Sonda de viaţă и ReadinessProbe.

LivenessProbe

În timpul Sonda de viaţă kubelet efectuează 3 tipuri de verificări: nu numai că determină dacă podul rulează, ci și dacă este gata să primească și să răspundă în mod adecvat solicitărilor:

  • Configurați o solicitare HTTP către pod. Răspunsul trebuie să conțină un cod de răspuns HTTP în intervalul de la 200 la 399. Astfel, codurile 5xx și 4xx semnalează că podul are probleme, chiar dacă procesul rulează.
  • Pentru a testa pod-uri cu servicii non-HTTP (de exemplu, serverul de e-mail Postfix), trebuie să stabiliți o conexiune TCP.
  • Executați o comandă arbitrară pentru un pod (intern). Verificarea este considerată reușită dacă codul de finalizare a comenzii este 0.

Un exemplu de cum funcționează acest lucru. Următoarea definiție a podului conține o aplicație NodeJS care afișează o eroare 500 la cererile HTTP. Pentru a ne asigura că containerul este repornit atunci când se primește o astfel de eroare, folosim parametrul livenessProbe:

apiVersion: v1
kind: Pod
metadata:
 name: node500
spec:
 containers:
   - image: magalix/node500
     name: node500
     ports:
       - containerPort: 3000
         protocol: TCP
     livenessProbe:
       httpGet:
         path: /
         port: 3000
       initialDelaySeconds: 5

Aceasta nu este diferită de orice altă definiție a podului, dar adăugăm un obiect .spec.containers.livenessProbe. Parametru httpGet acceptă calea către care este trimisă cererea HTTP GET (în exemplul nostru aceasta este /, dar în scenariile de luptă poate exista ceva de genul /api/v1/status). Un alt livenessProbe acceptă un parametru initialDelaySeconds, care indică operației de verificare să aștepte un anumit număr de secunde. Întârzierea este necesară deoarece containerul are nevoie de timp pentru a porni, iar atunci când este repornit, va fi indisponibil pentru o perioadă de timp.

Pentru a aplica această setare unui cluster, utilizați:

kubectl apply -f pod.yaml

După câteva secunde, puteți verifica conținutul podului folosind următoarea comandă:

kubectl describe pods node500

La sfârșitul ieșirii, găsiți asta e ceea ce.

După cum puteți vedea, livenessProbe a inițiat o solicitare HTTP GET, containerul a generat o eroare 500 (care este ceea ce a fost programat să facă), iar kubeletul l-a repornit.

Dacă vă întrebați cum a fost programată aplicația NideJS, iată aplicația.js și Dockerfile care au fost folosite:

app.js

var http = require('http');

var server = http.createServer(function(req, res) {
    res.writeHead(500, { "Content-type": "text/plain" });
    res.end("We have run into an errorn");
});

server.listen(3000, function() {
    console.log('Server is running at 3000')
})

Dockerfile

FROM node
COPY app.js /
EXPOSE 3000
ENTRYPOINT [ "node","/app.js" ]

Este important să rețineți acest lucru: livenessProbe va reporni containerul numai dacă nu reușește. Dacă o repornire nu corectează eroarea care împiedică rularea containerului, kubelet-ul nu va putea lua măsuri pentru a corecta problema.

ReadinessProbe

ReadinessProbe funcționează similar cu livenessProbes (cereri GET, comunicații TCP și execuție de comenzi), cu excepția acțiunilor de depanare. Containerul în care este detectată defecțiunea nu este repornit, ci este izolat de traficul de intrare. Imaginați-vă că unul dintre containere efectuează o mulțime de calcule sau este sub sarcină grea, ceea ce determină creșterea timpilor de răspuns. În cazul livenessProbe, se declanșează verificarea disponibilității răspunsului (prin parametrul de verificare timeoutSeconds), după care kubelet-ul repornește containerul. Când este pornit, containerul începe să efectueze sarcini mari de resurse și este repornit din nou. Acest lucru poate fi critic pentru aplicațiile care au nevoie de viteză de răspuns. De exemplu, o mașină în timp ce se află pe drum așteaptă un răspuns de la server, răspunsul este întârziat - și mașina intră într-un accident.

Să scriem o definiție redinessProbe care va seta timpul de răspuns la cererea GET la cel mult două secunde, iar aplicația va răspunde la cererea GET după 5 secunde. Fișierul pod.yaml ar trebui să arate astfel:

apiVersion: v1
kind: Pod
metadata:
 name: nodedelayed
spec:
 containers:
   - image: afakharany/node_delayed
     name: nodedelayed
     ports:
       - containerPort: 3000
         protocol: TCP
     readinessProbe:
       httpGet:
         path: /
         port: 3000
       timeoutSeconds: 2

Să implementăm un pod cu kubectl:

kubectl apply -f pod.yaml

Să așteptăm câteva secunde și apoi să vedem cum a funcționat ReadinessProbe:

kubectl describe pods nodedelayed

La sfârșitul rezultatului puteți vedea că unele dintre evenimente sunt similare Aceasta.

După cum puteți vedea, kubectl nu a repornit podul când timpul de verificare a depășit 2 secunde. În schimb, a anulat cererea. Comunicațiile primite sunt redirecționate către alte poduri care funcționează.

Rețineți că acum că pod-ul este descărcat, kubectl direcționează din nou solicitările către acesta: răspunsurile la cererile GET nu mai sunt întârziate.

Pentru comparație, mai jos este fișierul app.js modificat:

var http = require('http');

var server = http.createServer(function(req, res) {
   const sleep = (milliseconds) => {
       return new Promise(resolve => setTimeout(resolve, milliseconds))
   }
   sleep(5000).then(() => {
       res.writeHead(200, { "Content-type": "text/plain" });
       res.end("Hellon");
   })
});

server.listen(3000, function() {
   console.log('Server is running at 3000')
})

TL; DR
Înainte de apariția aplicațiilor cloud, jurnalele erau mijlocul principal de monitorizare și verificare a stării de sănătate a aplicațiilor. Cu toate acestea, nu a existat nicio modalitate de a lua măsuri corective. Jurnalele sunt și astăzi utile; ele trebuie colectate și trimise la un sistem de colectare a jurnalelor pentru analiza situațiilor de urgență și luarea deciziilor. [Toate acestea se puteau face fără aplicații cloud care să folosească monit, de exemplu, dar cu k8s a devenit mult mai ușor :) – nota editorului. ]

Astăzi, corecțiile trebuie făcute aproape în timp real, astfel încât aplicațiile nu mai trebuie să fie cutii negre. Nu, ar trebui să arate puncte finale care permit sistemelor de monitorizare să interogheze și să colecteze date valoroase despre starea proceselor, astfel încât să poată răspunde instantaneu dacă este necesar. Acesta se numește Modelul de proiectare a testului de performanță, care urmează principiul de înaltă observabilitate (HOP).

Kubernetes oferă 2 tipuri de verificări de sănătate în mod implicit: readinessProbe și livenessProbe. Ambele folosesc aceleași tipuri de verificări (cereri HTTP GET, comunicații TCP și execuție de comenzi). Ele diferă în ceea ce privește deciziile pe care le iau ca răspuns la problemele din păstăi. livenessProbe repornește containerul în speranța că eroarea nu se va mai repeta, iar readinessProbe izolează podul de traficul de intrare până când cauza problemei este rezolvată.

Proiectarea corectă a aplicației ar trebui să includă ambele tipuri de verificare și să se asigure că acestea colectează suficiente date, mai ales atunci când este aruncată o excepție. Ar trebui să arate, de asemenea, punctele finale API necesare care oferă sistemului de monitorizare (Prometheus) valori importante de sănătate.

Sursa: www.habr.com

Adauga un comentariu