Guida visiva alla diagnostica dei problemi in Kubernetes

Nota di traduzione.: Questo articolo fa parte dei materiali pubblicati ad accesso libero del progetto learnk8s, che insegna a lavorare con Kubernetes per aziende e amministratori individuali. In esso Daniele Polencic, il responsabile del progetto, condivide un'istruzione visiva su quali passi intraprendere in caso di problemi generali con le applicazioni eseguite nel cluster K8s.

Guida visiva alla diagnostica dei problemi in Kubernetes

TL;DR: ecco uno schema che ti aiuterà a debbugare il deployment in Kubernetes:

Guida visiva alla diagnostica dei problemi in Kubernetes

Diagramma per la ricerca e la correzione degli errori nel cluster. È disponibile in originale (in inglese) 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;
  • Servizio — un bilanciatore di carico interno che distribuisce il traffico tra i pod;
  • Ingress — una descrizione di come il traffico arriverà dal mondo esterno al Service.

Ecco un breve riassunto grafico:

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

Guida visiva alla diagnostica dei problemi in Kubernetes

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

Guida visiva alla diagnostica dei problemi in Kubernetes

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

Guida visiva alla diagnostica dei problemi in Kubernetes

Supponiamo che tu voglia 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 dovresti usare la porta 80 e quando la 8080?
  • Dovresti creare una nuova porta per ogni servizio affinché non ci siano conflitti?
  • Ha importanza il nome delle etichette? Devono essere uguali ovunque?

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

Collegamento tra Deployment e Service

Ti sorprenderà sapere che i Deployment e i Service non sono affatto collegati. Invece, il Service punta direttamente ai Pod, bypassando il Deployment.

Dunque, ci interessa sapere come sono collegati tra loro i Pod e i Service. Ricordate tre cose:

  1. Il selettore (selector) del Service deve corrispondere almeno a un'etichetta del Pod.
  2. targetPort deve corrispondere a containerPort del contenitore all'interno del Pod.
  3. port Il Service può essere qualsiasi. 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 indirizzi il traffico a un certo pod:

Guida visiva alla diagnostica dei problemi in Kubernetes

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

Guida visiva alla diagnostica dei problemi in Kubernetes

3) Quando si crea un servizio, è necessario specificare port e targetPort. Ma attraverso quale di essi si connette al contenitore?

Guida visiva alla diagnostica dei problemi in Kubernetes

4) Attraverso targetPort. Deve corrispondere a containerPort.

Guida visiva alla diagnostica dei problemi in Kubernetes

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

Guida visiva alla diagnostica 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 per quanto riguarda l'etichetta track: canary nella parte superiore della sezione Deployment? Deve corrispondere?

Questa etichetta si riferisce al deployment e non viene utilizzata dal servizio per instradare il 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.

Immagina che tu abbia fatto le modifiche corrette. Come puoi verificarle?

Puoi controllare l'etichetta dei pod con il seguente comando:

kubectl get pods --show-labels

Oppure, se i pod appartengono a diverse applicazioni:

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

Dove any-name=my-app — questa è l'etichetta any-name: my-app.

Hai ancora difficoltà?

Puoi connetterti al pod! Puoi farlo utilizzando il comando port-forward in kubectl. Permette di connettersi al servizio e verificare la connessione.

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

Qui:

  • service/<service name> — è il nome del servizio; nel nostro caso è my-service;
  • 3000 — la porta che deve essere aperta sul computer;
  • 80 — la porta specificata nel campo port per il servizio.

Se sei riuscito a stabilire la connessione, le impostazioni sono corrette.

Se non è stato possibile stabilire una connessione, significa che ci sono problemi con le etichette o le porte non corrispondono.

Collegamento tra il Service e l'Ingress

Il passo successivo per garantire l'accesso all'applicazione è la configurazione dell'Ingress. L'Ingress deve sapere come trovare il servizio, quindi individuare i pod e instradare il traffico verso di essi. L'Ingress trova il servizio desiderato per nome e porto aperto.

Nella descrizione dell'Ingress e del Service devono corrispondere due parametri:

  1. servicePort nell'Ingress deve corrispondere al parametro port nel Service;
  2. serviceName nell'Ingress deve corrispondere al campo name nel Service.

Il seguente schema riassume il collegamento delle porte:

1) Come già sapete, il Service ascolta un certo port:

Guida visiva alla diagnostica dei problemi in Kubernetes

2) L'Ingress ha un parametro chiamato servicePort:

Guida visiva alla diagnostica dei problemi in Kubernetes

3) Questo parametro (servicePort) deve sempre corrispondere a port nella definizione del Service:

Guida visiva alla diagnostica dei problemi in Kubernetes

4) Se nel Service è impostata la porta 80, è necessario che servicePort sia anche 80:

Guida visiva alla diagnostica 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?

Puoi utilizzare il metodo con kubectl port-forward, ma invece di collegarti al servizio, dovrai collegarti al controller dell'Ingress.

Prima di tutto, devi scoprire il nome del pod con il controller dell'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 (può 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 dell'Ingress. Navigando su http://localhost:3000, dovresti vedere la pagina creata dall'applicazione.

Riepilogo sulle porte

Rivediamo quali porte ed 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. port Nella definizione del Service può essere qualsiasi cosa. Servizi diversi possono utilizzare la stessa porta poiché hanno indirizzi IP diversi;
  4. servicePort Ingress deve corrispondere a port 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 i problemi delle applicazioni in Kubernetes

Prima di iniziare il debug del deployment, è necessario avere una buona comprensione di come funziona Kubernetes.

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

  1. Per prima cosa, è necessario assicurarsi che i pod funzionino, quindi…
  2. Verificare se il servizio fornisce traffico ai pod e poi…
  3. Controllare se l'Ingress è configurato correttamente.

Rappresentazione visiva:

1) La ricerca dei problemi dovrebbe iniziare dalla base. Prima verifica che i pod abbiano stati Pronto e Esecuzione:

Guida visiva alla diagnostica dei problemi in Kubernetes

2) Se i pod sono pronti (Pronto), è necessario scoprire se il servizio distribuisce traffico tra i pod:

Guida visiva alla diagnostica dei problemi in Kubernetes

3) Infine, è necessario analizzare il legame tra il servizio e l'Ingress:

Guida visiva alla diagnostica dei problemi in Kubernetes

1. Diagnostica dei pod

Nella maggior parte dei casi, il problema è legato al pod. Assicurati che i pod risultino essere Pronto e Esecuzione. Puoi verificare questo usando 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 di cui sopra, l'ultimo pod risulta essere Esecuzione e Pronto, ma per gli altri due non è così.

Come capire che qualcosa non va?

Ci sono quattro comandi utili per diagnosticare i pod:

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

Quale scegliere?

Il fatto è che non esiste un comando universale. Dovresti usare una combinazione di essi.

Problemi tipici dei pod

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

Errori di avvio:

  • ImagePullBackoff
  • ImageInspectError
  • ErrImagePull
  • ErrImageNeverPull
  • RegistryUnavailable
  • NomeImmagineNonValido

Errori diEsecuzione:

  • CrashLoopBackOff
  • ErroreEsecuzioneContainer
  • ErroreUccisioneContainer
  • ErroreVerificaNonRoot
  • ErroreEsecuzioneContainerIniziale
  • ErroreCreazionePodSandbox
  • ErroreConfigurazionePodSandbox
  • ErroreUccisionePodSandbox
  • ErroreImpostazioneRete
  • ErroreSmontaggioRete

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 ottenere l'immagine per uno dei container del pod. Ecco tre delle cause più comuni:

  1. Il nome dell'immagine è sbagliato, ad esempio, hai commesso un errore o l'immagine non esiste;
  2. È stato specificato un tag inesistente per l'immagine;
  3. L'immagine è memorizzata in un registro privato e Kubernetes non ha le autorizzazioni per accedervi.

Le prime due cause sono facili da risolvere: basta correggere il nome dell'immagine e il tag. Nel caso dell'ultima, è necessario fornire le credenziali per il registro privato in un Secret e aggiungere i riferimenti ai pod. Nella documentazione di Kubernetes c'è un esempio di come si può fare.

CrashLoopBackOff

Kubernetes restituisce un errore CrashLoopBackOff, se il container non può avviarsi. Solitamente ciò accade quando:

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

È necessario cercare di accedere ai log del container per determinare la causa del suo fallimento. Se l'accesso ai log è difficile perché il container si riavvia troppo rapidamente, si può utilizzare il seguente comando:

kubectl logs  --previous

Esso restituirà i messaggi di errore dalla reincarnazione precedente del container.

ErroreEsecuzioneContainer

Questo errore si verifica quando il container non è in grado di avviarsi. Si verifica prima dell'avvio dell'applicazione. Solitamente la causa è un'impostazione errata, ad esempio:

  • tentare di montare un volume inesistente, come ConfigMap o Secrets;
  • tentare di montare un volume di tipo read-only come read-write.

Per analizzare simili errori è utile il comando kubectl describe pod.

I pod sono in stato Pending

Dopo la creazione, il pod rimane nello stato Pending.

Perché accade questo?

Ecco alcune cause possibili (presumo che lo scheduler stia funzionando correttamente):

  1. Nel cluster non ci sono abbastanza risorse, come potenza di calcolo e memoria, per avviare il pod.
  2. Nello spazio dei nomi appropriato è stato impostato un oggetto ResourceQuota La creazione del pod porterà alla situazione in cui lo spazio dei nomi supererà il limite della quota.
  3. Il Pod è in attesa di completamento 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, è consigliabile esaminare i log del cluster utilizzando il comando

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

I Pod non sono nello stato Ready

Se il pod è contrassegnato come Esecuzione, ma non è in stato Pronto, significa che il controllo della sua prontezza (readiness probe) fallisce.

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

2. Diagnosi dei servizi

Se i pod sono contrassegnati come Esecuzione e Pronto, ma non c'è comunque risposta dall'applicazione, è necessario controllare le impostazioni del servizio.

I servizi gestiscono la reindirizzamento del traffico ai pod in base alle loro etichette. Pertanto, la prima cosa da fare è verificare quanti pod stanno lavorando con il servizio. Per farlo, puoi controllare gli endpoint nel servizio:

kubectl describe service  | grep Endpoints

Un endpoint è una coppia di valori del tipo <IP-адрес:порт>, e nell'output deve esserci almeno una coppia di questo tipo (ovvero deve esserci almeno un pod che lavora con il servizio).

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

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

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

Come controllare se il servizio è operativo?

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> è il nome del servizio;
  • 3000 è la porta che stai aprendo sul computer;
  • 80 è la porta dal lato del servizio.

3. Diagnosi Ingress

Se sei arrivato a questo punto:

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

Tuttavia, non riesci comunque a "raggiungere" l'applicazione.

Questo 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 configurare l'Ingress, si può fare qualcosa di molto semplice. L'Ingress utilizza serviceName e servicePort per connettersi al servizio. È necessario verificare se sono configurati correttamente. Questo può essere fatto usando il comando:

kubectl describe ingress

Se la colonna Backend è vuota, c'è una buona probabilità di errore nella configurazione. Se i backend sono al loro posto, ma non c'è ancora accesso all'applicazione, il problema potrebbe essere correlato a:

  • impostazioni di accessibilità dell'Ingress da Internet pubblico;
  • impostazioni di accessibilità del cluster da Internet pubblico.

È possibile identificare problemi con l'infrastruttura connettendosi direttamente al pod dell'Ingress. Per farlo, prima trova il pod del controller Ingress (potrebbe trovarsi in un altro namespace):

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 stabilire 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 saranno reindirizzate alla porta 80 del pod.

Funziona ora?

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

Se non riesci a far funzionare il controller Ingress, dovrai effettuare il debug.

Ci sono molte varianti di controller Ingress. I più popolari sono Nginx, HAProxy, Traefik e altri. (per maggiori dettagli sulle soluzioni esistenti, vedi il nostro overview — nota del traduttore.) Dovresti fare riferimento alla guida per la 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 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, ecc.;
  • connettersi all'Ingress;
  • esaminare la configurazione attuale.

Ti aiuteranno i seguenti tre comandi:

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

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

Riepilogo

La diagnostica in Kubernetes può rivelarsi un compito difficile se non si sa da dove iniziare. È sempre consigliabile affrontare il problema secondo il principio "dal basso verso l'alto": inizia dai pod e poi passa al servizio e all'Ingress. I metodi di debug descritti nell'articolo possono essere applicati anche ad altri oggetti, come:

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

Esprimo la mia gratitudine Gergely Risko, Daniel Weibel e Charles Christyraj per i preziosi commenti e suggerimenti.

P.S. dal traduttore

Leggi 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