Nota di traduzione.: Questo articolo fa parte dei materiali pubblicati ad accesso libero del progetto , 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.

TL;DR: ecco uno schema che ti aiuterà a debbugare il deployment in Kubernetes:
Diagramma per la ricerca e la correzione degli errori nel cluster. È disponibile in originale (in inglese) in e .
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.

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

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

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:
- Il selettore (
selector) del Service deve corrispondere almeno a un'etichetta del Pod. -
targetPortdeve corrispondere acontainerPortdel contenitore all'interno del Pod. -
portIl 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:

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

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

4) Attraverso targetPort. Deve corrispondere a containerPort.

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

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-labelsOppure, 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:80Qui:
-
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
portper 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:
-
servicePortnell'Ingress deve corrispondere al parametroportnel Service; -
serviceNamenell'Ingress deve corrispondere al camponamenel Service.
Il seguente schema riassume il collegamento delle porte:
1) Come già sapete, il Service ascolta un certo port:

2) L'Ingress ha un parametro chiamato servicePort:

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

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

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/TCPInfine, collegati al pod:
kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-systemOra 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 , dovresti vedere la pagina creata dall'applicazione.
Riepilogo sulle porte
Rivediamo quali porte ed etichette devono corrispondere:
- Il selettore nella definizione del Service deve corrispondere all'etichetta del pod;
-
targetPortnella definizione del Service deve corrispondere acontainerPortdel contenitore all'interno del pod; -
portNella definizione del Service può essere qualsiasi cosa. Servizi diversi possono utilizzare la stessa porta poiché hanno indirizzi IP diversi; -
servicePortIngress deve corrispondere aportnella definizione del Service; - Il nome del servizio deve corrispondere al campo
serviceNamein 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.
- Per prima cosa, è necessario assicurarsi che i pod funzionino, quindi…
- Verificare se il servizio fornisce traffico ai pod e poi…
- 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:

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

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

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:
-
kubectl logsper estrarre i log dai contenitori nel pod; -
kubectl describe podper visualizzare l'elenco degli eventi associati al pod; -
kubectl get podper ottenere la configurazione YAML del pod conservata in Kubernetes; -
kubectl exec -ti bashper 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:
- Il nome dell'immagine è sbagliato, ad esempio, hai commesso un errore o l'immagine non esiste;
- È stato specificato un tag inesistente per l'immagine;
- 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 di come si può fare.
CrashLoopBackOff
Kubernetes restituisce un errore CrashLoopBackOff, se il container non può avviarsi. Solitamente ciò accade quando:
- C'è un errore nell'applicazione che ne impedisce l'avvio;
- Contenitore ;
- 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 --previousEsso 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):
- Nel cluster non ci sono abbastanza risorse, come potenza di calcolo e memoria, per avviare il pod.
- Nello spazio dei nomi appropriato è stato impostato un oggetto
ResourceQuotaLa creazione del pod porterà alla situazione in cui lo spazio dei nomi supererà il limite della quota. - 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.creationTimestampI 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à:
- non c'è alcun pod con l'etichetta corretta (suggerimento: verifica se il namespace è selezionato correttamente);
- 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:80Qui:
-
<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
EsecuzioneePronto; - 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 PortsInfine, collegati al pod:
kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-systemOra 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 — nota del traduttore.) Dovresti fare riferimento alla guida per la risoluzione dei problemi nella documentazione del rispettivo controller. Poiché è 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 . 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— controllanginx.conf; -
kubectl ingress-nginx backend— esamina il backend (analogamente akubectl 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 , e per i preziosi commenti e suggerimenti.
P.S. dal traduttore
Leggi anche nel nostro blog:
- «»;
- «»;
- «»;
- «».
Fonte: habr.com
