Guida visiva alla diagnosi dei problemi in Kubernetes

Nota del traduttore.: Questo articolo fa parte del materiale pubblicato e reso disponibile dal progetto learnk8s, dedicato alla formazione per lavorare con Kubernetes per aziende e amministratori individuali. In questo documento, Daniele Polencic, il project leader, condivide un'istruzione dettagliata su quali passi intraprendere in caso di problemi generali con le applicazioni eseguite nel cluster K8s.

Guida visiva alla diagnosi dei problemi in Kubernetes

TL;DR: ecco uno schema che ti aiuterà a fare debugging del deployment in Kubernetes:

Guida visiva alla diagnosi dei problemi in Kubernetes

Diagramma per la diagnosi e la risoluzione dei problemi nel cluster. La versione originale (in inglese) è disponibile in PDF e come immagine.

Quando si distribuisce un'applicazione in Kubernetes, è solitamente necessario definire tre componenti:

  • Deployment — una sorta di ricetta per creare copie dell'applicazione, chiamate pod;
  • Service — un bilanciatore di carico interno che distribuisce il traffico tra i pod;
  • Ingress — una descrizione di come il traffico entrerà nel mondo esterno verso il Service.

Ecco un breve riepilogo grafico:

1) In Kubernetes, le applicazioni ricevono traffico dal mondo esterno attraverso due livelli di bilanciatori di carico: interno ed esterno.

Guida visiva alla diagnosi dei problemi in Kubernetes

2) Il bilanciatore di carico interno si chiama Service, quello esterno – Ingress.

Guida visiva alla diagnosi dei problemi in Kubernetes

3) Il Deployment crea i pod e li monitora (non vengono creati manualmente).

Guida visiva alla diagnosi dei problemi in Kubernetes

Immagina di voler distribuire una semplice applicazione simile a Hello World. La configurazione YAML per essa apparirà come segue:

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: /

La definizione è piuttosto lunga e può essere facile confondersi su come i componenti siano collegati tra loro.

Ad esempio:

  • Quando dovremmo usare la porta 80 e quando la 8080?
  • Dobbiamo creare una nuova porta per ciascun servizio per evitare conflitti?
  • I nomi delle etichette contano? Devono essere gli stessi ovunque?

Prima di concentrarci sul debug, ricordiamo come i tre componenti siano collegati tra loro. Cominciamo con il Deployment e il Service.

Collegamento tra Deployment e Service

Sorprendentemente, i Deployment e i Service non sono collegati tra loro. Invece, il Service punta direttamente ai Pod, bypassando il Deployment.

In questo modo, siamo interessati a come sono collegati tra loro i Pod e i Service.

  1. Selettore (selector) del Service deve corrispondere a almeno un'etichetta del Pod.
  2. targetPort deve corrispondere a containerPort del contenitore all'interno del Pod.
  3. porto Il Service può utilizzare qualsiasi porta. Diversi servizi possono utilizzare la stessa porta, poiché hanno indirizzi IP diversi.

Il seguente schema rappresenta tutto quanto sopra in forma grafica:

1) Immaginiamo che il servizio diriga il traffico verso un certo pod:

Guida visiva alla diagnosi dei problemi in Kubernetes

2) Quando si crea un pod, è necessario specificare containerPort per ogni contenitore nei pod:

Guida visiva alla diagnosi dei problemi in Kubernetes

3) Quando si crea un servizio, è necessario specificare porto e targetPort. Ma attraverso quale di essi avviene la connessione al contenitore?

Guida visiva alla diagnosi dei problemi in Kubernetes

4) Attraverso targetPort. Deve corrispondere a containerPort.

Guida visiva alla diagnosi dei problemi in Kubernetes

5) Supponiamo che nel contenitore sia aperta la porta 3000. Quindi il valore targetPort deve essere lo stesso.

Guida visiva alla diagnosi dei problemi in Kubernetes

Nel file YAML, le etichette e ports / targetPort devono corrispondere:

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  # <<<

E riguardo all'etichetta track: canary nella parte superiore della sezione Deployment? Dovrebbe corrispondere?

Questa etichetta è associata al deployment e non è utilizzata dal servizio per il routing del traffico. In altre parole, può essere rimossa o assegnata un altro valore.

E per quanto riguarda il selettore matchLabels?

Deve sempre corrispondere alle etichette del Pod, poiché viene utilizzato dal Deployment per monitorare i pod.

Supponiamo che tu abbia effettuato le modifiche corrette. Come le controlli?

Puoi controllare le etichette dei pod con il seguente comando:

kubectl get pods --show-labels

Oppure, se i pod appartengono a più applicazioni:

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

Dove any-name=my-app è un'etichetta any-name: my-app.

Hai ancora problemi?

Puoi connetterti al pod! Per farlo, utilizza il comando port-forward in kubectl. Ti permette di connetterti al servizio e verificare la connessione.

kubectl port-forward service/ 3000:80

Qui:

  • service/ — nome del servizio; nel nostro caso è my-service;
  • 3000 — porta che deve essere aperta sul computer;
  • 80 — porta specificata nel campo porto funzionale.

Se la connessione è stata stabilita, significa che le impostazioni sono corrette.

Se non è stato possibile stabilire la connessione, c'è un problema con le etichette o le porte non corrispondono.

Collegamento tra Service e Ingress

Il prossimo passo per garantire l'accesso all'applicazione è configurare l'Ingress. L'Ingress deve sapere come trovare il servizio, quindi localizzare i pod e instradare il traffico verso di essi. L'Ingress trova il servizio corretto per nome e porta aperta.

Nella descrizione di Ingress e Service devono coincidere due parametri:

  1. servicePort in Ingress deve corrispondere al parametro porto in Service;
  2. serviceName in Ingress deve corrispondere al campo name in Service.

Il seguente schema riassume la connessione delle porte:

1) Come già sai, il Service ascolta su una certa porto:

Guida visiva alla diagnosi dei problemi in Kubernetes

2) L'Ingress ha un parametro chiamato servicePort:

Guida visiva alla diagnosi dei problemi in Kubernetes

3) Questo parametro (servicePort) deve sempre corrispondere a porto nella definizione di Service:

Guida visiva alla diagnosi dei problemi in Kubernetes

4) Se nel Service è specificata la porta 80, allora è necessario che servicePort sia anch'essa uguale a 80:

Guida visiva alla diagnosi dei problemi in Kubernetes

Nella pratica, è importante prestare attenzione alle seguenti righe:

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: /

Come verificare se l'Ingress funziona?

Si può utilizzare il metodo con kubectl port-forward, ma invece di connettersi al servizio, bisogna collegarsi al controller Ingress.

Prima di tutto, è necessario scoprire il nome del pod con il controller 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

Trova il pod dell'Ingress (potrebbe appartenere a un altro namespace) ed esegui il comando describe, per scoprire i numeri delle porte:

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

Infine, collegati al pod:

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

Ora ogni volta che invii una richiesta alla porta 3000 sul computer, verrà reindirizzata alla porta 80 del pod con il controller Ingress. Vai a http://localhost:3000, dovreste vedere la pagina creata dall'applicazione.

Riepilogo delle porte

Ripassiamo quali porte e etichette devono corrispondere:

  1. Il selettore nella definizione del Service deve corrispondere all'etichetta del pod;
  2. targetPort nella definizione del Service deve corrispondere a containerPort del contenitore all'interno del pod;
  3. porto nella definizione del Service può essere qualsiasi. Servizi diversi possono utilizzare la stessa porta poiché hanno indirizzi IP diversi;
  4. servicePort di Ingress deve corrispondere a porto nella definizione del Service;
  5. Il nome del servizio deve corrispondere al campo serviceName in Ingress.

Purtroppo, non basta sapere come strutturare correttamente la configurazione YAML.

Cosa succede quando qualcosa va storto?

Potrebbe essere che il pod non si avvia o si arresta.

3 passaggi per diagnosticare problemi con le applicazioni in Kubernetes

Prima di iniziare il debug del deployment, è fondamentale avere una buona comprensione del funzionamento di Kubernetes.

Poiché ogni applicazione distribuita in K8s ha tre componenti, il debug deve essere eseguito in un ordine specifico, partendo dal basso.

  1. Prima, è necessario assicurarsi che i pod siano in esecuzione, poi…
  2. Controllare se il servizio sta fornendo traffico ai pod, e poi…
  3. Verifica se l'Ingress è configurato correttamente.

Rappresentazione visiva:

1) Inizia a cercare problemi dal fondo. Prima di tutto, controlla che i pod abbiano stati Pronto e Esecuzione:

Guida visiva alla diagnosi dei problemi in Kubernetes

2) Se i pod sono pronti (Pronto), devi verificare se il servizio distribuisce il traffico tra i pod:

Guida visiva alla diagnosi dei problemi in Kubernetes

3) Infine, analizza la connessione tra il servizio e l'Ingress:

Guida visiva alla diagnosi dei problemi in Kubernetes

1. Diagnostica dei pod

Nella maggior parte dei casi, il problema è legato al pod. Assicurati che i pod siano contrassegnati come Pronto e Esecuzione. Puoi controllarlo con il comando:

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

Nell'output del comando riportato sopra, l'ultimo pod è contrassegnato come Esecuzione e Pronto, ma per gli altri due non è così.

Come capire cosa sia andato storto?

Ci sono quattro comandi utili per diagnosticare i pod:

  1. kubectl logs consente di estrarre i log dai contenitori nel pod;
  2. kubectl describe pod consente di visualizzare l'elenco degli eventi legati al pod;
  3. kubectl get pod consente di ottenere la configurazione YAML del pod, memorizzata in Kubernetes;
  4. kubectl exec -ti bash consente di avviare una shell interattiva in uno dei container del pod

Quale scegliere?

Il fatto è che non esiste un comando universale. È necessario utilizzare una combinazione di essi.

Problemi tipici dei pod

Ci sono due principali tipi di errori nei pod: errori durante l'avvio (startup) ed errori durante l'esecuzione (runtime).

Errori di avvio:

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

Errori di runtime:

  • CrashLoopBackOff
  • RunContainerError
  • KillContainerError
  • VerifyNonRootError
  • RunInitContainerError
  • CreatePodSandboxError
  • ConfigPodSandboxError
  • KillPodSandboxError
  • SetupNetworkError
  • TeardownNetworkError

Alcuni errori si verificano più frequentemente di altri. Ecco alcuni degli errori più comuni e i modi per risolverli.

ImagePullBackOff

Questo errore si verifica quando Kubernetes non riesce a recuperare l'immagine per uno dei container del pod. Ecco tre delle cause più comuni:

  1. Nome dell'immagine errato — ad esempio, hai commesso un errore nel nome, o l'immagine non esiste;
  2. È stato specificato un tag inesistente per l'immagine;
  3. L'immagine si trova in un registro privato e Kubernetes non ha le autorizzazioni per accedervi.

Le prime due ragioni sono facili da risolvere: basta correggere il nome dell'immagine e il tag. Nel caso dell'ultimo, è necessario inserire le credenziali nel registro privato in Secret e aggiungere i collegamenti a esso nei pod. c'è un esempio di come questo può essere fatto.

CrashLoopBackOff

Kubernetes restituisce un errore CrashLoopBackOff, se il contenitore non riesce ad avviarsi. Questo di solito accade quando:

  1. C'è un errore nell'applicazione che impedisce l'avvio;
  2. Container è configurato in modo errato;
  3. Il test di Liveness è fallito troppe volte.

È necessario cercare di accedere ai log dal contenitore per scoprire la causa del suo errore. Se l'accesso ai log è difficile, poiché il contenitore si riavvia troppo rapidamente, si può utilizzare il comando seguente:

kubectl logs  --previous

Esso restituisce i messaggi di errore della precedente incarnazione del contenitore.

RunContainerError

Questo errore si verifica quando il contenitore non riesce ad avviarsi. Si verifica prima dell'avvio dell'applicazione. Di solito la causa è una configurazione errata, come ad esempio:

  • tentare di montare un volume inesistente, come ConfigMap o Secrets;
  • tentativo di montare un volume di tipo solo lettura come scrivibile.

Per analizzare questo tipo di errore è utile il comando kubectl describe pod.

I Pod sono in stato Pending

Dopo la creazione, il pod rimane in stato Pending.

Perché accade questo?

Ecco alcune possibili cause (presumendo che lo scheduler funzioni correttamente):

  1. Nel cluster non ci sono risorse sufficienti, come capacità di calcolo e memoria, per avviare il pod.
  2. Nello spazio dei nomi pertinente è presente un oggetto ResourceQuota e la creazione del pod porterà a superare il limite di quota dello spazio dei nomi.
  3. Il pod è in Pending PersistentVolumeClaim.

In questo caso, si consiglia di utilizzare il comando kubectl describe e controllare la sezione Eventi:

kubectl describe pod

In caso di errori relativi a ResourceQuotas, si consiglia di consultare i log del cluster utilizzando il comando

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

I Pod non sono in stato Ready

Se il pod risulta Esecuzione, ma non è in stato Pronto, significa che il controllo della sua prontezza (readiness probe) sta fallendo.

Quando si verifica ciò, il pod non si connette al servizio e non riceve traffico. Il fallimento del test di readiness è causato da problemi nell'applicazione. In questo caso, per trovare l'errore è necessario analizzare la sezione Eventi nell'output del comando kubectl describe.

2. Diagnosi dei servizi

Se i pod risultano come Esecuzione e Pronto, ma non si riceve ancora risposta dall'applicazione, è necessario verificare le impostazioni del servizio.

I servizi si occupano di instradare il traffico ai pod in base alle loro etichette. Pertanto, la prima cosa da fare è controllare quanti pod stanno funzionando con il servizio. Per fare ciò, è possibile controllare gli endpoint nel servizio:

kubectl describe service  | grep Endpoints

L'endpoint è una coppia di valori del tipo <IP-адрес:порт>, e nell'output deve essere presente almeno una coppia di questo tipo (cioè almeno un pod sta lavorando con il servizio).

Se la sezione Endpoints è vuota, ci sono due possibilità:

  1. non c'è nessun pod con l'etichetta corretta (suggerimento: controlla se il namespace è stato scelto correttamente);
  2. c'è un errore nelle etichette del servizio nel selettore.

Se vedi un elenco di endpoint, ma non riesci ancora ad accedere all'applicazione, il colpevole probabile è un errore nella targetPort descrizione del servizio.

Come verificare il funzionamento del servizio?

Indipendentemente dal tipo di servizio, puoi utilizzare il comando kubectl port-forward per connetterti ad esso:

kubectl port-forward service/ 3000:80

Qui:

  • <service-name> — nome del servizio;
  • 3000 — porta che stai aprendo sul computer;
  • 80 — porta lato servizio.

3. Diagnostica Ingress

Se sei arrivato fino a questo punto, significa che:

  • i pod sono registrati come Esecuzione e Pronto;
  • il servizio distribuisce con successo il traffico ai pod.

Tuttavia, non riesci ancora a «raggiungere» l'applicazione.

Ciò significa che, molto probabilmente, il controller Ingress non è configurato correttamente. Poiché il controller Ingress è un componente esterno nel cluster, ci sono vari metodi di debug a seconda del suo tipo.

Ma prima di ricorrere a strumenti specializzati per la configurazione dell'Ingress, puoi fare qualcosa di molto semplice. L'Ingress utilizza serviceName e servicePort per connettersi al servizio. È necessario verificare se sono configurati correttamente. Puoi farlo utilizzando il comando:

kubectl describe ingress

Se la colonna Backend è vuota, c'è alta probabilità di un errore nella configurazione. Se i backend sono a posto, ma l'accesso all'applicazione è ancora assente, il problema potrebbe essere legato a:

  • impostazioni di accesso dell'Ingress tramite internet pubblico;
  • le impostazioni di accessibilità del cluster da Internet pubblico.

Puoi identificare i problemi con l'infrastruttura collegandoti direttamente al pod dell'Ingress. Prima trova il pod del controller Ingress (potrebbe trovarsi in un altro spazio dei nomi):

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

Usa il comando describe, per impostare la porta:

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

Infine, collegati al pod:

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

Ora tutte le richieste sulla porta 3000 del computer verranno reindirizzate sulla porta 80 del pod.

Funziona adesso?

  • Se sì, il problema è con l'infrastruttura. È necessario capire come avviene il routing del traffico nel cluster.
  • Se no, il problema riguarda il controller Ingress.

Se non riesci a far funzionare il controller Ingress, dovrai eseguire la sua debug.

Esistono molte varianti di controller Ingress. I più popolari sono Nginx, HAProxy, Traefik e altri. (per maggiori dettagli sulle soluzioni disponibili, vedi la nostra recensione — nota di traduzione) È consigliabile seguire la guida alla risoluzione dei problemi nella documentazione del rispettivo controller. Poiché Ingress Nginx è il controller Ingress più popolare, abbiamo incluso nell'articolo alcuni suggerimenti per risolvere i problemi ad esso correlati.

Debugging del controller Ingress Nginx

Il progetto Ingress-nginx ha un plugin ufficiale per kubectl. Il comando kubectl ingress-nginx può essere utilizzato per:

  • analizzare i log, i backend, i certificati e altro;
  • connettersi all'Ingress;
  • esaminare la configurazione attuale.

A questo scopo, le seguenti tre comandi vi saranno utili:

  • kubectl ingress-nginx lint — controlla nginx.conf;
  • kubectl ingress-nginx backend — esplora il backend (analogamente a kubectl describe ingress);
  • kubectl ingress-nginx logs — verifica i log.

Si prega di notare: in alcuni casi potrebbe essere necessario specificare il namespace corretto per il controller Ingress con il flag --namespace.

Riepilogo

La diagnosi in Kubernetes può rivelarsi una sfida se non si conosce da dove iniziare. È sempre consigliato approcciare il problema secondo il principio "dal basso verso l'alto": iniziate dai pod e poi passate ai servizi e all'Ingress. I metodi di debugging descritti nell'articolo possono essere applicati anche ad altri oggetti, come ad esempio:

  • Job e CronJob non funzionanti;
  • StatefulSet e DaemonSet.

Desidero ringraziare Gergely Risko, Daniel Weibel e Charles Christyraj per i loro preziosi commenti e suggerimenti.

P.S. dal traduttore

Leggete anche nel nostro blog:

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS 🔥 Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS | ProHoster