Remarque perev. : Cet article fait partie du matériel disponible gratuitement du projet learnk8s , qui vous apprend à travailler avec les sociétés Kubernetes et les administrateurs individuels. Dans ce document, Daniele Polencic, le chef de projet, partage une instruction claire sur les mesures à prendre en cas de problèmes généraux pour les applications s'exécutant dans le cluster K8s.
TL; DR: voici un diagramme qui vous aidera à déboguer le déploiement dans Kubernetes:
Organigramme pour rechercher et corriger les erreurs dans un cluster. Dans l'original (en anglais), il est disponible en PDF et en image .Lors du déploiement d'une application sur Kubernetes, vous devez généralement définir trois composants:
- Le déploiement est une recette pour créer des copies d'une application appelée pods;
- Service - un équilibreur de charge interne qui répartit le trafic entre les pods;
- Entrée - une description de la façon dont le trafic sera acheminé du monde extérieur vers le service.
Voici un bref résumé graphique:
1) Dans Kubernetes, les applications reçoivent le trafic du monde extérieur via deux couches d'équilibreurs de charge: interne et externe.

2) L'équilibreur interne est appelé Service, externe - Ingress.

3) Le déploiement crée des pods et les surveille (ils ne sont pas créés manuellement).

Supposons que vous souhaitiez déployer une application simple à la
Hello World . La configuration YAML pour cela ressemblera à ceci:
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 définition est assez longue et il est facile de se tromper sur la façon dont les composants sont liés les uns aux autres.
Par exemple:
- Quand faut-il utiliser le port 80 et quand - 8080?
- Dois-je créer un nouveau port pour chaque service afin qu'ils n'entrent pas en conflit?
- Les noms d'étiquette sont-ils importants? Devraient-ils être les mêmes partout?
Avant de nous concentrer sur le débogage, rappelons comment les trois composants sont liés les uns aux autres. Commençons par le déploiement et le service.
Déploiement de connexion'a et Service'a
Vous serez surpris, mais les déploiements et les services ne sont en aucun cas connectés. Au lieu de cela, le service pointe directement vers des pods contournant le déploiement.
Ainsi, nous sommes intéressés par la façon dont les pods et les services sont liés les uns aux autres. Trois choses à retenir:
- Un
selector
service doit correspondre à au moins une étiquette de pod. targetPort
doit correspondre au containerPort
conteneur à l'intérieur du pod.port
service port
peut être n'importe quoi. Différents services peuvent utiliser le même port car ils ont des adresses IP différentes.
Le diagramme suivant représente tout ce qui précède sous forme graphique:
1) Imaginez que le service dirige le trafic vers un certain pod:

2) Lors de la création d'un pod, vous devez spécifier
containerPort
pour chaque conteneur dans les pods:

3) Lors de la création du service, vous devez spécifier le
port
et
targetPort
.
Mais lequel se connecte au conteneur?
4) Via
targetPort
. Il doit correspondre à
containerPort
.

5) Supposons que le port 3000 soit ouvert dans le conteneur, puis la valeur
targetPort
devrait être la même.

Dans le fichier YAML, les étiquettes et les
ports
/
targetPort
doivent correspondre:
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 # <<<
Qu'en est-il de la track: canary
en haut de la section Déploiement? Doit-elle correspondre?Cette étiquette fait référence au déploiement et n'est pas utilisée par le service pour acheminer le trafic. En d'autres termes, il peut être supprimé ou attribué une valeur différente.
Qu'en est-il du sélecteur matchLabels
?Il doit toujours correspondre aux étiquettes de Pod , car il est utilisé par Deployment pour suivre les pods.
Supposons que vous ayez effectué les modifications correctes. Comment les vérifier?Vous pouvez vérifier l'étiquette du pod à l'aide de la commande suivante:
kubectl get pods --show-labels
Ou, si les pods appartiennent à plusieurs applications:
kubectl get pods --selector any-name=my-app --show-labels
Où
any-name=my-app
est l'étiquette
any-name: my-app
.
Y a-t-il des difficultés?Vous pouvez vous connecter au pod! Pour ce faire, utilisez la commande
port-forward
dans kubectl. Il vous permet de vous connecter au service et de vérifier la connexion.
kubectl port-forward service/<service name> 3000:80
Ici:
service/<service name>
- nom du service; dans notre cas, c'est my-service
;- 3000 - le port que vous souhaitez ouvrir sur l'ordinateur;
- 80 - port spécifié dans le champ
port
du service.
Si vous pouvez établir une connexion, les paramètres sont corrects.
Si la connexion n'a pas pu être établie, il y a un problème avec les étiquettes ou les ports ne correspondent pas.
Connexion du service et de l'entrée
L'étape suivante pour fournir l'accès à l'application est liée à la configuration d'Ingress. Ingress doit savoir comment trouver le service, puis trouver les pods et diriger le trafic vers eux. Ingress trouve le service souhaité par son nom et son port ouvert.
Dans la description d'Ingress and Service, deux paramètres doivent correspondre:
servicePort
dans Ingress doit correspondre au paramètre de port
dans Service;serviceName
dans Ingress doit correspondre au champ de name
dans Service.
Le schéma suivant résume la connexion des ports:
1) Comme vous le savez déjà, le service écoute sur un certain
port
:

2) Ingress a un paramètre appelé
servicePort
:

3) Ce paramètre (
servicePort
) doit toujours correspondre au
port
dans la définition de service:

4) Si le port 80 est spécifié dans Service, alors
servicePort
doit également être 80:

En pratique, vous devez faire attention aux lignes suivantes:
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: /
Comment vérifier si Ingress fonctionne?Vous pouvez utiliser la méthode avec
kubectl port-forward
, mais au lieu du service, vous devez vous connecter au contrôleur Ingress.
Vous devez d'abord trouver le nom du pod avec le contrôleur 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
Localisez le module Ingress (il peut faire référence à un autre espace de noms) et exécutez la commande
describe
pour connaître les numéros de port:
kubectl describe pod nginx-ingress-controller-6fc5bcc \ --namespace kube-system \ | grep Ports Ports: 80/TCP, 443/TCP, 18080/TCP
Enfin, connectez-vous au pod:
kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system
Désormais, chaque fois que vous envoyez une demande au port 3000 sur l'ordinateur, elle sera redirigée vers le port 80 du pod avec le contrôleur Ingress. En allant sur
http: // localhost: 3000 , vous devriez voir la page créée par l'application.
Résumé du port
Rappelons-nous à nouveau quels ports et étiquettes doivent correspondre:
- Le sélecteur dans la définition de service doit correspondre à l'étiquette du pod;
targetPort
dans la définition de service doit correspondre au containerPort
conteneur à l'intérieur du pod;port
dans la définition de Service peut être n'importe quoi. Différents services peuvent utiliser le même port car ils ont des adresses IP différentes;servicePort
Ingress doit correspondre au port
dans la définition de service;- Le nom du service doit correspondre au champ
serviceName
dans Ingress.
Hélas, il ne suffit pas de savoir structurer correctement votre configuration YAML.
Que se passe-t-il en cas de problème?Peut-être que le pod ne démarre pas ou qu'il plante.
3 étapes pour résoudre les échecs d'applications dans Kubernetes
Avant de déboguer un déploiement, vous devez avoir une bonne compréhension du fonctionnement de Kubernetes.
Puisqu'il y a trois composants dans chaque application téléchargée sur K8, ils doivent être débogués dans un certain ordre, en commençant par le bas.
- Vous devez d'abord vous assurer que les modules fonctionnent, puis ...
- Vérifiez si le service fournit du trafic aux pods, puis ...
- Vérifiez si Ingress est correctement configuré.
Présentation visuelle:
1) Commencez la recherche des problèmes par le bas. Vérifiez d'abord que les modules ont les états
Ready
et
Running
:

2) Si les pods sont
Ready
, vous devez savoir si le service répartit le trafic entre les pods:

3) Enfin, vous devez analyser la connexion entre le service et Ingress:

1. Diagnostic des pods
Dans la plupart des cas, le problème vient du pod. Assurez-vous que les modules sont
Ready
et en
Running
. Vous pouvez le vérifier en utilisant la commande:
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
Dans la sortie de la commande ci-dessus, le dernier module est répertorié comme en
Running
d'
Running
et
Ready
, mais pas pour les deux autres.
Comment comprendre ce qui a mal tourné?Il existe quatre commandes utiles pour diagnostiquer les modules:
kubectl logs < pod'>
vous permet d'extraire les journaux des conteneurs du pod;kubectl describe pod < pod'>
vous permet d'afficher une liste des événements associés au pod;kubectl get pod < pod'>
vous permet d'obtenir la configuration YAML du kubectl get pod < pod'>
stockée dans Kubernetes;kubectl exec -ti < pod'> bash
vous permet d'exécuter un shell de commande interactif dans l'un des conteneurs de pod
Lequel choisir?Le fait est qu'il n'y a pas d'équipe universelle. Une combinaison de ces éléments doit être utilisée.
Problèmes courants de pod
Il existe deux principaux types d'erreurs de module: les erreurs de démarrage et les erreurs d'exécution.
Erreurs de démarrage:
ImagePullBackoff
ImageInspectError
ErrImagePull
ErrImageNeverPull
RegistryUnavailable
InvalidImageName
Erreurs d'exécution:
CrashLoopBackOff
RunContainerError
KillContainerError
VerifyNonRootError
RunInitContainerError
CreatePodSandboxError
ConfigPodSandboxError
KillPodSandboxError
SetupNetworkError
TeardownNetworkError
Certaines erreurs sont plus courantes que d'autres. Voici quelques erreurs courantes et comment les corriger.
ImagePullBackOff
Cette erreur apparaît lorsque Kubernetes ne parvient pas à obtenir une image pour l'un des conteneurs de pod. Voici les trois raisons les plus courantes à cela:
- Le nom de l'image n'est pas spécifié correctement - par exemple, vous y avez fait une erreur ou l'image n'existe pas;
- Une balise inexistante pour l'image est spécifiée;
- L'image est stockée dans un registre privé et Kubernetes n'est pas autorisé à y accéder.
Les deux premières raisons sont faciles à éliminer - il suffit de corriger le nom et la balise de l'image. Dans ce dernier cas, vous devez saisir les informations d'identification du registre privé dans Secret et ajouter des liens vers celui-ci dans les pods. La documentation de Kubernetes
a un exemple de comment cela peut être fait.
CrashLoopBackOff
Kubenetes générera une erreur CrashLoopBackOff si le conteneur ne peut pas démarrer. Cela se produit généralement lorsque:
- Il y a une erreur dans l'application qui l'empêche de démarrer;
- Le conteneur n'est pas configuré correctement ;
- Le test de vivacité a échoué trop de fois.
Vous devez essayer d'accéder aux journaux du conteneur pour découvrir la raison de son échec. Si l'accès aux journaux est difficile, car le conteneur redémarre trop rapidement, vous pouvez utiliser la commande suivante:
kubectl logs <pod-name> --previous
Il affiche les messages d'erreur d'une réincarnation de conteneur précédente.
RunContainerError
Cette erreur se produit lorsque le conteneur ne démarre pas. Il correspond à l'instant précédant le lancement de l'application. Habituellement, sa cause est une configuration incorrecte, par exemple:
- Tenter de monter un volume inexistant, tel que ConfigMap ou Secrets;
- essayez de monter un volume en lecture seule en lecture-écriture.
La commande
kubectl describe pod <pod-name>
est bien adaptée pour analyser de telles erreurs.
Gousses en attente
Après la création, le pod reste dans l'état
Pending
.
Pourquoi cela se produit-il?Voici les raisons possibles (je suppose que le planificateur fonctionne bien):
- Le cluster ne dispose pas de suffisamment de ressources, telles que la puissance de traitement et la mémoire, pour exécuter le module.
- L'objet
ResourceQuota
est installé dans l'espace de noms correspondant et la création d'un pod entraînera l'espace de noms au-delà du quota. - Le pod est lié à Pending
PersistentVolumeClaim
.
Dans ce cas, il est recommandé d'utiliser la commande
kubectl describe
et de vérifier la section
Events
:
kubectl describe pod <pod name>
En cas d'erreurs liées à
ResourceQuotas
, il est recommandé d'afficher les journaux de cluster à l'aide de la commande
kubectl get events --sort-by=.metadata.creationTimestamp
Pods non prêts
Si le module est répertorié comme
Running
d'
Running
, mais n'est pas à l'état
Ready
, la
sonde de préparation échoue.
Lorsque cela se produit, le pod ne se connecte pas au service et le trafic n'y circule pas. Le test de préparation a échoué en raison de problèmes d'application. Dans ce cas, pour trouver l'erreur, vous devez analyser la section
Events
dans la sortie de la commande
kubectl describe
.
2. Diagnostic des services
Si les modules sont répertoriés comme
Running
d'
Running
et
Ready
, mais qu'il n'y a toujours pas de réponse de l'application, vous devez vérifier les paramètres de service.
Les services sont impliqués dans l'acheminement du trafic vers les pods en fonction de leurs étiquettes. Par conséquent, la première chose à faire est de vérifier combien de pods fonctionnent avec le service. Pour ce faire, vous pouvez vérifier les points de terminaison du service:
kubectl describe service <service-name> | grep Endpoints
Le point de terminaison est une paire de valeurs de la forme
<IP-:>
, et au moins une telle paire doit être présente dans la sortie (c'est-à-dire qu'au moins un pod fonctionne avec le service).
Si la section
Endpoins
vide, deux options sont possibles:
- il n'y a pas de pods avec la bonne étiquette (astuce: vérifiez si l'espace de noms est sélectionné correctement);
- Il y a une erreur dans les étiquettes de service dans le sélecteur.
Si vous voyez une liste de points de terminaison, mais ne pouvez toujours pas accéder à l'application, le coupable probable est l'erreur dans
targetPort
dans la description du service.
Comment vérifier le bon fonctionnement du service?Quel que soit le type de service, vous pouvez utiliser la commande
kubectl port-forward
pour vous y connecter:
kubectl port-forward service/<service-name> 3000:80
Ici:
<service-name>
- le nom du service;- 3000 - le port que vous ouvrez sur l'ordinateur;
- 80 - port côté service.
3. Diagnostics d'entrée
Si vous lisez jusqu'à cet endroit, alors:
- les pods sont répertoriés comme en
Running
d' Running
et Ready
; - le service distribue avec succès le trafic entre les pods.
Cependant, vous ne pouvez toujours pas «tendre la main» à l'application.
Cela signifie que, très probablement, le contrôleur Ingress est mal configuré. Le contrôleur Ingress étant un composant tiers du cluster, il existe différentes méthodes de débogage en fonction de son type.
Mais avant de recourir à des outils spéciaux pour configurer Ingress, vous pouvez faire quelque chose de très simple. Ingress utilise
serviceName
et
servicePort
pour se connecter au service. Vous devez vérifier qu'ils sont correctement configurés. Vous pouvez le faire en utilisant la commande:
kubectl describe ingress <ingress-name>
Si la colonne
Backend
est vide, le risque d'erreur de configuration est élevé. Si les backends sont en place, mais qu'il n'y a toujours pas accès à l'application, le problème peut être lié à:
- Entrer les paramètres d'accessibilité à partir d'Internet public;
- paramètres d'accessibilité du cluster à partir d'Internet public.
Vous pouvez identifier les problèmes d'infrastructure en vous connectant directement au module Ingress. Pour ce faire, recherchez d'abord le pod du contrôleur Ingress (il peut se trouver dans un espace de noms différent):
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
Utilisez la commande
describe
pour définir le port:
kubectl describe pod nginx-ingress-controller-6fc5bcc --namespace kube-system \ | grep Ports
Enfin, connectez-vous au pod:
kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system
Désormais, toutes les demandes de port 3000 sur l'ordinateur seront redirigées vers le port 80 pod.
Ça marche maintenant?- Si c'est le cas, le problème est lié à l'infrastructure. Il est nécessaire de savoir exactement comment le trafic est acheminé vers le cluster.
- Sinon, le problème vient du contrôleur Ingress.
Si vous ne pouvez pas faire fonctionner le contrôleur Ingress, vous devrez le déboguer.
Il existe de nombreuses variétés de contrôleurs Ingress. Les plus populaires sont Nginx, HAProxy, Traefik, etc.
(pour plus d'informations sur les solutions existantes, consultez notre revue - environ la traduction). Vous devez utiliser le guide de dépannage dans la documentation du contrôleur correspondant. Étant donné
qu'Ingress Nginx est le contrôleur Ingress le plus populaire, nous avons inclus quelques conseils sur la résolution des problèmes connexes dans cet article.
Débogage d'un contrôleur Ingress Nginx
Le projet Ingress-nginx a un
plugin officiel
pour kubectl . La commande
kubectl ingress-nginx
peut être utilisée pour:
- analyse des journaux, backends, certificats, etc.;
- connexion à Ingress;
- étudier la configuration actuelle.
Les trois équipes suivantes vous y aideront:
kubectl ingress-nginx lint
- vérifie nginx.conf
;kubectl ingress-nginx backend
- examine le backend (similaire à kubectl describe ingress <ingress-name>
);kubectl ingress-nginx logs
- vérifie les journaux.
Veuillez noter que dans certains cas, il peut être nécessaire de spécifier l'espace de noms correct pour le contrôleur Ingress à l'aide de l'
--namespace <name>
.
Résumé
Le diagnostic de Kubernetes peut être une tâche ardue si vous ne savez pas par où commencer. Le problème doit toujours être abordé selon le principe ascendant: commencer par les pods, puis aller au service et à Ingress. Les méthodes de débogage décrites dans l'article peuvent être appliquées à d'autres objets, tels que:
- Jobs inactifs et CronJobs;
- StatefulSets et DaemonSets.
Merci à
Gergely Risko ,
Daniel Weibel et
Charles Christyraj pour leurs précieux commentaires et ajouts.
PS du traducteur
Lisez aussi dans notre blog: