Ghid vizual pentru diagnosticarea problemelor în Kubernetes

Nota traducătorului.: Acest articol face parte din materialele publicate liber ale proiectului learnk8s, dedicat formării în utilizarea Kubernetes pentru companii și administratori individuali. În el, Daniele Polencic, liderul proiectului, oferă un ghid vizual despre pașii care ar trebui urmați în caz de probleme generale cu aplicațiile rulante în clusterul K8s.

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

TL;DR: iată o diagramă care vă va ajuta să depanați deployment în Kubernetes:

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

Diagrama pentru identificarea și corectarea erorilor în cluster. Varianta originală (în engleză) este disponibilă în PDF și ca imagine.

Atunci când desfășurați o aplicație în Kubernetes, de obicei este necesar să definiți trei componente:

  • Deployment — este o rețetă pentru crearea copiilor aplicației, numite pod-uri;
  • Serviciu — un load balancer intern, care distribuie traficul între pod-uri;
  • Ingress — o descriere a modului în care traficul va ajunge din lumea externă la Service.

Iată un rezumat grafic concis:

1) În Kubernetes, aplicațiile primesc trafic din exterior prin două straturi de load balancere: intern și extern.

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

2) Load balancer-ul intern se numește Service, cel extern - Ingress.

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

3) Deployment-ul creează pod-uri și le monitorizează (acestea nu sunt create manual).

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

Să presupunem că doriți să desfășurați o aplicație simplă de tipul Hello World. Configurația YAML pentru aceasta va arăta astfel:

apiVersion: apps/v1
kind: Deployment # <<<
metadata:
  name: my-deployment
  labels:
    track: canary
spec:
  selector:
    matchLabels:
      any-name: my-app
  template:
    metadata:
      labels:
        any-name: my-app
    spec:
      containers:
      - name: cont1
        image: learnk8s/app:1.0.0
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service # <<<
metadata:
  name: my-service
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    name: app
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress # <<<
metadata:
  name: my-ingress
spec:
  rules:
  - http:
    paths:
    - backend:
        serviceName: app
        servicePort: 80
      path: /

Definiția este destul de lungă și este ușor să te pierzi în modul în care componentele sunt legate între ele.

De exemplu:

  • Când ar trebui să folosiți portul 80 și când - 8080?
  • Ar trebui să creați un nou port pentru fiecare serviciu, pentru a nu intra în conflict?
  • Au importanță numele etichetelor? Trebuie să fie aceleași peste tot?

Înainte de a ne concentra pe depanare, să ne reamintim cum sunt legate cele trei componente. Să începem cu Deployment și Service.

Legătura dintre Deployment și Service

Veți fi surprinși, dar Deployment-urile și Service-urile nu sunt legate între ele. În schimb, Service-ul indică direct către Pod-uri, fără a trece prin Deployment.

Astfel, ne interesează modul în care sunt corelate între ele Pod-urile și Service-urile. Trebuie să ținem cont de trei lucruri:

  1. Selector (selector) al Service-ului trebuie să corespundă cel puțin unei etichete a Pod-ului.
  2. targetPort trebuie să se potrivească cu containerPort al containerului din Pod.
  3. port Service-ul poate fi oricum. Diferite servicii pot folosi aceeași port, deoarece au adrese IP diferite.

Schema următoare prezintă tot ceea ce a fost menționat anterior într-o formă grafică:

1) Să presupunem că serviciul direcționează trafic către un pod:

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

2) La crearea pod-ului, este necesar să se stabilească containerPort pentru fiecare container din pod-uri:

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

3) La crearea serviciului trebuie să indicați port și targetPort. Dar prin care dintre acestea se face conectarea la container?

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

4) Prin targetPort. Acesta trebuie să se potrivească cu containerPort.

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

5) Să presupunem că în container este deschis portul 3000. Atunci valoarea targetPort trebuie să fie aceeași.

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

În fișierul YAML, etichetele și porturi / targetPort trebuie să corespundă:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
  labels:
    track: canary
spec:
  selector:
    matchLabels:
      any-name: my-app
  template:
    metadata:
     labels:  # <<<
        any-name: my-app  # <<<
   spec:
      containers:
      - name: cont1
        image: learnk8s/app:1.0.0
        ports:
       - containerPort: 8080  # <<<
---
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
  - port: 80
   targetPort: 8080  # <<<
 selector:  # <<<
    any-name: my-app  # <<<

Și ce zici de eticheta track: canary din partea de sus a secțiunii Deployment? Trebuie să se potrivească?

Această etichetă se referă la desfășurare și nu este folosită de serviciu pentru rutarea traficului. Cu alte cuvinte, poate fi ștearsă sau poate fi atribuită o altă valoare.

Dar ce zici de selectorul matchLabels?

Acesta trebuie întotdeauna să se potrivească cu etichetele Pod-ului, deoarece este folosit de Deployment pentru a urmări pod-urile.

Să presupunem că ai efectuat modificările corecte. Cum poți verifica?

Poți verifica etichetele pod-urilor folosind comanda următoare:

kubectl get pods --show-labels

Sau, dacă pod-urile aparțin mai multor aplicații:

kubectl get pods --selector any-name=my-app --show-labels

Unde any-name=my-app — aceasta este eticheta any-name: my-app.

A mai rămas vreo dificultate?

Se poate conecta la pod! Pentru asta trebuie folosită comanda port-forward în kubectl. Aceasta permite conectarea la serviciu și verificarea conexiunii.

kubectl port-forward service/<service name> 3000:80

Aici:

  • service/<service name> — numele serviciului; în cazul nostru este my-service;
  • 3000 — portul care trebuie să fie deschis pe computer;
  • 80 — portul specificat în câmpul port serviciului.

Dacă s-a reușit stabilirea conexiunii, atunci setările sunt corecte.

Dacă nu s-a reușit conectarea, atunci problema este cu etichetele sau porturile nu se potrivesc.

Conexiunea dintre Service și Ingress

Următorul pas în asigurarea accesului la aplicație este configurarea Ingress. Ingress trebuie să știe cum să găsească serviciul, apoi să găsească pod-urile și să le redirecționeze traficul. Ingress găsește serviciul dorit după nume și portul deschis.

În descrierea Ingress și Service trebuie să coincidă doi parametri:

  1. servicePort în Ingress trebuie să coincidă cu parametrul port în Service;
  2. serviceName în Ingress trebuie să coincida cu câmpul name în Service.

Schema următoare rezumă conexiunea porturilor:

1) Așa cum știți deja, Service ascultă pe un anumit port:

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

2) Ingress are un parametru numit servicePort:

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

3) Acest parametru (servicePort) trebuie să coincidă întotdeauna cu port în definiția Service:

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

4) Dacă în Service este specificat portul 80, atunci este necesar ca servicePort să fie de asemenea 80:

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

În practică, trebuie să fie atenți la următoarele linii:

apiVersion: v1
kind: Service
metadata:
 name: my-service  # <<<
spec:
  ports:
 - port: 80  # <<<
   targetPort: 8080
  selector:
    any-name: my-app
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: my-ingress
spec:
  rules:
  - http:
    paths:
    - backend:
       serviceName: my-service  # <<<
       servicePort: 80  # <<<
     path: /

Cum să verificați dacă Ingress funcționează?

Puteți utiliza metoda cu kubectl port-forward, dar în loc de serviciu trebuie să vă conectați la controlerul Ingress.

Mai întâi, trebuie să aflați numele pod-ului cu controlerul Ingress:

kubectl get pods --all-namespaces
NAMESPACE   NAME                              READY STATUS
kube-system coredns-5644d7b6d9-jn7cq          1/1   Running
kube-system etcd-minikube                     1/1   Running
kube-system kube-apiserver-minikube           1/1   Running
kube-system kube-controller-manager-minikube  1/1   Running
kube-system kube-proxy-zvf2h                  1/1   Running
kube-system kube-scheduler-minikube           1/1   Running
kube-system nginx-ingress-controller-6fc5bcc  1/1   Running

Găsiți pod-ul Ingress (poate aparține unui alt spațiu de nume) și executați comanda describe, pentru a afla numerele porturilor:

kubectl describe pod nginx-ingress-controller-6fc5bcc 
--namespace kube-system 
 | grep Ports
Ports:         80/TCP, 443/TCP, 18080/TCP

În cele din urmă, conectați-vă la pod:

kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system

Acum, de fiecare dată când veți trimite o cerere pe portul 3000 pe computerul dvs., va fi redirecționată către portul 80 al pod-ului cu controlerul Ingress. Accesând http://localhost:3000, ar trebui să vedeți pagina generată de aplicație.

Rezumatul porturilor

Să ne amintim din nou care porturi și etichete trebuie să coincidă:

  1. Selectorul în definiția Service trebuie să coincidă cu eticheta pod-ului;
  2. targetPort în definiția Service trebuie să coincidă cu containerPort containerul din interiorul pod-ului;
  3. port În definiția Service poate fi orice. Serviciile diferite pot folosi aceeași port, având adrese IP diferite;
  4. servicePort Ingress-ului trebuie să corespundă port în definiția Service;
  5. Numele serviciului trebuie să corespundă cu câmpul serviceName din Ingress.

Din păcate, nu este suficient să știi cum să structurezi corect configurația YAML.

Ce se întâmplă când ceva nu merge bine?

Este posibil ca podul să nu pornească sau să se blocheze.

3 pași pentru diagnosticarea problemelor aplicațiilor în Kubernetes

Înainte de a începe depanarea deploy-ului, trebuie să ai o bună înțelegere a modului în care funcționează Kubernetes.

Din moment ce fiecare aplicație rulată în K8s are trei componente, depanarea lor ar trebui să se facă într-o anumită ordine, începând cu partea de jos.

  1. Primul lucru de verificat este dacă podurile funcționează, apoi...
  2. Verifică dacă serviciul livrează trafic podurilor, apoi...
  3. Verifică dacă Ingress-ul este configurat corect.

Reprezentare vizuală:

1) Căutarea problemelor ar trebui să înceapă de jos. Începe prin a verifica dacă podurile au starea Gata și Running:

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

2) Dacă podurile sunt gata (Gata), trebuie să afli dacă serviciul distribuite traficul între poduri:

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

3) În cele din urmă, trebuie să analizezi legătura dintre serviciu și Ingress:

Ghid vizual pentru diagnosticarea problemelor în Kubernetes

1. Diagnosticarea podurilor

În majoritatea cazurilor, problema este legată de pod. Asigură-te că podurile sunt marcate ca Gata și Running. Acest lucru se poate verifica cu comanda:

kubectl get pods
NAME                    READY STATUS            RESTARTS  AGE
app1                    0/1   ImagePullBackOff  0         47h
app2                    0/1   Error             0         47h
app3-76f9fcd46b-xbv4k   1/1   Running           1         47h

În rezultatul comenzii de mai sus, ultimul pod este marcat ca Running și Gata, dar acest lucru nu este cazul pentru celelalte două.

Cum să înțelegem că ceva nu a funcționat?

Există patru comenzi utile pentru diagnosticarea podurilor:

  1. kubectl logs permite extragerea jurnalelor din containerele din pod;
  2. kubectl describe pod permet să vizualizezi lista evenimentelor legate de pod;
  3. kubectl get pod permite obținerea configurației YAML a podului stocate în Kubernetes;
  4. kubectl exec -ti bash permite lansarea unei sesiuni interactive de terminal într-unul dintre containerele podului.

Pe care dintre ele să o alegi?

Problema este că nu există o comandă universală. Ar trebui să folosești o combinație a acestora.

Problemele tipice ale podurilor

Există două tipuri principale de erori ale podurilor: erori la pornire (startup) și erori în timpul execuției (runtime).

Erori de pornire:

  • ImagePullBackoff
  • ImageInspectError
  • ErrImagePull
  • ErrImageNeverPull
  • RegistryUnavailable
  • InvalidImageName

Erori de execuție:

  • CrashLoopBackOff
  • RunContainerError
  • KillContainerError
  • VerifyNonRootError
  • RunInitContainerError
  • CreatePodSandboxError
  • ConfigPodSandboxError
  • KillPodSandboxError
  • Unele erori apar mai frecvent decât altele. Iată câteva dintre cele mai comune erori și modalitățile de a le remedia.
  • ImagePullBackOff

Această eroare apare atunci când Kubernetes nu poate obține imaginea pentru unul dintre containerele podului. Iată trei dintre cele mai comune motive pentru aceasta:

Numele imaginii este incorect specificat — de exemplu, ați făcut o greșeală în acesta sau imaginea nu există;

Un tag inexistent a fost specificat pentru imagine;

  1. Imaginea este stocată într-un registru privat, iar Kubernetes nu are permisiunea de a accesa acest registru.
  2. Primele două motive sunt ușor de remediat — pur și simplu corectați numele imaginii și tag-ul. În cazul ultimului, trebuie să adăugați acreditivele pentru registrul privat în Secret și să adăugați referințe la acesta în poduri. Documentația Kubernetes
  3. oferă un exemplu

de cum se poate face acest lucru. Kubernetes afișează o eroare , dacă containerul nu se poate lansa. De obicei, acest lucru se întâmplă când:

CrashLoopBackOff

Există o eroare în aplicație care împiedică lansarea acesteia; CrashLoopBackOffeste configurat incorect

  1. Testul Liveness a eșuat prea multe ori.
  2. Container Trebuie să încercați să accesați logurile din container pentru a determina cauza eșecului său. Dacă accesul la loguri este dificil, deoarece containerul se repornește prea repede, puteți folosi următoarea comandă:;
  3. kubectl logs --previous

Aceasta va afișa mesajele de eroare din reincarnarea anterioară a containerului.

Această eroare apare atunci când containerul nu reușește să pornească. Aceasta corespunde momentului de dinaintea lansării aplicației. De obicei, cauza este o configurație greșită, cum ar fi:

încercarea de a monta un volum inexistent, cum ar fi ConfigMap sau Secrets;

RunContainerError

încercarea de a monta un volum de tip read-only ca read-write.

  • Pentru analiza acestor erori, comanda potrivită este
  • kubectl describe pod

Podurile sunt în stare Pending După ce un pod este creat, rămâne în stare.

De ce se întâmplă acest lucru?

Iată posibilele cauze (presupun că scheduler-ul funcționează corect): Pending.

Nu sunt suficiente resurse în cluster, cum ar fi puterea de calcul și memoria, pentru a lansa podul.

În spațiul de nume corespunzător, a fost configurat un obiect

  1. ResourceQuota
  2. В соответствующем пространстве имен установлен объект ResourceQuota Crearea pod-ului va depăși cota spațiului de nume.
  3. Pod-ul este marcat ca Pending PersistentVolumeClaim.

În acest caz, se recomandă utilizarea comenzii kubectl describe și verificarea secțiunii Evenimente:

kubectl describe pod

În cazul erorilor legate de ResourceQuotas, se recomandă să verificați log-urile cluster-ului folosind comanda

kubectl get events --sort-by=.metadata.creationTimestamp

Pod-urile nu sunt în stare Ready

Dacă pod-ul este marcat ca Running, dar nu se află în stare Gata, înseamnă că verificarea pregătirii sale (readiness probe) a eșuat.

Când se întâmplă acest lucru, pod-ul nu se conectează la serviciu, iar traficul nu ajunge la el. Eșecul testului de readiness este cauzat de probleme în aplicație. În acest caz, pentru a găsi eroarea, trebuie să analizați secțiunea Evenimente în ieșirea comenzii kubectl describe.

2. Diagnosticarea serviciilor

Dacă pod-urile sunt marcate ca Running și Gata, dar nu există în continuare răspuns din partea aplicației, trebuie să verificați configurațiile serviciului.

Serviciile se ocupă de rutarea traficului către pod-uri în funcție de etichetele lor. Prin urmare, primul lucru care trebuie făcut este să verificați câte pod-uri sunt conectate la serviciu. Pentru asta, puteți verifica endpoint-urile din serviciu:

kubectl describe service  | grep Endpoints

Endpoint este o pereche de valori de tipul <IP-адрес:порт>, iar în output ar trebui să existe cel puțin o astfel de pereche (adică cu serviciul este conectat cel puțin un pod).

Dacă secțiunea Endpoints este goală, există două posibilități:

  1. nu există niciun pod cu eticheta corectă (sugestie: verificați dacă namespace-ul este corect ales);
  2. există o eroare în etichetele serviciului din selector.

Dacă vedeți o listă de endpoint-uri, dar totuși nu puteți accesa aplicația, atunci un vinovat probabil este o eroare în targetPort descrierea serviciului.

Cum să verificați funcționalitatea serviciului?

Indiferent de tipul serviciului, puteți utiliza comanda kubectl port-forward pentru a vă conecta la acesta:

kubectl port-forward service/ 3000:80

Aici:

  • <service-name> — numele serviciului;
  • 3000 — portul pe care îl deschideți pe computer;
  • 80 — portul pe partea serviciului.

3. Diagnosticarea Ingress

Dacă ați citit până aici, atunci:

  • pod-urile sunt marcate ca Running și Gata;
  • serviciul distribuie cu succes traficul între pod-uri.

Totuși, nu puteți în continuare să accesați aplicația.

Acest lucru înseamnă că, cel mai probabil, controlerul Ingress este configurat greșit. Deoarece controlerul Ingress este un component terț în cluster, există diverse metode de depanare în funcție de tipul acestuia.

Dar înainte de a recurge la ajutorul uneltelor speciale pentru configurarea Ingress-ului, se poate face ceva foarte simplu. Ingress folosește serviceName și servicePort pentru a se conecta la serviciu. Este necesar să verificați dacă sunt configurate corect. Acest lucru se poate face cu ajutorul comenzii:

kubectl describe ingress

Dacă coloana Backend este goală, este foarte probabil să existe o eroare în configurație. Dacă backend-urile sunt la locul lor, dar accesul la aplicație este totuși imposibil, problema ar putea fi legată de:

  • setările de accesibilitate ale Ingress-ului din internetul public;
  • setările de accesibilitate ale cluster-ului din internetul public.

Identificarea problemelor cu infrastructura se poate face conectându-vă direct la pod-ul Ingress-ului. Pentru asta, mai întâi găsiți pod-ul controlerului Ingress (poate fi în alt spațiu de nume):

kubectl get pods --all-namespaces
NAMESPACE   NAME                              READY STATUS
kube-system coredns-5644d7b6d9-jn7cq          1/1   Running
kube-system etcd-minikube                     1/1   Running
kube-system kube-apiserver-minikube           1/1   Running
kube-system kube-controller-manager-minikube  1/1   Running
kube-system kube-proxy-zvf2h                  1/1   Running
kube-system kube-scheduler-minikube           1/1   Running
kube-system nginx-ingress-controller-6fc5bcc  1/1   Running

Folosiți comanda describe, pentru a stabili portul:

kubectl describe pod nginx-ingress-controller-6fc5bcc
--namespace kube-system 
 | grep Ports

În cele din urmă, conectați-vă la pod:

kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system

Acum toate cererile pe portul 3000 de pe computer vor fi redirecționate către portul 80 al pod-ului.

Funcționează acum?

  • Dacă da, atunci problema este cu infrastructura. Este necesar să se determine cum se face rutarea traficului în cluster.
  • Dacă nu, atunci problema este cu controlerul Ingress.

Dacă nu reușiți să faceți controlerul Ingress să funcționeze, va trebui să-l depanați.

Există multe tipuri de controlere Ingress. Cele mai populare sunt Nginx, HAProxy, Traefik etc. (mai multe informații despre soluții existente găsiți în recenzia noastră — nota trad.) Este indicat să consultați ghidul de soluționare a problemelor din documentația controlerului corespunzător. Deoarece Ingress Nginx este cel mai popular controler Ingress, am inclus în articol câteva sfaturi pentru a rezolva problemele asociate acestuia.

Depanarea controlerului Ingress Nginx

Proiectul Ingress-nginx are un plugin oficial pentru kubectl. Comanda kubectl ingress-nginx poate fi folosită pentru:

  • analiza log-urilor, backend-urilor, certificatelor etc.;
  • conectarea la Ingress;
  • studierea configurației curente.

Vor fi utile următoarele trei comenzi:

  • kubectl ingress-nginx lint — verifică nginx.conf;
  • kubectl ingress-nginx backend — investighează backend-ul (similar cu kubectl describe ingress);
  • kubectl ingress-nginx logs — verifică log-urile.

Rețineți: în unele cazuri poate fi necesar să specificați spațiul de nume corect pentru controlerul Ingress folosind flag-ul --namespace.

Rezumat

Diagnostica în Kubernetes poate fi o sarcină dificilă dacă nu știi de unde să începi. Problema ar trebui să fie abordată întotdeauna conform principiului „de jos în sus”: începe cu pod-urile, apoi treci la serviciu și Ingress. Metodele de depanare descrise în articol pot fi aplicate și altor obiecte, cum ar fi:

  • Job-uri și CronJob-uri nefuncționale;
  • StatefulSet-uri și DaemonSet-uri.

Doresc să îmi exprim recunoștința Gergely Risko, Daniel Weibel și Charles Christyraj pentru observațiile și completările valoroase.

P.S. de la traducător

Citiți și în blogul nostru:

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster