Nota del traduttore.: Questo articolo fa parte del materiale pubblicato e reso disponibile dal progetto , 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.

TL;DR: ecco uno schema che ti aiuterà a fare debugging del deployment in Kubernetes:
Diagramma per la diagnosi e la risoluzione dei problemi nel cluster. La versione originale (in inglese) è disponibile 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;
- 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.

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).

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

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

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

4) Attraverso targetPort. Deve corrispondere a containerPort.

5) Supponiamo che nel contenitore sia aperta la porta 3000. Quindi 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 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-labelsOppure, 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:80Qui:
-
service/— nome del servizio; nel nostro caso èmy-service; - 3000 — porta che deve essere aperta sul computer;
- 80 — porta specificata nel campo
portofunzionale.
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:
-
servicePortin Ingress deve corrispondere al parametroportoin Service; -
serviceNamein Ingress deve corrispondere al camponamein Service.
Il seguente schema riassume la connessione delle porte:
1) Come già sai, il Service ascolta su una certa porto:

2) L'Ingress ha un parametro chiamato servicePort:

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

4) Se nel Service è specificata la porta 80, allora è necessario che servicePort sia anch'essa uguale a 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?
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/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 Ingress. Vai a , dovreste vedere la pagina creata dall'applicazione.
Riepilogo delle porte
Ripassiamo quali porte e 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; -
portonella definizione del Service può essere qualsiasi. Servizi diversi possono utilizzare la stessa porta poiché hanno indirizzi IP diversi; -
servicePortdi Ingress deve corrispondere aportonella 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 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.
- Prima, è necessario assicurarsi che i pod siano in esecuzione, poi…
- Controllare se il servizio sta fornendo traffico ai pod, e poi…
- 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:

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

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

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:
-
kubectl logsconsente di estrarre i log dai contenitori nel pod; -
kubectl describe podconsente di visualizzare l'elenco degli eventi legati al pod; -
kubectl get podconsente di ottenere la configurazione YAML del pod, memorizzata in Kubernetes; -
kubectl exec -ti bashconsente 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:
- Nome dell'immagine errato — ad esempio, hai commesso un errore nel nome, o l'immagine non esiste;
- È stato specificato un tag inesistente per l'immagine;
- 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. di come questo può essere fatto.
CrashLoopBackOff
Kubernetes restituisce un errore CrashLoopBackOff, se il contenitore non riesce ad avviarsi. Questo di solito accade quando:
- C'è un errore nell'applicazione che impedisce l'avvio;
- Container ;
- 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 --previousEsso 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):
- Nel cluster non ci sono risorse sufficienti, come capacità di calcolo e memoria, per avviare il pod.
- Nello spazio dei nomi pertinente è presente un oggetto
ResourceQuotae la creazione del pod porterà a superare il limite di quota dello spazio dei nomi. - 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.creationTimestampI 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à:
- non c'è nessun pod con l'etichetta corretta (suggerimento: controlla se il namespace è stato scelto correttamente);
- 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:80Qui:
-
<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
EsecuzioneePronto; - 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 PortsInfine, collegati al pod:
kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-systemOra 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 — nota di traduzione) È consigliabile seguire la guida alla risoluzione dei problemi nella documentazione del rispettivo controller. Poiché è 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 . 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— controllanginx.conf; -
kubectl ingress-nginx backend— esplora il backend (analogamente akubectl 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 , e per i loro preziosi commenti e suggerimenti.
P.S. dal traduttore
Leggete anche nel nostro blog:
- «»;
- «»;
- «»;
- «».
Fonte: habr.com
